Build an AI oracle into a smart contract
An AI oracle lets a smart contract act on the decision of an off-chain AI model — approve a mint, release escrow, flag a transaction, set a price. The model runs off-chain; its decision is signed, posted on-chain, and your contract reads it and acts. Here is the pattern, end to end, using only Armature’s standard EVM and post-quantum signing primitives.
What you’ll need
- An EVM toolchain. Solidity with Hardhat or Foundry for the consumer contract, and viem or ethers for the off-chain service. Armature is EVM-compatible, so the oracle pattern is standard.
- An off-chain AI model. Any model that produces a decision — your own classifier, a hosted LLM, a risk or pricing engine. It runs off-chain; only its decision goes on-chain.
- kxco-post-quantum (recommended). To sign each decision with ML-DSA-65 (NIST FIPS-204) so any party can verify which model identity produced it.
- A funded oracle account. The oracle posts decisions as transactions, so it needs a funded account on the permissioned chain — request one from KXCO.
Step-by-step
- 1
Decide what the oracle decides
Pin down the single on-chain action the AI gates — release escrow, approve a mint, flag a transaction, set a price, admit a participant. Keep the on-chain surface to one small, verified decision; all model logic stays off-chain where it is cheap and private.
- 2
Write the consumer contract
The contract stores the latest decision per subject, restricts writes to the authorized oracle account, and only acts on a decision that is present, fresh and approving. This is the core of building the AI oracle into the contract.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; contract AIOracleConsumer { address public oracle; // authorized AI-oracle account uint256 public maxAge = 1 hours; // reject stale decisions struct Decision { bool approved; uint64 score; uint64 timestamp; } mapping(bytes32 => Decision) public decisions; event DecisionSubmitted(bytes32 indexed subject, bool approved, uint64 score); event ActionExecuted(bytes32 indexed subject); modifier onlyOracle() { require(msg.sender == oracle, "not oracle"); _; } constructor(address _oracle) { oracle = _oracle; } // Called by the off-chain AI oracle after it runs its model. function submitDecision(bytes32 subject, bool approved, uint64 score) external onlyOracle { decisions[subject] = Decision(approved, score, uint64(block.timestamp)); emit DecisionSubmitted(subject, approved, score); } // The contract acts only on a fresh, approving decision. function executeAction(bytes32 subject) external { Decision memory d = decisions[subject]; require(d.timestamp != 0, "no decision"); require(block.timestamp - d.timestamp <= maxAge, "stale"); require(d.approved, "not approved"); // --- perform the gated on-chain action here --- emit ActionExecuted(subject); } } - 3
Run the model off-chain
Your service runs inference and produces a structured decision. The model can be anything — the chain never sees it, only the result.
// the AI runs wherever you host it const input = await getSubject(id) const decision = await runModel(input) // e.g. { approved: true, score: 87 } - 4
Sign the decision (post-quantum, recommended)
Sign the decision with the oracle’s ML-DSA-65 key so anyone can prove which model identity produced it. For on-chain-verifiable provenance, anchor the signed decision’s hash through the KXCO relay, which verifies the post-quantum signature off-chain and records the result on-chain.
import { sign } from 'kxco-post-quantum' const payload = JSON.stringify({ id, ...decision }) const signature = await sign(oraclePqPrivateKey, new TextEncoder().encode(payload)) // publish the oracle's public key so consumers can verify(payload, signature) - 5
Post the decision on-chain
The oracle account submits the decision with a standard contract write. Finality is ~2s, so the consumer contract can act almost immediately.
import { createWalletClient, http, defineChain, keccak256, toHex } from 'viem' import { privateKeyToAccount } from 'viem/accounts' const armature = defineChain({ id: 1111111, name: 'KXCO Armature', nativeCurrency: { name: 'ARMR', symbol: 'ARMR', decimals: 18 }, rpcUrls: { default: { http: ['https://chain.kxco.ai/rpc'] } } }) const account = privateKeyToAccount(process.env.ORACLE_KEY) const wallet = createWalletClient({ account, chain: armature, transport: http() }) await wallet.writeContract({ address: CONSUMER_ADDRESS, abi: CONSUMER_ABI, functionName: 'submitDecision', args: [keccak256(toHex(id)), decision.approved, BigInt(decision.score)], }) - 6
Let the contract act — and harden it
Anyone (or another contract) can now call executeAction; it proceeds only on a fresh, approving, oracle-written decision. Before production: keep writes restricted to the oracle, enforce the staleness window, and for high-value actions require an M-of-N quorum of independent oracle accounts so no single model or operator can force an outcome.
// a counterparty triggers the gated action once the AI has approved await wallet.writeContract({ address: CONSUMER_ADDRESS, abi: CONSUMER_ABI, functionName: 'executeAction', args: [keccak256(toHex(id))], })
Why this works well on Armature
A standard oracle pattern, plus post-quantum-verifiable provenance and fast finality on a permissioned chain built for regulated use.
Frequently asked
Go deeper
Build your AI oracle
Read the contract and SDK references, or contact KXCO to be provisioned with a funded oracle account on the permissioned chain.