Quickstart

Typescript SDK for building Queries into Axiom.

Github repo

The Github repository for this Quickstart is located here:

https://github.com/axiom-crypto/axiom-quickstart/tree/v1

Create a new project

In the command line, create and initialize a new Node.js project with the following commands:

  1. mkdir axiom-quickstart

  2. cd axiom-quickstart

  3. npm init

  4. touch src/index.ts

Installation

You can use your favorite package manager (npm, yarn, pnpm) to install the Axiom SDK:

npm i @axiom-crypto/core@1.0.0

Install the following additional packages:

npm i ethers dotenv typescript ts-node @types/node

Setup

Create a .env.local file to store your provider URI from Alchemy. You can do this in the terminal with the following command:

touch .env

Fill in your .env file with the following information:

PROVIDER_URI_GOERLI=<https Goerli provider URI>
PRIVATE_KEY=<private key for account on Goerli>

Create a new tsconfig.json file via:

tsc --init

The index.ts file in a new src folder inside the axiom-quickstart project. Start by importing the Axiom SDK, Ethers.js, and Dotenv library at the top of the page:

src/index.ts
import { 
  Axiom,
  AxiomConfig,
} from '@axiom-crypto/core';
import type { QueryBuilder } from '@axiom-crypto/core/query/queryBuilder';
import { ethers } from 'ethers';
import dotenv from 'dotenv';
dotenv.config();

Now let's set up a new instance of the Axiom SDK:

src/index.ts
const config: AxiomConfig = {
  providerUri: process.env.PROVIDER_URI_GOERLI || 'http://localhost:8545',
  version: "v1",
  chainId: 5,  // Goerli
  mock: true,
};
const ax = new Axiom(config);

We'll create mock proofs (they are of the same form as real proofs, but verification will always pass) and submit them to Axiom on Goerli testnet.

Create an instance of a signer (wallet) that can sign transactions:

src/index.ts
const provider = new ethers.JsonRpcProvider(process.env.PROVIDER_URI_GOERLI as string);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY as string, provider);

Creating a Query into Axiom

Depending on the historical data you want to query, you can add a QueryRow with block, account, or storage data:

Block dataAccount dataStorage data
  • block number

  • address

  • nonce

  • balance

  • storage root

  • code hash

  • block number

  • address

  • slot number

  • slot value

Building a Query

You can build a query by using the newQueryBuilder convenience function and appending data to it with its append function:

async function buildQuery(): Promise<QueryBuilder> {
  const queryData = [
    {
      blockNumber: 9335357,
      address: "0x4Fb202140c5319106F15706b1A69E441c9536306",
      slot: "0x1f5f6074f4419ff8032f6dd23e65794ca104b323667b66be5a0c73fd6ba2857e",
    }, {
      blockNumber: 9335466,
      address: "0x4Fb202140c5319106F15706b1A69E441c9536306",
      slot: "0xe162aef9009a7c65cb8d0c7992b1086de24c2a149b9b0d3db4ed7e64df46fa0f",
    }, {
      blockNumber: 9335492,
      address: "0x4Fb202140c5319106F15706b1A69E441c9536306",
      slot: (Math.floor(Math.random()*2**53)).toString(), // random slot
    }
  ];
  const qb = ax.newQueryBuilder();
  await qb.append(queryData[0]);
  await qb.append(queryData[1]);
  await qb.append(queryData[2]);
  return qb;
}

Submitting a built Query to Axiom

Next we'll write a function to submit the query on-chain to Axiom:

src/index.ts
async function submitQuery(qb: QueryBuilder) {
  const { keccakQueryResponse, queryHash, query } = await qb.build();
  // Create instance of the axiomV1Query contract - later we'll call methods from this contract
  const axiomV1Query = new ethers.Contract(
    ax.getAxiomQueryAddress() as string,
    ax.getAxiomQueryAbi(),
    wallet
  );
  
  // Create an on-chain transaction encoding this query using the sendQuery function in the AxiomV1Query contract
  const txResult = await axiomV1Query.sendQuery(
    keccakQueryResponse,
    await wallet.getAddress(),
    query,
    {
      value: ethers.parseEther("0.01"), // Goerli payment amount
    }
  );
  const txReceipt = await txResult.wait(); 
  console.log("sendQuery Receipt", txReceipt);

  console.log("Waiting for proof to be generated. This may take a few minutes...")
  // Listen for the QueryFulfilled event emitted by the Axiom contract indicating the proof has been generated
  axiomV1Query.on("QueryFulfilled", async (keccakQueryResponse, _payment, _prover) => {
    console.log("Proof generated!")
  });
}

For more details, see: Submitting a Query.

Running the Example

In order to run the whole thing, call buildQuery and submitQuery in a main function:

src/index.ts
async function main() {
  const qb = await buildQuery();
  await submitQuery(qb);
}
main();

Run this via the following command:

ts-node src/index.ts

Using the result

Once the QueryFulfilled event is emitted, you can then use the dataset of the Query you just built in your smart contract.

See: Reading Query Results

AxiomV1Query Contract

The AxiomV1Query contracts are located at:

Mainnet: 0xd617ab7f787adF64C2b5B920c251ea10Cd35a952

Goerli: 0x4Fb202140c5319106F15706b1A69E441c9536306

Last updated