Skip to main content
Glama
stargateclient.js•13.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.StargateClient = exports.BroadcastTxError = exports.assertIsDeliverTxFailure = exports.assertIsDeliverTxSuccess = exports.isDeliverTxSuccess = exports.isDeliverTxFailure = exports.TimeoutError = void 0; /* eslint-disable @typescript-eslint/naming-convention */ const amino_1 = require("@cosmjs/amino"); const encoding_1 = require("@cosmjs/encoding"); const math_1 = require("@cosmjs/math"); const tendermint_rpc_1 = require("@cosmjs/tendermint-rpc"); const utils_1 = require("@cosmjs/utils"); const abci_1 = require("cosmjs-types/cosmos/base/abci/v1beta1/abci"); const accounts_1 = require("./accounts"); const events_1 = require("./events"); const modules_1 = require("./modules"); const queryclient_1 = require("./queryclient"); class TimeoutError extends Error { constructor(message, txId) { super(message); this.txId = txId; } } exports.TimeoutError = TimeoutError; function isDeliverTxFailure(result) { return !!result.code; } exports.isDeliverTxFailure = isDeliverTxFailure; function isDeliverTxSuccess(result) { return !isDeliverTxFailure(result); } exports.isDeliverTxSuccess = isDeliverTxSuccess; /** * Ensures the given result is a success. Throws a detailed error message otherwise. */ function assertIsDeliverTxSuccess(result) { if (isDeliverTxFailure(result)) { throw new Error(`Error when broadcasting tx ${result.transactionHash} at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`); } } exports.assertIsDeliverTxSuccess = assertIsDeliverTxSuccess; /** * Ensures the given result is a failure. Throws a detailed error message otherwise. */ function assertIsDeliverTxFailure(result) { if (isDeliverTxSuccess(result)) { throw new Error(`Transaction ${result.transactionHash} did not fail at height ${result.height}. Code: ${result.code}; Raw log: ${result.rawLog}`); } } exports.assertIsDeliverTxFailure = assertIsDeliverTxFailure; /** * An error when broadcasting the transaction. This contains the CheckTx errors * from the blockchain. Once a transaction is included in a block no BroadcastTxError * is thrown, even if the execution fails (DeliverTx errors). */ class BroadcastTxError extends Error { constructor(code, codespace, log) { super(`Broadcasting transaction failed with code ${code} (codespace: ${codespace}). Log: ${log}`); this.code = code; this.codespace = codespace; this.log = log; } } exports.BroadcastTxError = BroadcastTxError; class StargateClient { /** * Creates an instance by connecting to the given Tendermint RPC endpoint. * * This uses auto-detection to decide between a Tendermint 0.37 and 0.34 client. * To set the Tendermint client explicitly, use `create`. */ static async connect(endpoint, options = {}) { // Tendermint/CometBFT 0.34/0.37 auto-detection. Starting with 0.37 we seem to get reliable versions again 🎉 // Using 0.34 as the fallback. let tmClient; const tm37Client = await tendermint_rpc_1.Tendermint37Client.connect(endpoint); const version = (await tm37Client.status()).nodeInfo.version; if (version.startsWith("0.37.")) { tmClient = tm37Client; } else { tm37Client.disconnect(); tmClient = await tendermint_rpc_1.Tendermint34Client.connect(endpoint); } return StargateClient.create(tmClient, options); } /** * Creates an instance from a manually created Tendermint client. * Use this to use `Tendermint37Client` instead of `Tendermint34Client`. */ static async create(tmClient, options = {}) { return new StargateClient(tmClient, options); } constructor(tmClient, options) { if (tmClient) { this.tmClient = tmClient; this.queryClient = queryclient_1.QueryClient.withExtensions(tmClient, modules_1.setupAuthExtension, modules_1.setupBankExtension, modules_1.setupStakingExtension, modules_1.setupTxExtension); } const { accountParser = accounts_1.accountFromAny } = options; this.accountParser = accountParser; } getTmClient() { return this.tmClient; } forceGetTmClient() { if (!this.tmClient) { throw new Error("Tendermint client not available. You cannot use online functionality in offline mode."); } return this.tmClient; } getQueryClient() { return this.queryClient; } forceGetQueryClient() { if (!this.queryClient) { throw new Error("Query client not available. You cannot use online functionality in offline mode."); } return this.queryClient; } async getChainId() { if (!this.chainId) { const response = await this.forceGetTmClient().status(); const chainId = response.nodeInfo.network; if (!chainId) throw new Error("Chain ID must not be empty"); this.chainId = chainId; } return this.chainId; } async getHeight() { const status = await this.forceGetTmClient().status(); return status.syncInfo.latestBlockHeight; } async getAccount(searchAddress) { try { const account = await this.forceGetQueryClient().auth.account(searchAddress); return account ? this.accountParser(account) : null; } catch (error) { if (/rpc error: code = NotFound/i.test(error.toString())) { return null; } throw error; } } async getSequence(address) { const account = await this.getAccount(address); if (!account) { throw new Error(`Account '${address}' does not exist on chain. Send some tokens there before trying to query sequence.`); } return { accountNumber: account.accountNumber, sequence: account.sequence, }; } async getBlock(height) { const response = await this.forceGetTmClient().block(height); return { id: (0, encoding_1.toHex)(response.blockId.hash).toUpperCase(), header: { version: { block: new math_1.Uint53(response.block.header.version.block).toString(), app: new math_1.Uint53(response.block.header.version.app).toString(), }, height: response.block.header.height, chainId: response.block.header.chainId, time: (0, tendermint_rpc_1.toRfc3339WithNanoseconds)(response.block.header.time), }, txs: response.block.txs, }; } async getBalance(address, searchDenom) { return this.forceGetQueryClient().bank.balance(address, searchDenom); } /** * Queries all balances for all denoms that belong to this address. * * Uses the grpc queries (which iterates over the store internally), and we cannot get * proofs from such a method. */ async getAllBalances(address) { return this.forceGetQueryClient().bank.allBalances(address); } async getBalanceStaked(address) { const allDelegations = []; let startAtKey = undefined; do { const { delegationResponses, pagination } = await this.forceGetQueryClient().staking.delegatorDelegations(address, startAtKey); const loadedDelegations = delegationResponses || []; allDelegations.push(...loadedDelegations); startAtKey = pagination?.nextKey; } while (startAtKey !== undefined && startAtKey.length !== 0); const sumValues = allDelegations.reduce((previousValue, currentValue) => { // Safe because field is set to non-nullable (https://github.com/cosmos/cosmos-sdk/blob/v0.45.3/proto/cosmos/staking/v1beta1/staking.proto#L295) (0, utils_1.assert)(currentValue.balance); return previousValue !== null ? (0, amino_1.addCoins)(previousValue, currentValue.balance) : currentValue.balance; }, null); return sumValues; } async getDelegation(delegatorAddress, validatorAddress) { let delegatedAmount; try { delegatedAmount = (await this.forceGetQueryClient().staking.delegation(delegatorAddress, validatorAddress)).delegationResponse?.balance; } catch (e) { if (e.toString().includes("key not found")) { // ignore, `delegatedAmount` remains undefined } else { throw e; } } return delegatedAmount || null; } async getTx(id) { const results = await this.txsQuery(`tx.hash='${id}'`); return results[0] ?? null; } async searchTx(query) { let rawQuery; if (typeof query === "string") { rawQuery = query; } else if (Array.isArray(query)) { rawQuery = query.map((t) => `${t.key}='${t.value}'`).join(" AND "); } else { throw new Error("Got unsupported query type. See CosmJS 0.31 CHANGELOG for API breaking changes here."); } return this.txsQuery(rawQuery); } disconnect() { if (this.tmClient) this.tmClient.disconnect(); } /** * Broadcasts a signed transaction to the network and monitors its inclusion in a block. * * If broadcasting is rejected by the node for some reason (e.g. because of a CheckTx failure), * an error is thrown. * * If the transaction is not included in a block before the provided timeout, this errors with a `TimeoutError`. * * If the transaction is included in a block, a `DeliverTxResponse` is returned. The caller then * usually needs to check for execution success or failure. */ async broadcastTx(tx, timeoutMs = 60000, pollIntervalMs = 3000) { let timedOut = false; const txPollTimeout = setTimeout(() => { timedOut = true; }, timeoutMs); const pollForTx = async (txId) => { if (timedOut) { throw new TimeoutError(`Transaction with ID ${txId} was submitted but was not yet found on the chain. You might want to check later. There was a wait of ${timeoutMs / 1000} seconds.`, txId); } await (0, utils_1.sleep)(pollIntervalMs); const result = await this.getTx(txId); return result ? { code: result.code, height: result.height, txIndex: result.txIndex, events: result.events, rawLog: result.rawLog, transactionHash: txId, msgResponses: result.msgResponses, gasUsed: result.gasUsed, gasWanted: result.gasWanted, } : pollForTx(txId); }; const transactionId = await this.broadcastTxSync(tx); return new Promise((resolve, reject) => pollForTx(transactionId).then((value) => { clearTimeout(txPollTimeout); resolve(value); }, (error) => { clearTimeout(txPollTimeout); reject(error); })); } /** * Broadcasts a signed transaction to the network without monitoring it. * * If broadcasting is rejected by the node for some reason (e.g. because of a CheckTx failure), * an error is thrown. * * If the transaction is broadcasted, a `string` containing the hash of the transaction is returned. The caller then * usually needs to check if the transaction was included in a block and was successful. * * @returns Returns the hash of the transaction */ async broadcastTxSync(tx) { const broadcasted = await this.forceGetTmClient().broadcastTxSync({ tx }); if (broadcasted.code) { return Promise.reject(new BroadcastTxError(broadcasted.code, broadcasted.codespace ?? "", broadcasted.log)); } const transactionId = (0, encoding_1.toHex)(broadcasted.hash).toUpperCase(); return transactionId; } async txsQuery(query) { const results = await this.forceGetTmClient().txSearchAll({ query: query }); return results.txs.map((tx) => { const txMsgData = abci_1.TxMsgData.decode(tx.result.data ?? new Uint8Array()); return { height: tx.height, txIndex: tx.index, hash: (0, encoding_1.toHex)(tx.hash).toUpperCase(), code: tx.result.code, events: tx.result.events.map(events_1.fromTendermintEvent), rawLog: tx.result.log || "", tx: tx.tx, msgResponses: txMsgData.msgResponses, gasUsed: tx.result.gasUsed, gasWanted: tx.result.gasWanted, }; }); } } exports.StargateClient = StargateClient; //# sourceMappingURL=stargateclient.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MyronKoch-dev/osmosis-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server