RPC Clients
This document provides a reference for using RPC (Remote Procedure Call) clients in Telescope-generated code to interact with Cosmos SDK blockchains.
Overview
Telescope supports generating TypeScript clients that can interact with Cosmos SDK chains through different RPC interfaces:
- Tendermint RPC - Direct access to Tendermint endpoints
- gRPC-web Client - Browser-compatible gRPC client
- gRPC-gateway Client - REST-based access to gRPC services
Configuration
To enable RPC client generation in Telescope, use the following configuration:
import { TelescopeOptions } from "@cosmology/types";
const options: TelescopeOptions = {
rpcClients: {
enabled: true,
camelCase: true // Optional: convert method names to camelCase
}
};
Configuration Parameters
Parameter | Type | Default | Description |
---|---|---|---|
rpcClients.enabled | boolean | false | Enables RPC client generation |
rpcClients.camelCase | boolean | true | Converts RPC method names to camelCase |
rpcClients.bundle | boolean | true | Include RPC clients in bundle |
rpcClients.methodName | string | "createRPCQueryClient" | Factory method name |
Tendermint Client
Basic Usage
import { createRPCQueryClient } from "./codegen/rpc";
// Create a client
const client = await createRPCQueryClient({
rpcEndpoint: "https://rpc.cosmos.network"
});
// Query blockchain data
const { validators } = await client.cosmos.staking.v1beta1.validators({});
console.log(`Found ${validators.length} validators`);
Tendermint-specific Endpoints
// Get node information
const nodeInfo = await client.cosmos.base.tendermint.v1beta1.getNodeInfo({});
console.log("Chain ID:", nodeInfo.defaultNodeInfo.network);
// Get latest block
const latestBlock = await client.cosmos.base.tendermint.v1beta1.getLatestBlock({});
console.log("Latest block height:", latestBlock.block.header.height);
// Get syncing status
const syncingStatus = await client.cosmos.base.tendermint.v1beta1.getSyncing({});
console.log("Node is syncing:", syncingStatus.syncing);
gRPC-web Client
For browser applications, use the gRPC-web client:
import { createRPCQueryClient } from "./codegen/rpc";
// Create a gRPC-web client
const client = await createRPCQueryClient({
rpcEndpoint: "https://grpc-web.cosmos.network"
});
// Use the same interface as Tendermint client
const { balance } = await client.cosmos.bank.v1beta1.balance({
address: "cosmos1...",
denom: "uatom"
});
gRPC-gateway Client
For REST-based access to gRPC services:
import { createRPCQueryClient } from "./codegen/rpc";
// Create a client using gRPC-gateway endpoint
const client = await createRPCQueryClient({
rpcEndpoint: "https://rest.cosmos.network"
});
// Query as normal
const { supply } = await client.cosmos.bank.v1beta1.totalSupply({});
Client Types
Specify the client type explicitly:
import { createRPCQueryClient } from "./codegen/rpc";
// Choose the client implementation
const client = await createRPCQueryClient({
rpcEndpoint: "https://rpc.cosmos.network",
clientType: "tendermint" // or "grpc-web" or "grpc-gateway"
});
Module Organization
RPC clients are organized by modules, versions, and methods:
// Bank module
const bank = client.cosmos.bank.v1beta1;
const balances = await bank.allBalances({ address: "cosmos1..." });
// Staking module
const staking = client.cosmos.staking.v1beta1;
const validators = await staking.validators({});
// Governance module
const gov = client.cosmos.gov.v1beta1;
const proposals = await gov.proposals({});
Pagination
Many RPC methods support pagination for handling large result sets:
// First page of validators
const { validators, pagination } = await client.cosmos.staking.v1beta1.validators({
pagination: {
limit: "10",
offset: "0",
countTotal: true
}
});
console.log(`Page 1: ${validators.length} validators`);
console.log(`Total count: ${pagination.total}`);
// Next page
const nextPage = await client.cosmos.staking.v1beta1.validators({
pagination: {
limit: "10",
offset: "10",
countTotal: false
}
});
console.log(`Page 2: ${nextPage.validators.length} validators`);
Key-based Pagination
For larger datasets, use key-based pagination:
let nextKey = null;
let allValidators = [];
do {
// Query with nextKey if available
const paginationParams = nextKey
? { key: nextKey, limit: "100" }
: { limit: "100" };
const response = await client.cosmos.staking.v1beta1.validators({
pagination: paginationParams
});
// Add results to collection
allValidators = [...allValidators, ...response.validators];
// Get the next pagination key
nextKey = response.pagination?.nextKey;
} while (nextKey && nextKey.length > 0);
console.log(`Retrieved ${allValidators.length} validators in total`);
Error Handling
try {
const { balance } = await client.cosmos.bank.v1beta1.balance({
address: "cosmos1...",
denom: "uatom"
});
console.log("Balance:", balance);
} catch (error) {
if (error.code === 3) { // NOT_FOUND in gRPC
console.error("Address not found");
} else if (error.code === 4) { // DEADLINE_EXCEEDED
console.error("Request timed out");
} else if (error.code === 13) { // INTERNAL
console.error("Internal server error");
} else {
console.error("Error:", error.message);
}
}
Custom Endpoints
For custom or extended RPC endpoints:
import { QueryClient } from "@cosmjs/stargate";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
import { setupCustomExtension } from "./my-extension";
// Create a Tendermint client
const tendermintClient = await Tendermint34Client.connect("https://rpc.cosmos.network");
// Create a QueryClient with custom extension
const queryClient = QueryClient.withExtensions(
tendermintClient,
setupCustomExtension
);
// Use the custom extension
const customResult = await queryClient.custom.myCustomQuery(params);
RPC Client Comparison
Feature | Tendermint RPC | gRPC-web | gRPC-gateway |
---|---|---|---|
Browser Support | Limited | Yes | Yes |
Efficiency | High | Medium | Low |
Streaming | Yes | Limited | No |
Compatibility | Node.js | All | All |
Communication | Direct | HTTP/1.1 + Protobuf | HTTP/REST |
Tendermint RPC Specific Methods
// Create a raw Tendermint client
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
const tmClient = await Tendermint34Client.connect("https://rpc.cosmos.network");
// Get blockchain information
const abciInfo = await tmClient.abciInfo();
console.log("ABCI application version:", abciInfo.applicationVersion);
// Get a block by height
const block = await tmClient.block(123456);
console.log("Block time:", block.block.header.time);
// Get validator set at a height
const validators = await tmClient.validatorsAll(123456);
console.log("Validator count:", validators.length);
// Subscribe to new blocks
const subscription = tmClient.subscribeNewBlock().subscribe({
next: (event) => {
console.log("New block:", event.header.height);
},
error: (error) => {
console.error("Subscription error:", error);
},
complete: () => {
console.log("Subscription completed");
}
});
// Later, unsubscribe
subscription.unsubscribe();
WebSocket Subscriptions
// Subscribe to transaction events
const subscription = tmClient.subscribeTx().subscribe({
next: (event) => {
console.log("Transaction hash:", event.hash);
console.log("Transaction result:", event.result);
},
error: (error) => {
console.error("Subscription error:", error);
}
});
// Subscribe to specific events
const subscription = tmClient.subscribeNewBlockHeader().subscribe({
next: (header) => {
console.log("New block height:", header.height);
console.log("Block time:", header.time);
console.log("Proposer:", header.proposerAddress);
}
});
Request Batching
For efficient network usage, batch related requests:
import { createRPCQueryClient } from "./codegen/rpc";
async function getBatchedData(addresses) {
const client = await createRPCQueryClient({
rpcEndpoint: "https://rpc.cosmos.network"
});
// Execute multiple queries in parallel
const balancePromises = addresses.map(address =>
client.cosmos.bank.v1beta1.allBalances({ address })
);
// Wait for all queries to complete
const balanceResults = await Promise.all(balancePromises);
// Process results
return addresses.map((address, index) => ({
address,
balances: balanceResults[index].balances
}));
}
// Example usage
const addresses = [
"cosmos1address1...",
"cosmos1address2...",
"cosmos1address3..."
];
const results = await getBatchedData(addresses);
console.log(results);
Connection Management
import { createRPCQueryClient } from "./codegen/rpc";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
// Create a Tendermint client directly
const tmClient = await Tendermint34Client.connect("https://rpc.cosmos.network");
// Create RPC client using existing Tendermint client
const client = await createRPCQueryClient({
rpcEndpoint: tmClient
});
// Use the client
const chainId = await client.cosmos.base.tendermint.v1beta1.getNodeInfo({});
// When done, disconnect
tmClient.disconnect();
Performance Considerations
- Connection Reuse: Create a single client and reuse it
- Pagination: Use appropriate page sizes
- Batching: Batch related requests with Promise.all
- Disconnection: Always disconnect when done
- Error Handling: Implement proper error handling and retries
- Timeouts: Set appropriate timeouts for RPC calls
Best Practices
- Client Lifecycle: Manage client connections properly
- Error Handling: Implement proper error handling with retries
- Batching: Batch related requests for efficiency
- Pagination: Use pagination for large datasets
- Disconnection: Always disconnect clients when done
- Type Safety: Leverage TypeScript typing for better developer experience
- Authentication: Include authentication if required by the endpoint