Skip to content

Proposal preview

Use Deference from a contract

Request a Panel answer or a chat answer from a Sepolia contract through DeferenceIntake, and receive it on chain.

Proposal preview

DeferenceIntake runs on Sepolia and is not part of deference.si yet. While this deployment answers in example mode, the fulfiller charges a cost of 0, so the whole escrow goes back to the payer.

DeferenceIntake is the front door to Deference for other contracts. A payer escrows up to maxCost INFERENCE with a request; the Deference fulfiller serves it off chain and calls fulfill with the result, its attestation and the actual cost. The cost is activated as service credit for the Deference account that served the request, the rest goes back to the payer, and the payer's callback, if any, receives the result.

ContractSepolia address
DeferenceIntake0x5c67d511EAE08d51C77185Be958aE55C7F46098b
INFERENCE (6 decimals)0x59e95fEde655A5E3e62067658e113A5292538193
ContestJudge (Panel verifier)0xD55Db52c37B4eE31804F6F30f0f5e1B446615Fc7

Request

function request(bytes32 kind, bytes calldata body, Callback calldata callback, uint256 maxCost)
    external
    returns (bytes32 requestId);

struct Callback {
    address target;   // zero for no callback
    bytes4 selector;  // called as target.selector(bytes32 requestId, bytes result)
    uint32 gasLimit;  // exactly this much gas
}

Approve maxCost INFERENCE to the intake first. Two kinds are enabled, each with a cap of 50 INFERENCE per request:

panel@1bytes32
A Panel verdict. The body is the JSON of the Panel request, for example {"question":"Did team Blue win?","panel":{"members":[...],"answer_type":"bool"}}. A plain-text body is read as the question, put to a default panel of five labs.
chat@1bytes32
One chat answer. The body is {"prompt":"..."}, a chat completions body, or plain text.

A kind is the ASCII name right-padded to 32 bytes, bytes32("panel@1") in Solidity. The body is at most 16,384 bytes.

Result

fulfill emits Fulfilled(requestId, resultHash, cost, callbackOk) and, for a Panel, Attested(requestId, attestation).

  • result is UTF-8 JSON. A Panel gives {"mode","kind","status","answer","agreed","size","quorum"}; a chat answer gives {"mode","kind","model","content"}.
  • attestation is abi.encode(PanelAttestation.Attestation, bytes signature), with the on-chain request id as its requestId. Decode it and pass both parts to PanelAttestation.verify to act on a verdict with the same checks as a direct Panel call.
  • The callback gets the result when it is at most 4,096 bytes, else empty bytes; resultHash always covers the full result. A reverting callback is recorded as callbackOk = false and never undoes the fulfilment.

Cancel

A request nobody fulfils can be cancelled by its payer once cancelAfter has passed, for a full refund:

INTAKE.cancel(requestId);

Example consumer

contract AsksThePanel {
    IDeferenceIntake public constant INTAKE = IDeferenceIntake(0x5c67d511EAE08d51C77185Be958aE55C7F46098b);
    IERC20 public constant INFERENCE = IERC20(0x59e95fEde655A5E3e62067658e113A5292538193);
    mapping(bytes32 requestId => bytes result) public results;

    function ask(string calldata question) external returns (bytes32 requestId) {
        uint256 maxCost = 1e6;
        INFERENCE.approve(address(INTAKE), maxCost);
        requestId = INTAKE.request(
            bytes32("panel@1"),
            bytes(string.concat('{"question":"', question, '"}')),
            IDeferenceIntake.Callback(address(this), this.onResult.selector, 200_000),
            maxCost
        );
    }

    function onResult(bytes32 requestId, bytes calldata result) external {
        require(msg.sender == address(INTAKE), "only the intake");
        results[requestId] = result;
    }
}

The contract must hold the INFERENCE it escrows. The Oracle page lists recent requests and sends one from your wallet.