@axiom-crypto/tools
Version:
Useful data, field, and byte manipulation tools for Axiom.
321 lines (320 loc) • 17.1 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.encodeBeaconValidatorSubquery = exports.encodeSolidityNestedMappingSubquery = exports.encodeReceiptSubquery = exports.encodeTxSubquery = exports.encodeStorageSubquery = exports.encodeAccountSubquery = exports.encodeHeaderSubquery = exports.encodeSubquery = exports.encodeFeeData = exports.getCallbackHash = exports.encodeCallback = exports.encodeComputeQuery = exports.encodeQuerySchema = exports.encodeDataSubquery = exports.encodeDataQuery = exports.getSubqueryHash = exports.getDataQueryHashFromSubqueries = exports.getDataQueryHash = exports.getQuerySchemaHash = exports.getResultHash = exports.encodeResult = exports.getQueryId = exports.getQueryHashV2 = exports.encodeFullQueryV2 = exports.encodeQueryV2 = void 0;
const ethers_1 = require("ethers");
const types_1 = require("./types");
const constants_1 = require("./constants");
const bytes_1 = require("../../utils/bytes");
const byteLength_1 = require("../../utils/byteLength");
const constants_2 = require("../../constants");
/**
* Encodes a v2 Query as a hex string
* @param sourceChainId
* @param caller
* @param dataQueryHash
* @param computeQuery
* @param callback
* @param feeData
* @param userSalt
* @param refundee
* @returns data encoded as hex string of bytes
*/
function encodeQueryV2(sourceChainId, caller, dataQueryHash, computeQuery, callback, feeData, userSalt, refundee) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
(0, bytes_1.validateAddress)(caller);
(0, bytes_1.validateBytes32)(dataQueryHash);
(0, bytes_1.validateBytes32)(userSalt);
(0, bytes_1.validateAddress)(refundee);
const encodedPart0 = ethers_1.ethers.solidityPacked(["uint8", "uint64", "address", "bytes32"], [constants_1.ConstantsV2.VERSION, sourceChainId, caller, dataQueryHash]);
const encodedComputeQuery = encodeComputeQuery(computeQuery.k, computeQuery.resultLen ?? constants_2.AxiomV2CircuitConstant.UserMaxOutputs, computeQuery.vkey, computeQuery.computeProof);
const encodedCallback = encodeCallback(callback.target, callback.extraData);
const encodedFeeData = encodeFeeData(feeData.maxFeePerGas, feeData.callbackGasLimit, feeData.overrideAxiomQueryFee);
return ethers_1.ethers.concat([
encodedPart0,
encodedComputeQuery,
encodedCallback,
encodedFeeData,
userSalt,
refundee,
]);
}
exports.encodeQueryV2 = encodeQueryV2;
/**
* Encodes the full AxiomV2Query that can be fully decoded later
*
*/
function encodeFullQueryV2(sourceChainId, caller, dataQuery, computeQuery, callback, feeData, userSalt, refundee) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
(0, bytes_1.validateAddress)(caller);
(0, bytes_1.validateBytes32)(userSalt);
(0, bytes_1.validateAddress)(refundee);
const encodedPart0 = ethers_1.ethers.solidityPacked(["uint8", "uint64", "address"], [constants_1.ConstantsV2.VERSION, sourceChainId, caller]);
const encodedDataQuery = encodeDataQuery(dataQuery.sourceChainId, dataQuery.subqueries);
const encodedComputeQuery = encodeComputeQuery(computeQuery.k, computeQuery.resultLen ?? constants_2.AxiomV2CircuitConstant.UserMaxOutputs, computeQuery.vkey, computeQuery.computeProof);
const encodedCallback = encodeCallback(callback.target, callback.extraData);
const encodedFeeData = encodeFeeData(feeData.maxFeePerGas, feeData.callbackGasLimit, feeData.overrideAxiomQueryFee);
return ethers_1.ethers.concat([
encodedPart0,
encodedDataQuery,
encodedComputeQuery,
encodedCallback,
encodedFeeData,
userSalt,
refundee,
]);
}
exports.encodeFullQueryV2 = encodeFullQueryV2;
/**
* Computes the query hash for a v2 query:
* queryHash = keccak(version . sourceChainId . dataQueryHash . computeQuery)
* @param sourceChainId
* @param dataQueryHash
* @param computeQuery
* @returns query hash as hex string of bytes
*/
function getQueryHashV2(sourceChainId, dataQueryHash, computeQuery) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
(0, bytes_1.validateBytes32)(dataQueryHash);
const encoded = ethers_1.ethers.solidityPacked(["uint8", "uint64", "bytes32"], [constants_1.ConstantsV2.VERSION, sourceChainId, dataQueryHash]);
const encodedComputeQuery = encodeComputeQuery(computeQuery.k, computeQuery.resultLen ?? 0, computeQuery.vkey, computeQuery.computeProof);
const concatenated = ethers_1.ethers.concat([encoded, encodedComputeQuery]);
return ethers_1.ethers.keccak256(concatenated);
}
exports.getQueryHashV2 = getQueryHashV2;
/**
* Calculates the queryId:
* queryId = keccak(targetChainId . caller . userSalt . queryHash . callbackHash . refundee)
*/
function getQueryId(targetChainId, caller, userSalt, queryHash, callbackHash, refundee) {
(0, byteLength_1.validateSize)(targetChainId, "uint64");
(0, bytes_1.validateAddress)(caller);
(0, bytes_1.validateBytes32)(userSalt);
(0, bytes_1.validateBytes32)(queryHash);
(0, bytes_1.validateBytes32)(callbackHash);
(0, bytes_1.validateAddress)(refundee);
const encoded = ethers_1.ethers.solidityPacked(["uint64", "address", "bytes32", "bytes32", "bytes32", "address"], [targetChainId, caller, userSalt, queryHash, callbackHash, refundee]);
return ethers_1.ethers.keccak256(encoded);
}
exports.getQueryId = getQueryId;
function encodeResult(sourceChainId, dataResultsRoot, dataResultsPoseidonRoot, computeResultsHash) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
(0, bytes_1.validateBytes32)(dataResultsRoot);
(0, bytes_1.validateBytes32)(dataResultsPoseidonRoot);
(0, bytes_1.validateBytes32)(computeResultsHash);
const encoded = ethers_1.ethers.solidityPacked(["uint64", "bytes32", "bytes32", "bytes32"], [sourceChainId, dataResultsRoot, dataResultsPoseidonRoot, computeResultsHash]);
return encoded;
}
exports.encodeResult = encodeResult;
function getResultHash(result) {
const encoded = encodeResult(result.sourceChainId, result.dataResultsRoot, result.dataResultsPoseidonRoot, result.computeResultsHash);
return ethers_1.ethers.keccak256(encoded);
}
exports.getResultHash = getResultHash;
function getQuerySchemaHash(k, resultLen, vkey) {
if (k === 0) {
return (0, bytes_1.bytes32)(0);
}
const encodedQuerySchema = encodeQuerySchema(k, resultLen, vkey);
return ethers_1.ethers.keccak256(encodedQuerySchema);
}
exports.getQuerySchemaHash = getQuerySchemaHash;
function getDataQueryHash(sourceChainId, subqueryHashes) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
const encodedSourceChainId = ethers_1.ethers.solidityPacked(["uint64"], [sourceChainId]);
const encodedSubqueryHashes = ethers_1.ethers.concat(subqueryHashes);
return ethers_1.ethers.keccak256(ethers_1.ethers.concat([encodedSourceChainId, encodedSubqueryHashes]));
}
exports.getDataQueryHash = getDataQueryHash;
function getDataQueryHashFromSubqueries(sourceChainId, dataSubqueries) {
return getDataQueryHash(sourceChainId, dataSubqueries.map((subquery) => getSubqueryHash(subquery.type, encodeDataSubquery(subquery))));
}
exports.getDataQueryHashFromSubqueries = getDataQueryHashFromSubqueries;
function getSubqueryHash(type, subqueryData) {
const encoded = encodeSubquery(type, subqueryData);
return ethers_1.ethers.keccak256(encoded);
}
exports.getSubqueryHash = getSubqueryHash;
/**
* Encodes a full DataQuery
* @param sourceChainId (uint64) chainId of the source chain
* @param dataSubqueries (DataSubquery[]) an array of DataSubqueries to be encoded
* @returns hex string of encoded query data
*/
function encodeDataQuery(sourceChainId, dataSubqueries) {
(0, byteLength_1.validateSize)(sourceChainId, "uint64");
let encodedSubqueries = "0x";
for (const subquery of dataSubqueries) {
let encodedSubquery = encodeDataSubquery(subquery);
encodedSubqueries = ethers_1.ethers.concat([
encodedSubqueries,
encodeSubquery(subquery.type, encodedSubquery),
]);
}
const encodedSourceChainId = ethers_1.ethers.solidityPacked(["uint64", "uint16"], [sourceChainId, dataSubqueries.length]);
return ethers_1.ethers.concat([encodedSourceChainId, encodedSubqueries]);
}
exports.encodeDataQuery = encodeDataQuery;
function encodeDataSubquery(subquery) {
let encodedSubquery;
switch (subquery.type) {
case types_1.DataSubqueryType.Header:
const headerSubquery = subquery.subqueryData;
encodedSubquery = encodeHeaderSubquery(headerSubquery.blockNumber, headerSubquery.fieldIdx);
break;
case types_1.DataSubqueryType.Account:
const accountSubquery = subquery.subqueryData;
encodedSubquery = encodeAccountSubquery(accountSubquery.blockNumber, accountSubquery.addr, accountSubquery.fieldIdx);
break;
case types_1.DataSubqueryType.Storage:
const storageSubquery = subquery.subqueryData;
encodedSubquery = encodeStorageSubquery(storageSubquery.blockNumber, storageSubquery.addr, storageSubquery.slot);
break;
case types_1.DataSubqueryType.Transaction:
const txSubquery = subquery.subqueryData;
encodedSubquery = encodeTxSubquery(txSubquery.blockNumber, txSubquery.txIdx, txSubquery.fieldOrCalldataIdx);
break;
case types_1.DataSubqueryType.Receipt:
const receiptSubquery = subquery.subqueryData;
encodedSubquery = encodeReceiptSubquery(receiptSubquery.blockNumber, receiptSubquery.txIdx, receiptSubquery.fieldOrLogIdx, receiptSubquery.topicOrDataOrAddressIdx, receiptSubquery.eventSchema);
break;
case types_1.DataSubqueryType.SolidityNestedMapping:
const solidityNestedMappingSubquery = subquery.subqueryData;
encodedSubquery = encodeSolidityNestedMappingSubquery(solidityNestedMappingSubquery.blockNumber, solidityNestedMappingSubquery.addr, solidityNestedMappingSubquery.mappingSlot, solidityNestedMappingSubquery.mappingDepth, solidityNestedMappingSubquery.keys);
break;
case types_1.DataSubqueryType.BeaconValidator:
// WIP
const beaconValidatorSubquery = subquery.subqueryData;
encodedSubquery = encodeBeaconValidatorSubquery();
break;
default:
throw new Error("Invalid subquery type");
}
return encodedSubquery;
}
exports.encodeDataSubquery = encodeDataSubquery;
function encodeQuerySchema(k, resultLen, vkey) {
(0, byteLength_1.validateSize)(k, "uint8");
(0, byteLength_1.validateSize)(resultLen, "uint16");
// If k is 0, then the querySchema is empty
if (k === 0) {
return ethers_1.ethers.solidityPacked(["uint8", "uint16"], [0, resultLen]);
}
const encodedComputeQueryParams = ethers_1.ethers.solidityPacked(["uint8", "uint16", "uint8"], [k, resultLen, vkey.length]);
// Ensure all elements of vkey array are 32 bytes long
vkey.forEach((element) => {
if ((0, bytes_1.getNumBytes)(element) !== 32) {
throw new Error("Invalid vkey");
}
});
const encodedVKey = encodeBytes32Array(vkey, vkey.length);
return ethers_1.ethers.concat([encodedComputeQueryParams, encodedVKey]);
}
exports.encodeQuerySchema = encodeQuerySchema;
function encodeComputeQuery(k, resultLen, vkey, computeProof) {
(0, byteLength_1.validateSize)(k, "uint8");
(0, byteLength_1.validateSize)(resultLen, "uint16");
// If k is 0, then the compute query is empty
if (k === 0) {
return ethers_1.ethers.solidityPacked(["uint8", "uint16"], [0, resultLen]);
}
const encodedQuerySchema = encodeQuerySchema(k, resultLen, vkey);
const encodedComputeProof = encodeVarLenBytes(computeProof, "uint32");
return ethers_1.ethers.concat([encodedQuerySchema, encodedComputeProof]);
}
exports.encodeComputeQuery = encodeComputeQuery;
function encodeCallback(target, extraData) {
(0, bytes_1.validateAddress)(target);
const encodedTarget = ethers_1.ethers.solidityPacked(["address"], [target]);
const encodedExtraData = encodeVarLenBytes(extraData, "uint16");
return ethers_1.ethers.concat([encodedTarget, encodedExtraData]);
}
exports.encodeCallback = encodeCallback;
function getCallbackHash(target, extraData) {
const encodedCallback = ethers_1.ethers.solidityPacked(["address", "bytes"], [target, extraData]);
return ethers_1.ethers.keccak256(encodedCallback);
}
exports.getCallbackHash = getCallbackHash;
function encodeFeeData(maxFeePerGas, callbackGasLimit, overrideAxiomQueryFee) {
(0, byteLength_1.validateSize)(maxFeePerGas, "uint64");
(0, byteLength_1.validateSize)(callbackGasLimit, "uint32");
(0, byteLength_1.validateSize)(overrideAxiomQueryFee, "uint256");
return ethers_1.ethers.solidityPacked(["uint64", "uint32", "uint256"], [maxFeePerGas, callbackGasLimit, overrideAxiomQueryFee]);
}
exports.encodeFeeData = encodeFeeData;
function encodeVarLenBytes(data, numType) {
const numBytes = (0, bytes_1.getNumBytes)(data);
return ethers_1.ethers.solidityPacked([numType, "bytes"], [numBytes, data]);
}
/**
* Encodes an array into a concatenated array of `length` bytes32 items
* @param data Array of data
* @param length Desired length of 32-byte array items
* @returns Hex string of encoded data
*/
function encodeBytes32Array(data, length) {
if (data.length > length) {
throw new Error(`Invalid data length: ${data.length} (must be <= ${length})`);
}
const paddedData = data.map((d) => ethers_1.ethers.toBeHex(d, 32));
const gap = length - data.length;
for (let i = 0; i < gap; i++) {
paddedData.push(ethers_1.ethers.ZeroHash);
}
return ethers_1.ethers.concat(paddedData);
}
function encodeSubquery(type, encodedData) {
const encodedType = ethers_1.ethers.solidityPacked(["uint16"], [type]);
return ethers_1.ethers.concat([encodedType, encodedData]);
}
exports.encodeSubquery = encodeSubquery;
function encodeHeaderSubquery(blockNumber, fieldIdx) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, byteLength_1.validateSize)(fieldIdx, "uint32");
return ethers_1.ethers.solidityPacked(["uint32", "uint32"], [blockNumber, fieldIdx]);
}
exports.encodeHeaderSubquery = encodeHeaderSubquery;
function encodeAccountSubquery(blockNumber, addr, fieldIdx) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, bytes_1.validateAddress)(addr);
(0, byteLength_1.validateSize)(fieldIdx, "uint32");
return ethers_1.ethers.solidityPacked(["uint32", "address", "uint32"], [blockNumber, addr, fieldIdx]);
}
exports.encodeAccountSubquery = encodeAccountSubquery;
function encodeStorageSubquery(blockNumber, addr, slot) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, bytes_1.validateAddress)(addr);
(0, byteLength_1.validateSize)(slot, "uint256");
return ethers_1.ethers.solidityPacked(["uint32", "address", "uint256"], [blockNumber, addr, slot]);
}
exports.encodeStorageSubquery = encodeStorageSubquery;
function encodeTxSubquery(blockNumber, txIdx, fieldOrCalldataIdx) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, byteLength_1.validateSize)(txIdx, "uint16");
(0, byteLength_1.validateSize)(fieldOrCalldataIdx, "uint32");
return ethers_1.ethers.solidityPacked(["uint32", "uint16", "uint32"], [blockNumber, txIdx, fieldOrCalldataIdx]);
}
exports.encodeTxSubquery = encodeTxSubquery;
function encodeReceiptSubquery(blockNumber, txIdx, fieldOrLogIdx, topicOrDataOrAddressIdx, eventSchema) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, byteLength_1.validateSize)(txIdx, "uint16");
(0, byteLength_1.validateSize)(fieldOrLogIdx, "uint32");
(0, byteLength_1.validateSize)(topicOrDataOrAddressIdx, "uint32");
(0, bytes_1.validateBytes32)(eventSchema);
return ethers_1.ethers.solidityPacked(["uint32", "uint16", "uint32", "uint32", "bytes32"], [blockNumber, txIdx, fieldOrLogIdx, topicOrDataOrAddressIdx, eventSchema]);
}
exports.encodeReceiptSubquery = encodeReceiptSubquery;
function encodeSolidityNestedMappingSubquery(blockNumber, addr, mappingSlot, mappingDepth, keys) {
(0, byteLength_1.validateSize)(blockNumber, "uint32");
(0, bytes_1.validateAddress)(addr);
(0, byteLength_1.validateSize)(mappingSlot, "uint256");
(0, byteLength_1.validateSize)(mappingDepth, "uint8");
keys.forEach((key) => (0, bytes_1.validateBytes32)(key));
const keysConcat = ethers_1.ethers.concat((0, bytes_1.resizeArray)(keys, Number(mappingDepth), ethers_1.ethers.ZeroHash));
return ethers_1.ethers.solidityPacked(["uint32", "address", "uint256", "uint8", "bytes"], [blockNumber, addr, mappingSlot, mappingDepth, keysConcat]);
}
exports.encodeSolidityNestedMappingSubquery = encodeSolidityNestedMappingSubquery;
function encodeBeaconValidatorSubquery() {
// WIP
return "";
}
exports.encodeBeaconValidatorSubquery = encodeBeaconValidatorSubquery;