import { Tendermint34Client } from "@cosmjs/tendermint-rpc"; import { QueryClient } from "@cosmjs/stargate"; import { WasmExtension, setupWasmExtension } from "@cosmjs/cosmwasm-stargate"; // TODO: expose the querier and consume them in price pusher /** * Interface for classes implementing a querier for a cosmwasm chain. * * The querier interacts with contracts only and can get it's info, stored state. * It can also query the `Query methods` defined in the contract. * * For contract dependent response, you need to look into contract specific schema in order * to know what to expect * * @interface ChainQuerier */ export interface ChainQuerier { /** * `getContractInfo` gets the contract info for the give contract address. * @param {ContractInfoRequest} req * @param {string} req.contractAddr * @returns {ContractInfoResponse} * - {number} res.codeId. The codeId of the contract's code. * - {string} res.creator. The creator of the contract. * - {string} res.adminAddr - Address of the current admin. * - {string} res.label - The label with which the contract was instantiated * * @throws an error if it fails. */ getContractInfo(req: ContractInfoRequest): Promise; /** * `getSmartContractState` query the `Query methods` implemented in the contract * @param {SmartContractRequest} req * @param {string} req.contractAddr * @param {Object} req.query - The query object for the contract. It accepts any object as it is contract dependent. * @returns {Object} - It returns an object. As the response is contract dependent we can't have a structure for it. * * @throws an error if it fails. */ getSmartContractState(req: SmartContractRequest): Promise; /** * Contracts local storage on chain is structured as key-value pairs. * `getSmartContractState` query the local storage of a contract for the given key. * @param {RawContractStateRequest} req * @param {string} req.contractAddr * @param {Buffer} req.key * @returns {Object} - It returns an object. As the response is contract dependent we can't have a structure for it. * * @throws an error if it fails. */ getRawContractState(req: RawContractStateRequest): Promise; /** * Contracts local storage on chain is structured as key-value pairs. * `getAllContractState` query the local storage of a contract for all such pairs. * @param {AllContractStateRequest} req * @param {string} req.contractAddr * @returns {Object} - It returns an object. As the response is contract dependent we can't have a structure for it. * * @throws an error if it fails. */ getAllContractState(req: AllContractStateRequest): Promise; } export type ContractInfoRequest = { contractAddr: string; }; export type ContractInfoResponse = { codeId: number; creator: string; adminAddr: string; label: string; }; export type SmartContractRequest = { contractAddr: string; query: Object; }; export type RawContractStateRequest = { contractAddr: string; key: Buffer; }; export type GetCodeRequest = { codeId: number; }; export type AllContractStateRequest = { contractAddr: string; }; export class CosmwasmQuerier implements ChainQuerier { private readonly wasmQueryClient: WasmExtension; private constructor(readonly tendermintClient: Tendermint34Client) { this.wasmQueryClient = setupWasmExtension( new QueryClient(tendermintClient) ); } async getContractInfo( req: ContractInfoRequest ): Promise { const { contractAddr } = req; const { wasm: wasmQueryClient } = this.wasmQueryClient; const { contractInfo } = await wasmQueryClient.getContractInfo( contractAddr ); if (contractInfo === undefined) throw new Error("error fetching contract info"); return { ...contractInfo, codeId: Number(contractInfo.codeId), adminAddr: contractInfo.admin, }; } async getSmartContractState(req: SmartContractRequest): Promise { const { contractAddr, query } = req; const { wasm: wasmQueryClient } = this.wasmQueryClient; return await wasmQueryClient.queryContractSmart(contractAddr, query); } async getRawContractState(req: RawContractStateRequest): Promise { const { contractAddr, key } = req; const { wasm: wasmQueryClient } = this.wasmQueryClient; const { data } = await wasmQueryClient.queryContractRaw(contractAddr, key); return JSON.parse(Buffer.from(data).toString()); } async getCode(req: GetCodeRequest): Promise { const { codeId } = req; const { wasm: wasmQueryClient } = this.wasmQueryClient; const { data } = await wasmQueryClient.getCode(codeId); return Buffer.from(data); } async getAllContractState(req: AllContractStateRequest): Promise { const { contractAddr } = req; const { wasm: wasmQueryClient } = this.wasmQueryClient; const { models } = await wasmQueryClient.getAllContractState(contractAddr); const state = models.reduce((prevValue, model) => { const key = Buffer.from(model.key).toString(); const value = Buffer.from(model.value).toString(); prevValue[key] = value; return prevValue; }, {} as any); return state; } static async connect(endpoint: string): Promise { const tendermintClient = await Tendermint34Client.connect(endpoint); return new CosmwasmQuerier(tendermintClient); } }