Batch Query Examples
This page demonstrates how to query using a smart contract batch query router. The purpose of a query router is to combine multiple queries to one or more contracts into a single http request. This reduces load on the node infrastructure and is faster for retrieving data than would otherwise be obtained by individual contract queries.
The batch query function is generalized to work with any contract queries. Miscellaneous ShadeJS services already implement the batch query router, for example the Pairs Info Query
By default, the batch query will process all queries in a single thread, i.e. using a single node to query. However, there are query gas limitations controlled by the node provider that will throw errors when batch size is too large. When a batch size is provided as an input to the batch query, it will divide the batch into multi-threaded batches of batches of that size. There are certain batch sizes that have already been tested and are provided for specific ShadeJS services and recommended node settings, however your batch size will vary based on the type of query you are performing and your node provider settings.
Batch Query
input
type BatchQueryParams = {
id: string | number,
contract: {
address: string,
codeHash: string,
},
queryMsg: any,
}
// MinBlockHeightValidationOptions is an optional property that is used to validate the
// accuracy of the data in the batch response using an expected minimum
// block height associated with the data. The query will be retried until
// non-stale data is found (up to a max number of retries before error is thrown).
// The assumption is that you are working with a node cluster where one or more
// stale nodes are mixed into healthy nodes, and eventually the query will be
// tried with a healthy node and meet the minimum block height threshold.
// onStaleNodeDetected is a callback function for when stale nodes are found. This
// can be useful for error/node monitoring services.
type MinBlockHeightValidationOptions = {
minBlockHeight: number, // data must come from this block height or newer block
maxRetries: number,
onStaleNodeDetected?: () => void
}
async function batchQuery({
contractAddress,
codeHash,
lcdEndpoint,
chainId,
queries,
batchSize, // defaults to all queries in single batch
minBlockHeightValidationOptions,
}:{
contractAddress: string,
codeHash?: string,
lcdEndpoint?: string,
chainId?: string,
queries: BatchQueryParams[],
batchSize?: number,
minBlockHeightValidationOptions?: MinBlockHeightValidationOptions,
}): Promise<BatchQueryParsedResponse>
type BatchQueryParams = {
id: string | number,
contract: {
address: string,
codeHash: string,
},
queryMsg: any,
}
// MinBlockHeightValidationOptions is an optional property that is used to validate the
// accuracy of the data in the batch response using an expected minimum
// block height associated with the data. The query will be retried until
// non-stale data is found (up to a max number of retries before error is thrown).
// The assumption is that you are working with a node cluster where one or more
// stale nodes are mixed into healthy nodes, and eventually the query will be
// tried with a healthy node and meet the minimum block height threshold.
// onStaleNodeDetected is a callback function for when stale nodes are found. This
// can be useful for error/node monitoring services.
type MinBlockHeightValidationOptions = {
minBlockHeight: number, // data must come from this block height or newer block
maxRetries: number,
onStaleNodeDetected?: () => void
}
async function batchQuery({
contractAddress,
codeHash,
lcdEndpoint,
chainId,
queries,
batchSize, // defaults to all queries in single batch
minBlockHeightValidationOptions,
}:{
contractAddress: string,
codeHash?: string,
lcdEndpoint?: string,
chainId?: string,
queries: BatchQueryParams[],
batchSize?: number,
minBlockHeightValidationOptions?: MinBlockHeightValidationOptions,
}): Promise<BatchQueryParsedResponse>
output
type BatchQueryParsedResponse = BatchQueryParsedResponseItem[]
enum BatchItemResponseStatus {
SUCCESS = 'success',
ERROR = 'error',
}
type BatchQueryParsedResponseItem = {
id: string | number,
response: any,
status?: BatchItemResponseStatus,
blockHeight: number, // the block height that the data is from
}
type BatchQueryParsedResponse = BatchQueryParsedResponseItem[]
enum BatchItemResponseStatus {
SUCCESS = 'success',
ERROR = 'error',
}
type BatchQueryParsedResponseItem = {
id: string | number,
response: any,
status?: BatchItemResponseStatus,
blockHeight: number, // the block height that the data is from
}
example use
const output = await batchQuery({
contractAddress: '[QUERY_ROUTER_CONTRACT_ADDRESS]',
codeHash: '[QUERY_ROUTER_CODE_HASH]',
queries: [{
id: 1,
contract: {
address: 'PAIR_CONTRACT_ADDRESS',
codeHash: 'PAIR_CODE_HASH',
},
queryMsg: { get_config:{}},
}],
})
console.log(output)
const output = await batchQuery({
contractAddress: '[QUERY_ROUTER_CONTRACT_ADDRESS]',
codeHash: '[QUERY_ROUTER_CODE_HASH]',
queries: [{
id: 1,
contract: {
address: 'PAIR_CONTRACT_ADDRESS',
codeHash: 'PAIR_CODE_HASH',
},
queryMsg: { get_config:{}},
}],
})
console.log(output)
console
[{
id: 1,
response: {
get_config: {
factory_contract: {
address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp',
code_hash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e',
},
lp_token: {
address: 'secret1l2u35dcx2a4wyx9a6lxn9va6e66z493ycqxtmx',
code_hash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b',
},
staking_contract: {
address: 'secret16h5sqd79x43wutne8ge3pdz3e3lngw62vy5lmr',
code_hash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49',
},
pair: [
{
custom_token: {
contract_addr: 'secret19e75l25r6sa6nhdf4lggjmgpw0vmpfvsw5cnpe',
token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
},
{
custom_token: {
contract_addr: 'secret16vjfe24un4z7d3sp9vd0cmmfmz397nh2njpw3e',
token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
},
true,
],
custom_fee: {
shade_dao_fee: {
nom: 500,
denom: 1000000,
},
lp_fee: {
nom: 500,
denom: 1000000,
},
},
},
},
blockHeight: 123456789,
}];
[{
id: 1,
response: {
get_config: {
factory_contract: {
address: 'secret1ja0hcwvy76grqkpgwznxukgd7t8a8anmmx05pp',
code_hash: '2ad4ed2a4a45fd6de3daca9541ba82c26bb66c76d1c3540de39b509abd26538e',
},
lp_token: {
address: 'secret1l2u35dcx2a4wyx9a6lxn9va6e66z493ycqxtmx',
code_hash: 'b0c2048d28a0ca0b92274549b336703622ecb24a8c21f417e70c03aa620fcd7b',
},
staking_contract: {
address: 'secret16h5sqd79x43wutne8ge3pdz3e3lngw62vy5lmr',
code_hash: 'a83f0fdc6e5bcdb1f59e39200a084401309fc5338dbb2e54a2bcdc08fa3eaf49',
},
pair: [
{
custom_token: {
contract_addr: 'secret19e75l25r6sa6nhdf4lggjmgpw0vmpfvsw5cnpe',
token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
},
{
custom_token: {
contract_addr: 'secret16vjfe24un4z7d3sp9vd0cmmfmz397nh2njpw3e',
token_code_hash: '638a3e1d50175fbcb8373cf801565283e3eb23d88a9b7b7f99fcc5eb1e6b561e',
},
},
true,
],
custom_fee: {
shade_dao_fee: {
nom: 500,
denom: 1000000,
},
lp_fee: {
nom: 500,
denom: 1000000,
},
},
},
},
blockHeight: 123456789,
}];