Build guide

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. 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. 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. 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. 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. 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. 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.

Standard EVM pattern
A push-oracle in plain Solidity — no special chain features required. Hardhat, Foundry, viem and ethers all work unchanged.
Off-chain intelligence, on-chain action
The model runs where it’s cheap and private; the contract acts deterministically on a single verified decision.
Post-quantum provenance
Sign each decision with ML-DSA-65 so consumers can prove which model identity produced it — verified off-chain, anchored on-chain.
Fast finality
~2s QBFT finality means the contract can act almost immediately after the oracle posts.
Access-controlled
Only the authorized oracle account can write decisions; staleness and approval are enforced on-chain.
Quorum-ready
Compose multiple oracle accounts into an M-of-N quorum so no single AI or operator can unilaterally drive an action.

Frequently asked

Does the AI model run on-chain?
No. The model runs off-chain — your own infrastructure or a hosted API. Only its decision (a small, verified value) is posted on-chain. That keeps gas low and keeps the model and its inputs private.
How does the contract know the decision is genuine?
Writes are restricted to the authorized oracle account by access control. For cryptographic provenance, sign each decision with ML-DSA-65 and verify it against the oracle’s published public key — optionally anchored through the KXCO relay.
Can several AI models feed one contract?
Yes. Register multiple oracle accounts and require an M-of-N quorum before executeAction proceeds, so no single model or operator can force an outcome.
What stops a stale decision being used?
Each decision carries a timestamp, and the contract rejects any decision older than its maxAge window. Set the window to match how quickly the underlying data changes.
Is the post-quantum signature checked on-chain?
Today, ML-DSA-65 verification happens off-chain via the KXCO relay, which anchors the result on-chain. See the Quantum section for the chain’s full cryptographic posture.

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.