Smart Contract Integration
To receive Axiom query results in your smart contract, you will need to implement an Axiom-specific callback function which will be called after the query results have been ZK-verified on-chain. To do this, first install axiom-std in your Foundry project:
forge install axiom-crypto/axiom-std
and add @axiom-crypto/axiom-std/=lib/axiom-std/src
to your remappings.txt
. Installing axiom-std
will also install axiom-v2-periphery, which contains the client smart contracts and interfaces needed to integrate Axiom V2, and Foundry test utilities specific to testing Axiom applications.
Implementing an Axiom Client Contract
To receive callbacks from Axiom V2 in your contract, you will need to extend the AxiomV2Client
abstract contract, which is available via @axiom-crypto/v2-periphery
. This will require implementing _validateAxiomV2Call
and _axiomV2Callback
, which validate and implement the callback.
pragma solidity ^0.8.0;
import { AxiomV2Client } from "axiom-crypto/v2-periphery/client/AxiomV2Client.sol";
contract AxiomExample is AxiomV2Client {
bytes32 immutable QUERY_SCHEMA;
uint64 immutable SOURCE_CHAIN_ID;
constructor(address _axiomV2QueryAddress, uint64 _callbackSourceChainId, bytes32 _querySchema)
AxiomV2Client(_axiomV2QueryAddress)
{
QUERY_SCHEMA = _querySchema;
SOURCE_CHAIN_ID = _callbackSourceChainId;
}
function _validateAxiomV2Call(
AxiomCallbackType callbackType,
uint64 sourceChainId,
address caller,
bytes32 querySchema,
uint256 queryId,
bytes calldata extraData
) internal view override {
require(sourceChainId == SOURCE_CHAIN_ID, "Source chain ID does not match");
require(querySchema == QUERY_SCHEMA, "Invalid query schema");
// <Add any additional desired validation>
}
function _axiomV2Callback(
uint64 sourceChainId,
address caller,
bytes32 querySchema,
uint256 queryId,
bytes32[] calldata axiomResults,
bytes calldata extraData
) internal override {
// <Implement your application logic>
}
}
Axiom Callback Format
The AxiomV2Client
abstract contract implements the axiomV2Callback
callback function:
function axiomV2Callback(
uint64 sourceChainId,
address caller,
bytes32 querySchema,
uint256 queryId,
bytes32[] calldata axiomResults,
bytes calldata extraData
) external {
if (msg.sender != axiomV2QueryAddress) {
revert CallerMustBeAxiomV2Query();
}
emit AxiomV2Call(sourceChainId, caller, querySchema, queryId);
_validateAxiomV2Call(
AxiomCallbackType.OnChain,
sourceChainId,
caller,
querySchema,
queryId,
extraData
);
_axiomV2Callback(sourceChainId, caller, querySchema, queryId, axiomResults, extraData);
}
This callback will be called by AxiomV2Query
after successful query fulfillment. You can implement custom query validation and callback execution by overriding _validateAxiomV2Call
and _axiomV2Callback
, which have access to:
sourceChainId
: The numerical ID of the chain that the query was generated fromcaller
: The original address that sent the query to Axiom V2querySchema
: A unique hash identifier of the client circuit used to specify the queryqueryId
: A unique identifier for the submitted query, including user-specific information such as the caller and callback addressesaxiomResults
: An array of the results inbytes32
formatcallbackExtraData
: Arbitrary extra data specified in the query.
In addition, for query validation you can use:
callbackType
: Whether the query into Axiom was initiated on-chain or off-chain
Query Validation
The _validateAxiomV2Call
function allows you to validate Axiom callbacks to ensure they are pertinent to your smart contract. The most frequent checks are:
- Ensuring the
sourceChainId
corresponds to the chain you are expecting. - Ensuring the
querySchema
matches the Axiom client circuit you are expecting.
You may also want to check that caller
comes from a set of allowed callers or queryId
corresponds to an Axiom query previously requested from your contract. To read about what the querySchema
is, see Axiom Query Format.
Callback execution
Once validated, the callback data is provided to your contract for execution. The most relevant fields will be the axiomResults
array, which contains the outputs of the Axiom client circuit in bytes32
form. Elements of axiomResults
correspond to the order in which addToCallback
is called in the Axiom client circuit.