⚠️ Important: Please upgrade to@hyperweb/telescope
to leverage new features like generating MCP servers and agents for AI-powered blockchain interactions!
Telescope is a TypeScript Transpiler for Cosmos Protobufs. It generates strongly-typed TypeScript libraries for Cosmos blockchains, providing developers with type-safe interfaces to build applications. Think of Telescope as "Babel for the Cosmos" - simply point to your protobuf files and generate fully-typed libraries for building dApps.
The following blockchain libraries (generated by Telescope) are available via npm:
🎥 Check out our video course to learn how to use telescope
!
- Telescope 🔭
- Usage
- Programmatic Usage
- Options
- General Options
- Amino Encoding
- Implemented Interface Options
- Prototypes Options
- Prototypes Methods
- Enums Options
- LCD Client Options
- Aggregated LCD
- RPC Client Options
- Helper Functions
- Stargate Client Options
- State Management
- Typings and Formatting
- Protobuf parser
- Typescript Disabling
- ESLint Disabling
- Bundle
- MCP Server
- Output
- Types
- Composing Messages
- Calculating Fees
- Stargate Clients
- Creating Signers
- Broadcasting Messages
- LCD Clients
- LCD Clients Classes
- RPC Clients
- RPC Client Classes
- Instant RPC Methods
- Manually Registering Types
- JSON Patch Protos
- CosmWasm
- Helper Functions Configuration
- Dependencies
- Troubleshooting
- Developing
- Sponsors
- Stack
- Credits
- Disclaimer
Follow the instructions below to generate a new TypeScript package that you can publish to npm. You can also follow the video tutorial: https://youtu.be/iQf6p65fbdY
create-interchain-app
npm install -g create-interchain-app
Use the create-interchain-app
command to create a new package from the telescope
boilerplate.
cia --boilerplate telescope
Next, navigate to the ./your-project/packages/telescope
directory to continue.
If any required options are missing, the CLI will prompt you for the necessary information.
For detailed CLI generate
commands, please refer to our documentation.
You can download proto files using the download-protos
command provided in the boilerplate:
yarn download-protos
After running this command, you'll see:
- Cloned repositories in
./git-modules
- Proto files in
./protos
These proto files are downloaded based on your configuration. For detailed CLI download
commands, please refer to our documentation.
To generate TypeScript files for your chain, run the yarn codegen
command:
yarn codegen
Finally, build your package to generate JavaScript files and TypeScript definitions:
yarn install
yarn build
Your generated code is now in the ./src
folder, ready for publishing. If you used the create-interchain-app
boilerplate, use lerna
to publish (refer to the boilerplate's README for detailed instructions). Alternatively, run npm publish
from your repository.
The following sections describe various methods to install and use Telescope.
Install Telescope globally:
npm install -g @hyperweb/telescope
The workflow consists of three steps: generate, download, and transpile.
Use the interactive prompt:
telescope generate
Or use command-line options for non-interactive setup:
telescope generate --access public --userfullname "Your Name" --useremail "[email protected]" --module-desc "Your module description" --username "your-username" --license MIT --module-name "your-module" --chain-name cosmos --use-npm-scoped
Available options:
--userfullname
: Your full name--useremail
: Your email--module-desc
: Module description--username
: GitHub username--module-name
: Module name--chain-name
: Chain name--access
: Package access (public
orprivate
)--use-npm-scoped
: Use npm scoped package (only works with--access public
)--license
: License type
telescope download
This will clone repositories into ./git-modules
and generate proto files in ./protos
.
Download with a config file:
telescope download --config ./protod.config.json --out ./git-modules
protod.config.json example:
{
"repos": [
{ "owner": "cosmos", "repo": "cosmos-sdk", "branch": "release/v0.50.x" },
{ "owner": "cosmos", "repo": "ibc-go" },
],
"protoDirMapping": {
"gogo/protobuf/master": ".",
"googleapis/googleapis/master": ".",
"protocolbuffers/protobuf/main": "src"
},
"outDir": "protos",
"ssh": true,
"tempRepoDir": "git-modules",
"targets": [
"cosmos/**/*.proto",
"cosmwasm/**/*.proto",
"ibc/**/*.proto",
]
}
Download from a specific repository:
telescope download --git-repo owner/repository --targets cosmos/auth/v1beta1/auth.proto
Use default Telescope options:
telescope transpile
Use a custom configuration file:
telescope transpile --config telescope-config.json
Example telescope-config.json
:
{
"protoDirs": [
"./protos/"
],
"outPath": "./codegen/",
"options": {
"classesUseArrowFunctions": true,
"env": "v-next",
"useInterchainJs": true,
"useSDKTypes": false,
"prototypes": {
"enableRegistryLoader": false,
"enableMessageComposer": false,
"enabled": true,
"parser": {
"keepCase": false
},
"methods": {
"fromJSON": false,
"toJSON": false,
"encode": true,
"decode": true,
"fromPartial": true,
"toAmino": true,
"fromAmino": true,
"fromProto": false,
"toProto": false,
"fromProtoMsg": false,
"toProtoMsg": false,
"toAminoMsg": true,
"fromAminoMsg": true
},
"addTypeUrlToDecoders": false,
"addTypeUrlToObjects": true,
"addAminoTypeToObjects": true,
"typingsFormat": {
"duration": "duration",
"timestamp": "date",
"useExact": false,
"useDeepPartial": true,
"num64": "bigint",
"customTypes": {
"useCosmosSDKDec": true,
"useEnhancedDecimal": false
},
"useTelescopeGeneratedType": true,
"autoFixUndefinedEnumDefault": true
}
},
"bundle": {
"enabled": false
},
"stargateClients": {
"enabled": false
},
"lcdClients": {
"enabled": false
},
"rpcClients": {
"enabled": false
},
"helperFunctions": {
"enabled": true,
"useGlobalDecoderRegistry": true,
"hooks": {
"react": true,
"vue": false
}
},
"interfaces": {
"enabled": true,
"useGlobalDecoderRegistry": true,
"registerAllDecodersToGlobal": false,
"useUnionTypes": true
},
"aminoEncoding": {
"enabled": true,
"useLegacyInlineEncoding": false,
"disableMsgTypes": false,
"useProtoOptionality": true,
"customTypes": {
"useCosmosSDKDec": true
}
}
}
}
Please follow the video tutorial: https://youtu.be/iQf6p65fbdY Or see the Quickstart section.
First, install create-cosmos-app
:
npm install -g create-cosmos-app
Use the create-cosmos-app
command to create a new package from the telescope
boilerplate.
cca --boilerplate telescope
Next, navigate to the ./your-project/packages/telescope
directory.
Install dependencies and download the proto files you need:
yarn install
telescope download --config ./your.config.json
Generate TypeScript files for your chain:
yarn codegen
To use Telescope in an existing project, run the following command in your project directory:
yarn add --dev @hyperweb/telescope
Also install the required helpers and CosmJS dependencies listed here.
We recommend using Programmatic Usage for better integration.
You can also use the Telescope CLI. When using CLI commands within your project, prefix them with npx
or yarn
. For example:
yarn telescope generate
npx telescope download
First add telescope to your devDependencies
:
yarn add --dev @hyperweb/telescope
Install the required helpers and CosmJS dependencies listed here.
import downloadProtos from '@hyperweb/telescope/main/commands/download'
const config = {
repos: [
{ owner: "cosmos", repo: "cosmos-sdk", branch: "release/v0.50.x" },
{ owner: "cosmos", repo: "ibc-go" },
],
protoDirMapping: {
"gogo/protobuf/master": ".",
"googleapis/googleapis/master": ".",
"protocolbuffers/protobuf/main": "src"
},
outDir: "protos",
ssh: false,
tempRepoDir: "git-modules",
targets: [
"cosmos/**/*.proto",
"ibc/**/*.proto",
]
};
downloadProtos(config)
.then(() => console.log('✅ Proto download completed'))
// @ts-ignore
.catch((error) => {
console.error('❌ Proto download failed:', error);
process.exit(1);
});
import { join } from 'path';
import telescope from '@hyperweb/telescope';
import { sync as rimraf } from 'rimraf';
const protoDirs = [join(__dirname, '/../proto')];
const outPath = join(__dirname, '../src');
rimraf(outPath);
telescope({
protoDirs,
outPath,
// all options are totally optional ;)
options: {
aminoEncoding: {
enabled: true
},
lcdClients: {
enabled: false
},
rpcClients: {
enabled: false,
camelCase: true
},
// you can scope options to certain packages:
packages: {
nebula: {
prototypes: {
typingsFormat: {
useExact: false
}
}
},
akash: {
stargateClients: {
enabled: true,
includeCosmosDefaultTypes: false
},
prototypes: {
typingsFormat: {
useExact: false
}
}
}
}
}
}).then(() => {
console.log('✨ all done!');
}).catch(e => {
console.error(e);
process.exit(1);
})
option | description | defaults |
---|---|---|
useInterchainJs |
Use InterchainJS for signing clients instead of CosmJS | false |
removeUnusedImports |
Remove unused imports from generated files | true |
classesUseArrowFunctions |
Use arrow functions for class methods | false |
useSDKTypes |
Use Cosmos SDK types (like Dec) instead of native types | true |
includeExternalHelpers |
Include external helper functions | false |
restoreImportExtension |
Restore extensions of imported paths (e.g., '.js'). null means no extension | null |
readme.enabled |
Generate README files | false |
logLevel |
Logging level (0=None, 1=Info, 2=Warn, 3=Error, 4=Debug) | 0 |
option | description | defaults |
---|---|---|
aminoEncoding.enabled |
Generate amino types and amino converters | true |
aminoEncoding.omitEmptyTags |
Array of strings that determines whether a field should be omitted when serialized to JSON. If the array includes "omitempty", any field with the "omitempty" option in either gogoproto.jsontag or cosmos_proto.json_tag will be omitted. If the array includes "dont_omitempty", the field will be omitted or not based on the value of "(amino.dont_omitempty)": if it's null or false, the field will be omitted; if it's true, the field will not be omitted. | ["omitempty", "dont_omitempty"] |
aminoEncoding.useProtoOptionality |
Use proto optionality for determining required fields | false |
aminoEncoding.disableMsgTypes |
Disable generating AminoMsg types | false |
aminoEncoding.casingFn |
Set the amino-casing function for a project | snake() |
aminoEncoding.exceptions |
Set specific aminoType name exceptions | see code |
aminoEncoding.typeUrlToAmino |
Create functions for aminoType name exceptions | undefined |
aminoEncoding.useLegacyInlineEncoding |
@deprecated. Use legacy inline encoding instead of v2 recursive encoding | false |
aminoEncoding.useRecursiveV2encoding |
This option has been removed. See useLegacyInlineEncoding instead. |
|
aminoEncoding.legacy.useNullHandling |
Handle null cases when generating legacy amino converters (those in tx.amino.ts) | |
aminoEncoding.legacy.useOmitEmpty |
Handle omit empty behavior when generating legacy amino converters (those in tx.amino.ts) |
option | description | defaults |
---|---|---|
interfaces.enabled |
Enable converters between Any type and specific implemented interfaces | true |
interfaces.useGlobalDecoderRegistry |
Enable GlobalDecoderRegistry and related functions. Highly recommended when dealing with fields with 'accepted_interface' option. See 'packages/telescope/tests/impl-interfaces.test.ts' for usage. | false |
interfaces.registerAllDecodersToGlobal |
Automatically register all decoders to the global registry | true |
interfaces.useUseInterfacesParams |
Add useInterfaces argument to decode and toAmino functions |
false |
interfaces.useByDefault |
Use interface decoders by default (default for useInterfaces argument to decode and toAmino functions) |
true |
interfaces.useByDefaultRpc |
Use interface decoders by default in RPC clients | true |
interfaces.useUnionTypes |
Generate Any type as union types (TextProposal | RegisterIncentiveProposal) instead of intersection types (TextProposal & RegisterIncentiveProposal) | false |
option | description | defaults |
---|---|---|
prototypes.enabled |
Enable generation of proto encoding methods | true |
prototypes.includePackageVar |
Export a protoPackage variable to indicate package name |
false |
prototypes.includes.packages |
Include a set of packages during transpilation (if a package meets both include and exclude, it will be excluded) | undefined |
prototypes.includes.protos |
Include a set of proto files during transpilation (if a proto meets both include and exclude, it will be excluded) | undefined |
prototypes.excluded.packages |
Exclude a set of packages from transpilation | undefined |
prototypes.excluded.protos |
Try to exclude a set of proto files from transpilation. If files in the list are dependencies to other files, they will still be transpiled. | undefined |
prototypes.excluded.hardProtos |
Exclude a set of proto files from transpilation. Files in this list will be excluded regardless of dependencies. | undefined |
prototypes.fieldDefaultIsOptional |
Set default optionality of fields | false |
prototypes.useOptionalNullable |
Use (gogoproto.nullable) values in determining optionality |
true |
prototypes.allowUndefinedTypes |
Allow Type s to be undefined |
false |
prototypes.allowEncodeDefaultScalars |
Allow encoders to encode default values of scalar types (e.g., empty string, 0, or false) | false |
prototypes.isScalarDefaultToNullable |
Determine whether scalar types are nullable by default when gogoproto.nullable is not specified. If true, scalar types will be nullable; if false, they will be required | false |
prototypes.enforceNullCheck |
Enforce checking that required scalar fields are not null or undefined during encoding | false |
prototypes.optionalQueryParams |
Make queryParams optional | false |
prototypes.optionalPageRequests |
Make PageRequest fields optional |
false |
prototypes.addTypeUrlToDecoders |
Add $typeUrl field to generated interfaces | true |
prototypes.addAminoTypeToObjects |
Add aminoType field to generated Decoders | false |
prototypes.addTypeUrlToObjects |
Add typeUrl field to generated Decoders | true |
prototypes.strictNullCheckForPrototypeMethods |
Enable strict null checks for prototype methods | false |
prototypes.paginationDefaultFromPartial |
Set default values for pagination in fromPartial methods | false |
prototypes.enableRegistryLoader |
Generate Registry loader in *.registry.ts files | true |
prototypes.enableMessageComposer |
Generate MessageComposer in *.registry.ts files | true |
prototypes.patch |
Object mapping filenames to an array of Operation to be applied as patches to proto files during generation. See JSON Patch Protos |
undefined |
option | description | defaults |
---|---|---|
prototypes.methods.encode |
Enable encode method on proto objects |
true |
prototypes.methods.decode |
Enable decode method on proto objects |
true |
prototypes.methods.fromJSON |
Enable fromJSON method on proto objects |
true |
prototypes.methods.toJSON |
Enable toJSON method on proto objects |
true |
prototypes.methods.fromPartial |
Enable fromPartial method on proto objects |
true |
prototypes.methods.fromSDK |
Enable fromSDK method on proto objects |
false |
prototypes.methods.toSDK |
Enable toSDK method on proto objects |
false |
option | description | defaults |
---|---|---|
enums.useCustomNames |
Enable usage of custom names for enums if specified through proto options or annotations, allowing for more descriptive or project-specific naming conventions | false |
option | description | defaults |
---|---|---|
lcdClients.enabled |
Generate LCD clients that can query proto Query messages |
true |
lcdClients.bundle |
Generate factory bundle aggregate of all LCD Clients | true |
lcdClients.scoped |
Generate factory of scoped LCD Clients | undefined |
lcdClients.scopedIsExclusive |
Allow both scoped bundles and all RPC Clients | true |
See LCD Clients for more info.
option | description | defaults |
---|---|---|
aggregatedLCD.dir |
Directory to place aggregated LCD client file | undefined |
aggregatedLCD.filename |
Filename for aggregated LCD client | undefined |
aggregatedLCD.packages |
Packages to include in aggregated LCD client | [] |
aggregatedLCD.protos |
Proto files to include in aggregated LCD client | [] |
aggregatedLCD.addToBundle |
Add aggregated LCD client to bundle | false |
option | description | defaults |
---|---|---|
rpcClients.type |
Generate this type of RPC client (tendermint , gRPC-web , gRPC ) |
tendermint |
rpcClients.enabled |
Generate RPC clients that can interact with proto messages | true |
rpcClients.bundle |
Generate factory bundle aggregate of all RPC Clients | true |
rpcClients.inline |
Inline all RPC client methods into a single file | false |
rpcClients.extensions |
Enable extensions for RPC clients | true |
rpcClients.camelCase |
Use camel-case for RPC methods when generating RPC clients | true |
rpcClients.scoped |
Generate factory of scoped RPC Clients | undefined |
rpcClients.scopedIsExclusive |
Allow both scoped bundles and all RPC Clients | true |
rpcClients.enabledServices |
Which services to enable | [Msg ,Query ,Service ] |
rpcClients.instantOps |
Generate instant RPC operations in the file service-ops.ts under root folder, which contains customized classes having selected RPC methods |
undefined |
rpcClients.useConnectComet |
Use connectComet function to get a tendermint client | undefined |
rpcClients.useMakeClient |
Allow user to pass a query client resolver to create query client in createRPCQueryClient function | undefined |
rpcClients.serviceImplement |
Assign implement type of RPC methods, Query or Tx , by setting patterns under service types |
undefined |
rpcClients.clientStyle.useUpdatedClientStyle |
The default value is false , which sets the generated client to use the legacy style. Setting it to true applies the updated style and activates the remaining options in clientStyle. |
false |
rpcClients.clientStyle.type |
A string array containing possible values: all-client , sdk-module-client , and custom-client . The value all-client generates an all-module-client file. The value sdk-module-client generates a client for the module specified by the sdkModuleClientOption . The value custom-client generates a customized client as specified by customClientOption |
undefined |
rpcClients.clientStyle.customClientOption.name |
Assign the client name like {name}AminoConverters , get{name}SigningClient etc |
undefined |
rpcClients.clientStyle.customClientOption.fileName |
Assign the file name of generated client in root directory | undefined |
rpcClients.clientStyle.customClientOption.include.patterns |
Determine which proto files will be imported for the current client such as cosmos.gov.v1beta1.** |
undefined |
See RPC Clients for more info.
Option | Description | Defaults |
---|---|---|
helperFunctions.enabled |
Enable the generation of helper function files .func.ts |
false |
helperFunctions.useGlobalDecoderRegistry |
Use global decoder registry in helper functions | false |
helperFunctions.hooks |
Generates hooks selected alongside helper functions | { react: false, vue: false } |
helperFunctions.include.serviceTypes |
Specifies which types of services to include (Query , Msg ). undefined includes all types. |
undefined |
helperFunctions.include.patterns |
Array of glob patterns patterns (e.g., "**" , "cosmos.bank.v1beta1.bala*" , etc.) to match specific proto services. |
undefined |
helperFunctions.nameMappers |
Configuration object for customizing function names and prefixes | {} |
helperFunctions.nameMappers.All.funcBody |
Maps method names to a new name for all services. | "unchanged" |
helperFunctions.nameMappers.All.hookPrefix |
Prefix for the hooks. | "use" |
helperFunctions.nameMappers.Query.funcBody |
Maps method names to a new name for Query services. |
"get" |
helperFunctions.nameMappers.Query.hookPrefix |
Prefix for the hooks for Query services. |
"use" |
helperFunctions.nameMappers.Msg.funcBody |
Maps method names to a new name for Msg services. |
"unchanged" |
helperFunctions.nameMappers.Msg.hookPrefix |
Prefix for the hooks for Msg services. |
"use" |
See Helper Functions Configuration for more info.
option | description | defaults |
---|---|---|
stargateClients.includeCosmosDefaultTypes |
Include the CosmJS defaults with stargate clients | true (except cosmos package) |
stargateClients.addGetTxRpc |
Add getSigningTxRpc to clients in namespaces | false |
option | description | defaults |
---|---|---|
reactQuery.enabled |
Create React hooks that use @tanstack/react-query hooks |
false |
reactQuery.needExtraQueryKey |
Allow users to input extra React Query key to customized hooks (e.g., ['rpcEndpoint', 'yourExtraKey']) | false |
reactQuery.include.protos |
Create hooks on matched proto filenames or patterns using minimatch | [] |
reactQuery.include.packages |
Create hooks on matched packages files using minimatch | [] |
reactQuery.include.patterns |
Create hooks on matched patterns of files using minimatch (deprecated in favor of packages and protos) | [] |
reactQuery.instantExport.include.patterns |
Expose instant hooks on matched patterns of packages + method (e.g., cosmos.bank.v1beta1.useBalance) using minimatch. If there are duplicated method names in multiple packages without setting reactQuery.instantExport.nameMapping , one duplicated name will be created like: useCosmosBankV1beta1Balance |
[] |
reactQuery.instantExport.nameMapping |
Map an alias to a package + method for better naming of duplicated method names (e.g., useBankBalance: cosmos.bank.v1beta1.useBalance). Customized hook name is set in front of pkg+method to prevent duplicate aliases. | {} |
option | description | defaults |
---|---|---|
mobx.enabled |
Create MobX stores that use mobx |
false |
mobx.include.protos |
Create MobX stores on matched proto filenames or patterns using minimatch | [] |
mobx.include.packages |
Create MobX stores on matched packages files using minimatch | [] |
mobx.include.patterns |
Create MobX stores on matched patterns of proto files using minimatch (deprecated in favor of packages and protos) | [] |
option | description | defaults |
---|---|---|
pinia.enabled |
Create Pinia stores that use pinia |
false |
pinia.include.protos |
Create Pinia stores on matched proto filenames or patterns using minimatch | [] |
pinia.include.packages |
Create Pinia stores on matched packages files using minimatch | [] |
pinia.include.patterns |
Create Pinia stores on matched patterns of proto files using minimatch (deprecated in favor of packages and protos) | [] |
option | description | defaults |
---|---|---|
vueQuery.enabled |
Create Vue composables that use @tanstack/vue-query composables |
false |
vueQuery.include.protos |
Create composables on matched proto filenames or patterns using minimatch | [] |
vueQuery.include.packages |
Create composables on matched packages files using minimatch | [] |
option | description | defaults |
---|---|---|
prototypes.typingsFormat.customTypes.useCosmosSDKDec |
Enable handling of "cosmos.Dec" proto custom type. Used to show decimal fields with the custom type correctly. Highly recommended to set to true. | true |
prototypes.typingsFormat.customTypes.useEnhancedDecimal |
Use patched decimal instead of decimal from @cosmjs/math | false |
prototypes.typingsFormat.customTypes.base64Lib |
Use endo/base64 methods | undefined |
prototypes.typingsFormat.num64 |
'long' or 'bigint', the way of generating int64 proto types. Set to 'bigint' to use more stable built-in type | bigint |
prototypes.typingsFormat.useTelescopeGeneratedType |
Discard GeneratedType from CosmJS, use TelescopeGeneratedType instead inside *.registry.ts files | false |
prototypes.typingsFormat.useDeepPartial |
Use DeepPartial type instead of Partial TS type |
false |
prototypes.typingsFormat.useExact |
Use the Exact TS type |
false |
prototypes.typingsFormat.toJsonUnknown |
Use any for toJSON methods instead of JsonSafe |
true |
prototypes.typingsFormat.timestamp |
Use either date or timestamp for Timestamp proto type |
"date" |
prototypes.typingsFormat.duration |
Use either duration or string for Duration proto type |
"duration" |
prototypes.typingsFormat.updatedDuration |
temporary field to avoid breaking changes | false |
prototypes.typingsFormat.setDefaultEnumToUnrecognized |
false: enum empty value would be 0, true: -1 (value for enum unrecognized) | true |
prototypes.typingsFormat.setDefaultCustomTypesToUndefined |
true: Timestamp, Duration, Any, Coin empty value would be undefined. false: use fromPartial to get an empty object | false |
prototypes.typingsFormat.autoFixUndefinedEnumDefault |
The default value of an enum field would be: 1 (proto2); 0 (proto3). But in some rare cases, those default values don't exist. By enabling this, the default value will be automatically fixed with the smallest value inside the enum. | false |
option | description | defaults |
---|---|---|
prototypes.parser.keepCase |
Pass keepCase to protobuf parse() to keep original casing |
true |
prototypes.parser.alternateCommentMode |
Pass alternateCommentMode to protobuf parse() method |
true |
prototypes.parser.preferTrailingComment |
Pass preferTrailingComment to protobuf parse() method |
false |
option | description | defaults |
---|---|---|
tsDisable.disableAll |
Include //@ts-nocheck on every output file |
false |
tsDisable.patterns |
Include //@ts-nocheck on matched patterns |
[] |
tsDisable.files |
Include //@ts-nocheck on matched files |
[] |
option | description | defaults |
---|---|---|
eslintDisable.disableAll |
Include /* eslint-disable */ on every output file |
false |
eslintDisable.patterns |
Include /* eslint-disable */ on matched patterns |
[] |
eslintDisable.files |
Include /* eslint-disable */ on matched files |
[] |
option | description | defaults |
---|---|---|
bundle.enabled |
Bundle all files into a scoped index file | true |
bundle.type |
Bundle type: "namespace" or "module" | "namespace" |
Warning: This option is not recommended. It will generate a bundle file that exports all the types and functions under one namespace. This will make the bundle file very large and hard to maintain. e.g. using
cosmos.bank.v1beta1.MsgSend
might be intuitive, but it will also includecosmos.gov.v1beta1.*
and other types in the final bundle file. So use this option with caution.
option | description | defaults |
---|---|---|
mcpServer.enabled |
generate MCP (Model Context Protocol) servers alongside TypeScript clients for AI agent integration | false |
When enabled, Telescope generates an MCP server package ({packageName}-mcp
) that includes:
- Function generator tool for creating custom blockchain functions
- Telescope examples as reference patterns for implementation
- AI prompts for usage guidelines and best practices
See MCP Integration for detailed documentation.
option | description | defaults |
---|---|---|
env |
'default' or 'v-next', set to 'v-next' to enable yet-to-be-released features | default |
removeUnusedImports |
Remove unused imports | true |
classesUseArrowFunctions |
Classes use arrow functions instead of bind() ing in constructors |
false |
includeExternalHelpers |
Export helper functions in extern.ts |
false |
restoreImportExtension |
Restore extensions of imported paths (e.g., '.js'). null means no extension | null |
The representation of google.protobuf.Timestamp
is configurable by the prototypes.typingsFormat.timestamp
option.
Protobuf type | Default/date='date' |
date='timestamp' |
---|---|---|
google.protobuf.Timestamp |
Date |
{ seconds: Long, nanos: number } |
TODO
- add
date='string'
option
The representation of google.protobuf.Duration
is configurable by the prototypes.typingsFormat.duration
option.
Protobuf type | Default/duration='duration' |
duration='string' |
|
---|---|---|---|
google.protobuf.Duration |
{ seconds: Long, nanos: number } |
string |
This example shows messages from the osmojs
, which was built with Telescope.
Import the osmosis
object from osmojs
. In this case, we're showing the messages available from the osmosis.gamm.v1beta1
module:
import { osmosis } from 'osmojs';
const {
joinPool,
exitPool,
exitSwapExternAmountOut,
exitSwapShareAmountIn,
joinSwapExternAmountIn,
joinSwapShareAmountOut,
swapExactAmountIn,
swapExactAmountOut
} = osmosis.gamm.v1beta1.MessageComposer.withTypeUrl;
Now you can construct messages. If you use VS Code or another TypeScript-enabled IDE, you should also be able to use Ctrl+Space
to see auto-completion of the fields required for the message.
import { coin } from '@cosmjs/amino';
const msg = swapExactAmountIn({
sender,
routes,
tokenIn: coin(amount, denom),
tokenOutMinAmount
});
Make sure to create a fee
object in addition to your message.
import { coins } from '@cosmjs/amino';
const fee = {
amount: coins(0, 'uosmo'),
gas: '250000'
}
If you are broadcasting multiple messages in a batch, you should simulate
your transaction and estimate the fee
import { Dec, IntPretty } from '@keplr-wallet/unit';
const gasEstimated = await stargateClient.simulate(address, msgs, memo);
const fee = {
amount: coins(0, 'uosmo'),
gas: new IntPretty(new Dec(gasEstimated).mul(new Dec(1.3)))
.maxDecimals(0)
.locale(false)
.toString()
};
Every module gets their own signing client. This example demonstrates for the osmosis
module.
Use getSigningOsmosisClient
to get your SigningStargateClient
, with the Osmosis proto/amino messages full-loaded. No need to manually add amino types, just require and initialize the client:
import { getSigningOsmosisClient } from 'osmojs';
const client = await getSigningOsmosisClient({
rpcEndpoint,
signer // OfflineSigner
});
To broadcast messages, you'll want to use either Keplr or an OfflineSigner
from cosmjs
using mnemonics.
Likely you'll want to use the Amino, so unless you need proto, you should use this one:
import { getOfflineSigner as getOfflineSignerAmino } from 'cosmjs-utils';
import { getOfflineSigner as getOfflineSignerProto } from 'cosmjs-utils';
WARNING: NOT RECOMMENDED TO USE PLAIN-TEXT MNEMONICS. Please take care of your security and use best practices such as AES encryption and/or methods from 12-factor applications.
import { chains } from 'chain-registry';
const mnemonic =
'unfold client turtle either pilot stock floor glow toward bullet car science';
const chain = chains.find(({ chain_name }) => chain_name === 'osmosis');
const signer = await getOfflineSigner({
mnemonic,
chain
});
Now that you have your client
, you can broadcast messages:
import { signAndBroadcast } from '@osmosnauts/helpers';
const res = await signAndBroadcast({
client, // SigningStargateClient
chainId: 'osmosis-1', // use 'osmo-test-4' for testnet
address,
msgs: [msg],
fee,
memo: ''
});
For querying data via REST endpoints, you can use LCD Clients. For a better developer experience, you can generate a factory of scoped bundles of all LCD Clients with the lcdClients
option.
const options: TelescopeOptions = {
lcdClients: {
enabled: true,
},
};
If you use the lcdClients.scoped
array, you can scope to only the modules of your interest.
const options: TelescopeOptions = {
lcdClients: {
enabled: true,
scoped: [
{
dir: 'osmosis',
filename: 'custom-lcd-client.ts',
packages: [
'cosmos.bank.v1beta1',
'cosmos.gov.v1beta1',
'osmosis.gamm.v1beta1'
],
addToBundle: true,
methodName: 'createCustomLCDClient'
},
{
dir: 'evmos',
filename: 'custom-lcd-client.ts',
packages: [
'cosmos.bank.v1beta1',
'cosmos.gov.v1beta1',
'evmos.erc20.v1'
],
addToBundle: true,
methodName: 'createEvmosLCDClient'
}
]
}
};
This will generate a nice helper in the ClientFactory
, which you can then use to query multiple modules from a single object:
import { osmosis } from './codegen';
const main = async () => {
const client = await osmosis.ClientFactory.createLCDClient({ restEndpoint: REST_ENDPOINT });
// now you can query the modules
const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};
If you want to instantiate a single client, for any module that has a Query
type, there will be a LCDQueryClient
object:
import { osmosis } from "osmojs";
export const main = async () => {
const requestClient = new LCDClient({ restEndpoint: REST_ENDPOINT });
const client = new osmosis.gamm.v1beta1.LCDQueryClient({ requestClient });
const pools = await client.pools();
console.log(pools);
};
main().then(() => {
console.log('all done')
})
For querying data via RPC endpoints, you can use RPC Clients. For a better developer experience, you can generate a factory of scoped bundles of all RPC Clients with the rpcClients
option.
const options: TelescopeOptions = {
rpcClients: {
type: 'tendermint',
enabled: true,
camelCase: true
}
};
If you use the rpcClients.scoped
array, you can scope to only the modules of your interest. gRPC-web
and gRPC-gateway
work the same way with this option.
const options: TelescopeOptions = {
rpcClients: {
enabled: true,
camelCase: true,
scoped: [
{
dir: 'osmosis',
filename: 'osmosis-rpc-client.ts',
packages: [
'cosmos.bank.v1beta1',
'cosmos.gov.v1beta1',
'osmosis.gamm.v1beta1'
],
addToBundle: true,
methodNameQuery: 'createRPCQueryClient',
methodNameTx: 'createRPCTxClient'
}
]
}
};
This will generate helpers createRPCQueryClient
and createRPCTxClient
in the ClientFactory
, which you can then use to query multiple modules from a single object:
import { osmosis } from './codegen';
const main = async () => {
const client = await osmosis.ClientFactory.createRPCQueryClient({ rpcEndpoint });
// now you can query the modules
const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};
For querying data via gRPC-web endpoints, you can use gRPC-web Clients. For a better developer experience, you can generate a factory of scoped bundles of all gRPC-web Clients with the rpcClients
option.
const options: TelescopeOptions = {
rpcClients: {
type: 'grpc-web',
enabled: true,
camelCase: true
}
};
This will generate helpers createGrpcWebClient
and createGrpcMsgClient
in the ClientFactory
, which you can then use to query multiple modules from a single object, if you need an example with scaffold and broadcast msg you can refer to the example below in grpc-gateway
:
import { osmosis } from './codegen';
const main = async () => {
const client = await osmosis.ClientFactory.createGrpcWebClient({ endpoint });
// now you can query the modules
const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};
For querying data via gRPC-gateway endpoints, you can use gRPC-gateway Clients. For a better developer experience, you can generate a factory of scoped bundles of all gRPC-gateway Clients with the rpcClients
option.
const options: TelescopeOptions = {
rpcClients: {
type: 'grpc-gateway',
enabled: true,
camelCase: true
}
};
This will generate helpers createGrpcGateWayClient
in the ClientFactory
, which you can then use to query multiple modules from a single object:
import { osmosis } from './codegen';
const main = async () => {
// endpoint here is lcd endpoint
const client = await osmosis.ClientFactory.createGrpcGateWayClient({ endpoint });
// now you can query the modules
const pool = await client.osmosis.gamm.v1beta1.pool({ poolId: "1" });
const balance = await client.cosmos.bank.v1beta1.allBalances({ address: 'osmo1addresshere' });
};
Below is an example of scaffolding a grant
Proto Msg for gRPC-web and gRPC-gateway and then broadcasting it.
const { grant } = cosmos.authz.v1beta1.MessageComposer.withTypeUrl;
const msg = grant({
granter: 'granter_address',
grantee: 'grantee_address',
grant: {
authorization: StakeAuthorization.toProtoMsg({
maxTokens: {
denom: 'uosmo',
amount: '100000000'
},
authorizationType: AuthorizationType.AUTHORIZATION_TYPE_DELEGATE
}),
expiration: new Date(Date.now() + 60 * 60 * 24 * 7)
}})
const signed_tx = await signClient.sign('granter_address', [msg], fee, 'telescope: grant', signerData);
const txRawBytes = Uint8Array.from(TxRaw.encode(signed_tx).finish());
const res = await client.cosmos.tx.v1beta1.broadcastTx({
txBytes: txRawBytes,
mode: BroadcastMode.BROADCAST_MODE_BLOCK
})
console.log(res);
If you want to instantiate a single client, you can generate RPC classes with the rpcClients
option;
For any module that has a Msg
, Query
or Service
type, a
import { osmosis, cosmos } from 'osmojs';
const MsgClient = osmosis.gamm.v1beta1.MsgClientImpl;
const QueryClient = osmosis.gamm.v1beta1.QueryClientImpl;
const ServiceClient = cosmos.base.tendermint.v1beta1.ServiceClientImpl;
Here is an example of making a query if you want to use the RPC client classes manually:
import { osmosis } from "osmojs";
import { createProtobufRpcClient, QueryClient } from "@cosmjs/stargate";
import { Tendermint34Client } from "@cosmjs/tendermint-rpc";
export const main = async () => {
const tmClient = await Tendermint34Client.connect(RPC_ENDPOINT);
const QueryClientImpl = osmosis.gamm.v1beta1.QueryClientImpl;
const client = new QueryClient(tmClient);
const rpc = createProtobufRpcClient(client);
const queryService = new QueryClientImpl(rpc);
const pools = await queryService.pools({})
console.log(pools);
};
main().then(() => {
console.log('all done')
})
Use the instantOps option to expose instant RPC methods.
For example, for this config:
{
instantOps: [
{
className: "OsmosisClaim",
include: {
patterns: ["osmosis.**.*claim*"],
},
},
{
className: "CosmosAuthAccount",
include: {
patterns: [
"cosmos.auth.**.*account*",
"cosmos.auth.**.*Account*",
"cosmos.gov.v1beta1.**",
],
},
nameMapping: {
// name mapping rule for both Msg and Query methods.
// moduleAccounts will be renamed to authModuleAccounts in generated class.
All: {
authModuleAccounts: "cosmos.auth.v1beta1.moduleAccounts",
},
// name mapping rule for Msg methods.
Msg: {
// deposit method under Msg will be renamed to txDeposit in generated class. While deposit method under Query will remain the same.
txDeposit: "cosmos.gov.v1beta1.deposit",
// Same for vote method.
txVote: "cosmos.gov.v1beta1.vote",
},
},
},
],
}
There will be an extra file generated in the root folder called service-ops.ts:
export interface OsmosisClaim extends _OsmosisClaimV1beta1Queryrpc.OsmosisClaim {}
export class OsmosisClaim {
rpc: TxRpc;
init(rpc: TxRpc) {
this.rpc = rpc;
this.claimRecord = _OsmosisClaimV1beta1Queryrpc.createClientImpl(rpc).claimRecord;
this.claimableForAction = _OsmosisClaimV1beta1Queryrpc.createClientImpl(rpc).claimableForAction;
}
}
export interface CosmosAuthAccount extends _CosmosAuthV1beta1Queryrpc.CosmosAuthAccount, _CosmosGovV1beta1Queryrpc.CosmosAuthAccount, _CosmosGovV1beta1Txrpc.CosmosAuthAccount {}
export class CosmosAuthAccount {
rpc: TxRpc;
init(rpc: TxRpc) {
this.rpc = rpc;
this.accounts = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).accounts;
this.account = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).account;
// moduleAccounts has been renamed to authModuleAccounts as the nameMapping in settings.
this.authModuleAccounts = _CosmosAuthV1beta1Queryrpc.createClientImpl(rpc).moduleAccounts;
this.proposal = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).proposal;
this.proposals = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).proposals;
// vote under Query remains the same.
this.vote = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).vote;
this.votes = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).votes;
this.params = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).params;
// deposit under Query remains the same.
this.deposit = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).deposit;
this.deposits = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).deposits;
this.tallyResult = _CosmosGovV1beta1Queryrpc.createClientImpl(rpc).tallyResult;
this.submitProposal = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).submitProposal;
// same as txDeposit for vote here.
this.txVote = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).vote;
this.voteWeighted = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).voteWeighted;
// deposit method under Msg will be renamed to txDeposit in generated class. While deposit method under Query will remain the same.
this.txDeposit = _CosmosGovV1beta1Txrpc.createClientImpl(rpc).deposit;
}
}
Use client style to define the client file generated according to the config
For example, for this config:
clientStyle: {
useUpdatedClientStyle: true,
type: ['all-client', 'sdk-module-client', 'custom-client'],
customClientOption: [
{
name: "custom",
fileName: "custom-client.ts",
include: {
patterns: [
"cosmos.gov.v1beta1*",
"cosmos.gov.v1*",
"ibc.core.channel.*",
],
},
},
],
sdkModuleClientOption: [
'akash',
'osmosis',
'cosmos',
],
},
There will be client files (all-module-client.ts
, akash-sdk-module-client.ts
, osmosis-sdk-module-client.ts
, cosmos-sdk-module-client.ts
, custom-client.ts
) generated in the root directory according to the setting.
The all-module-client.ts
file consolidates all proto imports into one file and exports them as a single client.
All SDK module client files will be identical to the legacy client.ts
files generated in each module directory, except they will be located in the root directory.
The custom client imports proto files based on include.patterns
, allowing protos to originate from different modules.
For example, the custom-client.ts will be like:
export const cosmosIbcAminoConverters = {
...cosmosGovV1TxAmino.AminoConverter,
...cosmosGovV1beta1TxAmino.AminoConverter,
...ibcCoreChannelV1TxAmino.AminoConverter
};
export const cosmosIbcProtoRegistry: ReadonlyArray<[string, GeneratedType]> = [...cosmosGovV1TxRegistry.registry, ...cosmosGovV1beta1TxRegistry.registry, ...ibcCoreChannelV1TxRegistry.registry];
export const getCosmosIbcSigningClientOptions = ({
defaultTypes = defaultRegistryTypes
}: {
...
};
export const getCosmosIbcSigningClient = async ({
rpcEndpoint,
signer,
defaultTypes = defaultRegistryTypes
}: {
rpcEndpoint: string | HttpEndpoint;
signer: OfflineSigner;
defaultTypes?: ReadonlyArray<[string, GeneratedType]>;
}) => {
...
};
This example is with osmosis
module in osmojs
, but it is the same pattern for any module.
NOTE: this is using @cosmjs/[email protected]
import {
AminoTypes,
SigningStargateClient
} from '@cosmjs/stargate';
import { Registry } from '@cosmjs/proto-signing';
import { defaultRegistryTypes } from '@cosmjs/stargate';
import { OfflineSigner } from '@cosmjs/proto-signing'
import { osmosis } from 'osmojs';
export const getCustomSigningClient = async ({ rpcEndpoint, signer }: { rpcEndpoint: string, signer: OfflineSigner }) => {
// registry
const registry = new Registry(defaultRegistryTypes);
// aminotypes
const aminoTypes = new AminoTypes({
...osmosis.gamm.v1beta1.AminoConverter,
...osmosis.lockup.AminoConverter,
...osmosis.superfluid.AminoConverter
});
// load the types
osmosis.gamm.v1beta1.load(registry);
osmosis.lockup.load(registry);
osmosis.superfluid.load(registry);
const client = await SigningStargateClient.connectWithSigner(
rpcEndpoint,
signer,
{ registry, aminoTypes }
);
return client;
};
The prototypes.patch
configuration within the options object allows for dynamic modifications to protobuf definitions during code generation. This feature is designed to apply specific changes to proto files without altering the original source. By using JSON Patch operations such as replace
and add
, developers can customize the generated output to better fit project requirements when upstream SDK PRs are lagging or not in production.
Patches are specified as arrays of Operation
s, where each operation is defined by:
op
: The operation type (add
orreplace
).path
: The JSON path to the target field, optionally prefixed with@
to denote paths derived automatically from the package name, simplifying navigation within the proto file's structure.value
: The new value to be set at the target location specified by the path.
Here is how these patches can be defined within the prototypes configuration:
{
"prototypes": {
"patch": {
"cosmwasm/wasm/v1/types.proto": [
{
"op": "replace",
"path": "@/AccessType/valuesOptions/ACCESS_TYPE_UNSPECIFIED/(gogoproto.enumvalue_customname)",
"value": "UnspecifiedAccess"
},
{
"op": "replace",
"path": "@/AccessType/valuesOptions/ACCESS_TYPE_NOBODY/(gogoproto.enumvalue_customname)",
"value": "NobodyAccess"
},
{
"op": "add",
"path": "@/AccessType/values/ACCESS_TYPE_SUPER_FUN",
"value": 4
},
{
"op": "add",
"path": "@/AccessType/valuesOptions/ACCESS_TYPE_SUPER_FUN",
"value": {
"(gogoproto.enumvalue_customname)": "SuperFunAccessType"
}
}
]
}
}
}
Generate TypeScript SDKs for your CosmWasm smart contracts by using the cosmwasm
option on TelescopeOptions
. The cosmwasm
option is actually a direct reference to the TSBuilderInput
object, for the most up-to-date documentation, visit @cosmwasm/ts-codegen.
import { TSBuilderInput } from '@cosmwasm/ts-codegen';
const options: TelescopeOptions = {
cosmwasm: {
contracts: [
{
name: 'SG721',
dir: './path/to/sg721/schema'
},
{
name: 'Minter',
dir: './path/to/Minter/schema'
}
],
outPath: './path/to/code/src/'
}
};
The nameMappers object supports three service types: All, Query, and Msg. Each pattern within these categories can specify:
{
"pattern": {
funcBody: (ctx: AliasNameMappersContext) => string, // Function to transform the method name
hookPrefix?: string // Prefix for the hook function (default: "use")
}
}
const options: TelescopeOptions = {
helperFunctions: {
enabled: true,
genCustomHooks: {
react: true,
vue: true
},
include: {
patterns: ["cosmos.gov.v1beta1.**", "cosmos.bank.v1beta1.*Send*"],
},
nameMappers: {
All: {
"cosmos.gov.v1beta1.*Vote*": {
funcBody: (ctx) => `helper${ctx.name}`,
hookPrefix: "use",
},
},
Query: {
"cosmos.gov.v1beta1.*Deposits*": {
funcBody: (ctx) => `goOver${ctx.name}`,
},
},
Msg: {
"cosmos.gov.v1beta1.*VoteWeighted*": {
funcBody: (ctx) => `lets${ctx.name}`,
hookPrefix: "useTx",
},
},
},
},
};
- Service-specific patterns (
Query
,Msg
) take precedence overAll
patterns - More specific patterns take precedence over general patterns
- Patterns are case-sensitive
- For a method named
VoteWeighted
: - Default:
createVoteWeighted
anduseVoteWeighted
- With custom mapping:
constructLetsVoteWeighted
anduseTxLetsVoteWeighted
- Patterns support glob-style matching (e.g.,
**
,*
) - Each service type can have its own naming conventions
- Custom prefixes are optional; defaults will be used if not specified
- Function body transformations can be customized using the funcBody property
If you don't use the boilerplate, you will need to manually install
@cosmjs/amino
@cosmjs/proto-signing
@cosmjs/stargate
@cosmjs/tendermint-rpc
yarn add @cosmjs/amino @cosmjs/proto-signing @cosmjs/stargate @cosmjs/tendermint-rpc
If you use the LCD Client generation, you'll need to add
@cosmology/lcd
yarn add @cosmology/lcd
CRA requires that you update Webpack configurations:
Here is an example of a config-overrides.js
:
https://github.com/pyramation/osmosis-ui/blob/main/config-overrides.js
This should not be an issue, but if you experience problems with syntax or are not using preset-env
, you may need these babel plugins:
- babel-plugin-proposal-numeric-separator
- babel-plugin-proposal-optional-chaining
- babel-plugin-proposal-nullish-coalescing-operator
See our documentation for how to contribute and develop Telescope.
Kudos to our sponsors:
- Osmosis funded the creation of Telescope.
A unified toolkit for building applications and smart contracts in the Interchain ecosystem
Category | Tools | Description |
---|---|---|
Chain Information | Chain Registry, Utils, Client | Everything from token symbols, logos, and IBC denominations for all assets you want to support in your application. |
Wallet Connectors | Interchain Kitbeta, Cosmos Kit | Experience the convenience of connecting with a variety of web3 wallets through a single, streamlined interface. |
Signing Clients | InterchainJSbeta, CosmJS | A single, universal signing interface for any network |
SDK Clients | Telescope | Your Frontend Companion for Building with TypeScript with Cosmos SDK Modules. |
Starter Kits | Create Interchain Appbeta | Set up a modern Interchain app by running one command. |
UI Kits | Interchain UI | The Interchain Design System, empowering developers with a flexible, easy-to-use UI kit. |
Testing Frameworks | Starship | Unified Testing and Development for the Interchain. |
TypeScript Smart Contracts | Create Hyperweb App | Build and deploy full-stack blockchain applications with TypeScript |
CosmWasm Contracts | CosmWasm TS Codegen | Convert your CosmWasm smart contracts into dev-friendly TypeScript classes. |
🛠 Built by Hyperweb (formerly Cosmology) — if you like our tools, please checkout and contribute to our github ⚛️
Thanks to these engineers, teams and projects for inspiring Telescope:
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.