sigil
All patterns
startercookbook

AI Price Oracle

On-chain price feed without an oracle network.

Pull a quote from any HTTPS API, extract the price field with JQ, and surface it on-chain. No Chainlink, no keeper. Pair with Scheduler for periodic updates.

Architecture


  ┌─────────┐    HTTP   ┌─────────────┐
  │ Oracle  │ ────────▶ │ JQ extract  │
  │.tick()  │  0x0801    │  0x0803     │
  └─────────┘           └──────┬──────┘
       ▲                        │ uint256
       │ schedule()              ▼
  ┌─────────┐           ┌─────────────┐
  │Scheduler│           │ on-chain    │
  │ 0x56e7  │           │ price feed  │
  └─────────┘           └─────────────┘

Code

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

import {PrecompileConsumer} from "./utils/PrecompileConsumer.sol";

contract PriceOracle is PrecompileConsumer {
    address constant HTTP = address(0x801);
    address constant JQ   = address(0x803);

    uint256 public lastPrice;
    uint256 public lastUpdated;

    /// @notice Tick — invoked by Scheduler.
    function tick(address executor) external {
        // 1. Fetch JSON
        string[] memory keys = new string[](1);
        keys[0] = "Accept";
        string[] memory values = new string[](1);
        values[0] = "application/json";

        bytes memory http = abi.encode(
            executor, new bytes[](0), uint256(100), new bytes[](0), bytes(""),
            "https://api.coingecko.com/api/v3/simple/price?ids=ethereum&vs_currencies=usd",
            uint8(1), keys, values, bytes(""), uint256(0), uint8(0), false
        );
        bytes memory output = _executePrecompile(HTTP, http);
        (uint16 status, , , bytes memory body, ) =
            abi.decode(output, (uint16, string[], string[], bytes, string));
        require(status == 200, "fetch failed");

        // 2. Extract price with JQ — sync, same tx
        (bool ok, bytes memory jq) = JQ.staticcall(
            abi.encode(".ethereum.usd", string(body), uint8(1))
        );
        require(ok && jq.length > 0, "jq failed");

        lastPrice    = abi.decode(jq, (uint256));
        lastUpdated  = block.number;
    }
}