@axiom-crypto/experimental
v2.2.43
Published
SDK to build on top of Axiom, the ZK Coprocessor for Ethereum.
Downloads
508
Readme
Axiom SDK
Axiom is a ZK coprocessor for Ethereum. Utilizing the properties of Zero Knowledge proofs, Axiom allows anyone to prove historical data on-chain and trustlessly use that data in a smart contract.
Getting started with v2
Create an Axiom
instance and a QueryV2
instance from it (we will use Goerli testnet in this example):
const config: AxiomConfig = {
privateKey: process.env.PRIVATE_KEY_GOERLI as string,
providerUri: process.env.PROVIDER_URI_GOERLI as string,
version: "v2",
chainId: 5, // Goerli
mock: true,
};
const axiom = new Axiom(config);
const query = (axiom.query as QueryV2).new();
Building a Query
There are two ways to build a query. We'll cover both of them here.
// Some constants used below
const BLOCK_NUM = 9500000;
const WETH_ADDR = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
const WSOL_ADDR = "0xd31a59c85ae9d8edefec411d448f90841571b89c";
const WETH_WHALE = "0x2E15D7AA0650dE1009710FDd45C3468d75AE1392";
const UNI_V3_FACTORY_ADDR = "0x1F98431c8aD98523631AE4a59f267346ea31F984";
Building a Query: Appending method
// Appends a subquery where we look for the gas used at block BLOCK_NUM
const headerSubquery = buildHeaderSubquery(BLOCK_NUM)
.field(HeaderField.GasUsed);
query.appendDataSubquery(headerSubquery);
// Appends a receipt subquery for transaction
// 0x0a126c0e009e19af335e964de0cea513098c9efe290c269dee77ca9f10838e7b in which we look at
// log index 4 (Transfer (index_topic_1 address from, index_topic_2 address to, uint256 value))
// and the second topic (indexed address to) of that log event
const txHash = "0x0a126c0e009e19af335e964de0cea513098c9efe290c269dee77ca9f10838e7b";
const swapEventSchema = getEventSchema(
"Swap(address,uint256,uint256,uint256,uint256,address)"
);
const receiptSubquery = buildReceiptSubquery(txHash)
.log(4) // event
.topic(0) // event schema
.eventSchema(swapEventSchema);
query.appendDataSubquery(receiptSubquery);
// slot 5: mapping(address => mapping(address => mapping(uint24 => address))) public override getPool;
const mappingSubquery = buildSolidityNestedMappingSubquery(BLOCK_NUM)
.address(UNI_V3_FACTORY_ADDR)
.mappingSlot(5)
.keys([
WETH_ADDR,
WSOL_ADDR,
10000,
]);
query.appendDataSubquery(mappingSubquery);
const callbackQuery = {
target: WETH_ADDR,
extraData: ethers.solidityPacked(["address"], [WETH_WHALE]),
};
query.setCallback(callbackQuery);
await query.build();
Building a Query: Passing in objects
const dataQuery = [
{
blockNumber: BLOCK_NUM,
fieldIdx: HeaderField.GasLimit,
},
{
blockNumber: BLOCK_NUM + 1,
fieldIdx: HeaderField.StateRoot,
},
{
blockNumber: BLOCK_NUM,
addr: WETH_WHALE,
fieldIdx: AccountField.Nonce,
},
{
txHash: "0x47082a4eaba054312c652a21c6d75a44095b8be43c60bdaeffad03d38a8b1602",
fieldOrLogIdx: ReceiptField.CumulativeGas,
topicOrDataOrAddressIdx: 0,
eventSchema: ethers.ZeroHash,
},
];
const callbackQuery = {
target: WETH_ADDR,
extraData: ethers.solidityPacked(["address"], [WETH_WHALE]),
};
const query = (axiom.query as QueryV2).new(
dataQuery,
undefined, // no computeQuery
callbackQuery,
);
Validating a Query
Validation will write to console.error
for any errors detected in each of the fields (it will not throw an error or return early) and will return false
if there is any error detected in any of the Query
fields.
const isValid = await query.validate();
if (!isValid) {
throw new Error("Query validation failed.");
}
Submitting a Query
Once a Query
has been built, it can be submitted via two methods: On-chain or via IPFS.
Submitting a Query: On-chain
// ensure you've already called `await query.build()`
const payment = query.calculateFee();
const queryId = await query.sendOnchainQuery(
payment,
(receipt: ethers.ContractTransactionReceipt) => {
// You can do something here once you've received the transaction receipt
console.log("receipt", receipt);
}
);
console.log("queryId", queryId);
Submitting a Query: IPFS
// WIP: will be supported soon
// ensure you've already called `await query.build()`
const payment = query.calculateFee();
const queryId = await query.sendQueryWithIpfs(
payment,
(receipt: ethers.ContractTransactionReceipt) => {
// You can do something here once you've received the transaction receipt
console.log("receipt", receipt);
}
);
console.log("queryId", queryId);
Additional examples
There are also numerous up-to-date examples in the test/unit/v2/
folder. Some slightly older examples exist in the Examples V2 repo.