Handling Axiom Callbacks

Using Axiom-verified results in your contract

Callbacks are triggered automatically once your Query is fulfilled by the Axiom V2 prover. The proof will be verified via Axiom's on-chain verifier before the callback to your smart contract is called.

Specifying a callback in the Axiom SDK

The AxiomV2Callback struct can be built with the following fields:

  • target: the address of the contract to call the callback on

  • extraData: data that's passed to the callback (the EVM will automatically right-pad zeroes to this field for any size, which is probably not what you want, so it's recommended that you convert this to bytes32 if you are dealing with types that are not a multiple of 32 bytes)

const exampleClientAddr = "0x888d44c887DFCfaeBBf41C53eD87C0C9ED994165";
const callback: AxiomV2Callback = {
    target: exampleClientAddr,
    extraData: bytes32(0),
}

And subsequently set for the Query:

query.setCallback(callback);

The callback's resultLen will be automatically updated based on the dataQuery or computeQuery set on the Query.

Receiving the Callback

In order to properly receive the Axiom V2 callback in your contract, you'll need to create a function that has the following argument format:

function axiomV2Callback(
    uint64 sourceChainId,
    address caller,
    bytes32 querySchema,
    uint256 queryId,
    bytes32[] calldata axiomResults,
    bytes calldata extraData
) external { 
    // validate msg.sender against the AxiomV2Query address
    <...>
    
    // validate the sourceChainId, caller, querySchema, and queryId
    <...>
    
    // perform your application logic
    <...>
}
  • sourceChainId: The numerical ID of the chain that the Query was generated from

  • caller: The original contract or EOA that sent the query to Axiom V2

  • querySchema: The hashed schema, defined as keccak256(k . vkeyLen . vkey) and is essentially a commitment to the circuit defined in the computeQuery

  • queryId: A unique identifier for the submitted Query

  • axiomResults: An array of the results in bytes32 format

    • If you used a ComputeQuery, your results will be returned in the order and number of times in which you called addToCallback(value)

    • If you used a DataQuery, your results will be returned as value of the results of your data subqueries in the order that you appended them to the Query

Parsing the Callback

When you receive the callback, you will want to validate that all of the fields match so that no one who maliciously submits a Query with invalid data is able to pass the validation.

Additionally, you will want to parse the axiomResults array to ensure that the data that is passed in matches all of the conditions that are required for the user. For example, if you require that a user submits a proof that their account balance is above 1 ETH at some historic block, you'll likely want to validate that the value of the index of the axiomResults array of the account balance is greater than 1 ETH.

Examples

Last updated