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;
}
}