Core concepts

AI Precompiles

Citrate ships with seven AI-specific precompiled contracts at reserved addresses 0x0100 through 0x0106. These precompiles are native to the Lattice Virtual Machine and execute AI operations at gas costs far below what equivalent Solidity implementations would require.

Precompiles are contracts whose logic is implemented directly in the node software rather than in EVM bytecode. Ethereum uses precompiles for cryptographic primitives (ecrecover, SHA-256, etc.); Citrate extends this pattern to AI-native operations. We chose precompiles over library contracts because they give us dramatically lower gas costs for compute-intensive AI workloads.

Contract Addresses

AddressNamePurpose
0x0100MODEL_DEPLOYRegister and deploy AI models to the on-chain registry
0x0101MODEL_INFERENCEExecute single inference requests against registered models
0x0102BATCH_INFERENCEExecute batched inference calls with amortized costs
0x0103MODEL_METADATAQuery on-chain model metadata (read-only)
0x0104PROOF_VERIFYVerify zero-knowledge proofs of inference results
0x0105MODEL_BENCHMARKRun standardized benchmark suites against models
0x0106MODEL_ENCRYPTIONEncrypt or decrypt model weights via secure enclave

MODEL_DEPLOY (0x0100)

MODEL_DEPLOY is the entry point for registering AI models on the Citrate network. Model weights are stored off-chain (IPFS or Arweave) while metadata and content hashes are committed on-chain.

interface IModelDeploy {
    /// Register a new model with metadata and a SALT stake bond.
    function deployModel(
        string calldata name,
        bytes32 modelHash,          // SHA-256 hash of the model weights
        string calldata storageUri, // IPFS or Arweave URI
        string calldata format,     // "gguf", "onnx", "safetensors", "mlx"
        bytes calldata inputSchema,
        bytes calldata outputSchema
    ) external returns (bytes32 modelId);
}

Gas costs:

  • Base cost: 100,000 gas + 16 gas per input byte

MODEL_INFERENCE (0x0101)

MODEL_INFERENCE dispatches a single inference request to a registered model. The precompile loads the model, runs inference in a sandboxed runtime, and returns the output with an optional ZK proof.

interface IModelInference {
    /// Execute a single inference call.
    function runInference(
        bytes32 modelId,
        bytes calldata input,
        bool generateProof
    ) external returns (bytes memory output, bytes memory proof);
}

Gas costs:

  • Base cost: 5,000 gas + 10 gas per input element
  • Add 200,000 gas if generateProof is true

BATCH_INFERENCE (0x0102)

BATCH_INFERENCE executes multiple inference calls in a single precompile invocation, amortizing model loading costs. Batch calls receive a 20% gas discount compared to equivalent individual calls.

interface IBatchInference {
    /// Execute batched inference calls (max batch size: 32).
    function batchInference(
        bytes32 modelId,
        bytes[] calldata inputs,
        bool generateProofs
    ) external returns (bytes[] memory outputs, bytes[] memory proofs);
}

Gas costs:

  • Base cost: 5,000 gas + (batch_size * input_size * 6 gas)
  • 20% discount vs. equivalent individual MODEL_INFERENCE calls
  • Maximum batch size: 32

MODEL_METADATA (0x0103)

MODEL_METADATA returns on-chain metadata for a registered model. This is a read-only operation with no state changes.

interface IModelMetadata {
    /// Query model metadata by ID.
    function getModelMetadata(
        bytes32 modelId
    ) external view returns (
        string memory name,
        bytes32 modelHash,
        string memory storageUri,
        string memory format,
        address owner,
        uint256 deployBlock,
        uint256 inferenceCount,
        bytes memory inputSchema,
        bytes memory outputSchema
    );
}

Gas costs: 2,600 gas (equivalent to a cold SLOAD).

PROOF_VERIFY (0x0104)

PROOF_VERIFY verifies zero-knowledge proofs of inference results on-chain. When a node fulfills an inference request with generateProof = true, it produces a cryptographic attestation. This precompile verifies those proofs efficiently.

interface IProofVerify {
    /// Verify a ZK inference proof.
    function verifyProof(
        bytes calldata proof,
        bytes32 modelId,
        bytes32 inputHash,
        bytes32 outputHash
    ) external view returns (bool valid);
}

Gas costs: 3,000 gas base + 16 gas per proof byte

MODEL_BENCHMARK (0x0105)

MODEL_BENCHMARK runs a standardized benchmark suite against a registered model and returns performance metrics. Results are stored on-chain and used by the mentorship protocol to rank model providers.

interface IModelBenchmark {
    /// Run benchmarks against a registered model.
    function benchmarkModel(
        bytes32 modelId,
        bytes calldata benchmarkSuite
    ) external returns (
        uint256 latencyMs,
        uint256 throughput,
        uint256 accuracy,       // Basis points (0-10000)
        bytes32 resultHash
    );
}

Gas costs: 500,000 gas + model-size-dependent overhead

MODEL_ENCRYPTION (0x0106)

MODEL_ENCRYPTION encrypts or decrypts model weights using the node's secure enclave. This enables confidential model deployment where weights remain private.

interface IModelEncryption {
    /// Encrypt or decrypt model weights.
    function encryptModel(
        bytes calldata modelWeights,
        bytes32 encryptionKey,
        bool isEncrypt           // true = encrypt, false = decrypt
    ) external returns (bytes memory result);
}

Gas costs: 100,000 gas + 32 gas per input byte

Calling Precompiles from Solidity

Because precompiles sit at fixed addresses, you can call them using standard Solidity interface patterns:

import "./interfaces/IModelDeploy.sol";
 
contract MyAIApp {
    IModelDeploy constant modelDeploy = IModelDeploy(address(0x0100));
    IModelInference constant inference = IModelInference(address(0x0101));
 
    function runModel(bytes32 modelId, bytes calldata input)
        external returns (bytes memory output)
    {
        (output, ) = inference.runInference(modelId, input, false);
    }
}

All seven precompile addresses (0x0100 through 0x0106) are reserved at genesis and cannot be overwritten by user-deployed contracts.

Further Reading