# Create Custom CosmJS Interfaces
In this section, you will:
- Create custom CosmJS interfaces to connect to custom Cosmos SDK modules.
- Define custom interfaces with Protobuf.
- Define custom types and messages.
- Integrate with Ignite - previously known as Starport.
CosmJS comes out of the box with interfaces that connect with the standard Cosmos SDK modules such as bank
and gov
and understand the way their messages are serialized. Since your own blockchain's modules are unique, they need custom CosmJS interfaces. That process consists of several steps:
- Creating the Protobuf objects and clients in TypeScript.
- Creating extensions that facilitate the use of the above clients.
- Any further level of abstraction that you deem useful for integration.
This section assumes that you have a working Cosmos blockchain with its own modules. It is based on CosmJS version v0.28.3
(opens new window).
# Compiling the Protobuf objects and clients
You can choose which library you use to compile your Protobuf objects into TypeScript or JavaScript. Reproducing what cosmjs-types (opens new window) or Stargate (opens new window) do is a good choice.
# Preparation
This exercise assumes that:
- The library you're creating is in 'myLib'.
- Your Protobuf definition files are in
./proto/
. - You want to transpile them into TypeScript in
./src/codegen/
.
Install telescope
, in your package or in Docker:
You can confirm the version you received by running:
This returns something like:
The compiler tools are ready. Time to use them.
# Getting third party files
You need to get the imports that appear in your .proto
files. Usually you can find the following in query.proto
(opens new window):
You need local copies of the right file versions in the right locations. Pay particular attention to Cosmos SDK's version of your project. You can check by running:
This returns something like:
Use this version as a tag on Github. One way to retrieve the pagination file (opens new window) is:
You can do the same for the others, found in the third_party
folder (opens new window) under the same version:
Or you can add the latest whole third_party
folders by running npx telescope install
under myLib
folder:
# Compilation
You can now compile the Protobuf files using Telescope.
You should now see some .ts
files generated in ./src/codegen
. These are the real source files used in your application.
By default, the command takes ./proto
as input folder and ./src/codegen
as output folder. You can config in and out folders and the way Telescope generates code by using options like these examples:
When running the command, Telescope takes a proto folder as input, and generates files in a 'gen/src' folder.
Each time telescope transpile
has run, a config file, .telescope.json
, will be created in the folder where it is running:
protoDirs
: root directory that contains folders with Protobuf files. For example, ifprotoDirs
is set withproto
andproto-common
, when importing fromcosmos/base/query/v1beta1/pagination.proto
, Telescope will check whether there isproto/cosmos/base/query/v1beta1/pagination.proto
orproto-common/cosmos/base/query/v1beta1/pagination.proto
, and then generate a dependency based on the file.outPath
: contains TypeScript files structured by the folder structure of Protobuf files.
You can modify the config file according to Telescope options (opens new window), and then use the file by the option --config
:
protoDirs
are taken both from--protoDirs
arguments and theprotoDirs
field in the configuration file. It means proto files insideproto-common
andproto
will be taken in this case.outPath
is taken only from the configuration file,gen/src
in this case. It means that even if the--outPath
option is provided to the command, the value will be ignored.
After it has run successfully, there will be a message:
outPath
is created automatically during the transpilation. This means that you will get no "missing dir" error, for instance if your outPath
is incorrect.
You should now see your files transpiled into TypeScript. They have been correctly filed under their respective folders and contain both types and services definitions. It also created the transpiled versions of your third party imports.
# A note about the result
Your tx.proto
file may have contained the following:
If so, you find its service declaration in the compiled tx.ts
file (with Telescope option rpcClients.inline: true
) or in tx.rpc.msg.ts
file (with Telescope option rpcClients.inline: false
):
It also appears in the default implementation:
The important points to remember from this are:
rpc: RPC
is an instance of a Protobuf RPC client that is given to you by CosmJS. Although the interface appears to be declared locally (opens new window), this is the same interface found throughout CosmJS (opens new window). It is given to you on construction (opens new window). At this point you do not need an implementation for it.- You can see
encode
anddecode
in action. Notice the.finish()
that flushes the Protobuf writer buffer. - The
rpc.request
makes calls that are correctly understood by the Protobuf compiled server on the other side.
You can find the same structure in query.ts
(opens new window). (or query.rpc.Query.ts
with telescope option rpcClients.inline: false)
# Proper saving
Commit the extra .proto
files and the compiled ones to your repository so you do not need to recreate them. Add an npm run target to keep track of how this was done and easily reproduce it in the future when you update a Protobuf file:
# Add convenience with types
CosmJS provides an interface to which all the created types conform, TsProtoGeneratedType
(opens new window), which is itself a sub-type of GeneratedType
(opens new window). In the same file, note the definition:
The typeUrl
is the identifier by which Protobuf identifies the type of the data to serialize or deserialize. It is composed of the type's package and its name. For instance (and see also here (opens new window)):
In this case, the MsgSend
's type URL is "/cosmos.bank.v1beta1.MsgSend"
(opens new window).
Each of your types is associated like this. You can also find constant strings like those in generated files if prototypes.addTypeUrlToObjects
is set to true, take tx.ts (opens new window) as an example:
# For messages
Messages, sub-types of Msg
, are assembled into transactions that are then sent to CometBFT. CosmJS types already include types for transactions (opens new window). These are assembled, signed, and sent by the SigningStargateClient
(opens new window) of CosmJS.
The Msg
kind also needs to be added to a registry. To facilitate that, you should prepare them in a nested array:
Add child types to EncodeObject
to direct TypeScript:
In the previous code, you cannot reuse your msgSendTypeUrl
because it is a value not a type. You can add a type helper, which is useful in an if else
situation:
# For queries
Queries have very different types of calls. It makes sense to organize them in one place, called an extension. For example:
Note that there is a key bank:
inside it. This becomes important later on when you add it to Stargate.
- Create an extension interface for your module using function names and parameters that satisfy your needs.
- It is recommended to make sure that the key is unique and does not overlap with any other modules of your application.
- Create a factory for its implementation copying the model here (opens new window). Remember that the
QueryClientImpl
(opens new window) implementation must come from your own compiled Protobuf query service.
# Integration with Stargate
StargateClient
and SigningStargateClient
are typically the ultimate abstractions that facilitate the querying and sending of transactions. You are now ready to add your own elements to them. The easiest way is to inherit from them and expose the extra functions you require.
If your extra functions map one-for-one with those of your own extension, then you can publicly expose the extension itself to minimize duplication in StargateClient
(opens new window) and SigningStargateClient
(opens new window).
For example, if you have your interface MyExtension
with a myKey
key and you are creating MyStargateClient
:
You can extend StargateClientOptions
(opens new window) if your own client can receive further options.
You also need to inform MySigningStargateClient
about the extra encodable types it should be able to handle. The list is defined in a registry that you can pass as options (opens new window).
Take inspiration from the SigningStargateClient
source code (opens new window) itself. Collect your new types into an array:
Taking inspiration from the same place (opens new window), add the registry creator:
Now you are ready to combine this into your own MySigningStargateClient
. It still takes an optional registry, but if that is missing it adds your newly defined default one:
You can optionally add dedicated functions that use your own types, modeled on:
Think of your functions as examples of proper use, that other developers can reuse when assembling more complex transactions.
You are ready to import and use this in a server script or a GUI.
If you would like to get started on building your own CosmJS elements on your own checkers game, you can go straight to the exercise in CosmJS for Your Chain to start from scratch.
More specifically, you can jump to:
- Create Custom Objects, to see how to compile the Protobuf objects.
- Create Custom Messages, to see how to create messages relevant for checkers.
- Backend Script for Game Indexing, to see how this can be used also to listen to events coming from the blockchain.
- Integrate CosmJS and Keplr, to see how to use and integrate what you prepared into a preexisting Checkers GUI.
To summarize, this section has explored:
- How CosmJS's out-of-the-box interfaces understand how messages of standard Cosmos SDK modules are serialized, meaning that your unique modules will require custom CosmJS interfaces of their own.
- How to create the necessary Protobuf objects and clients in TypeScript, the extensions that facilitate the use of these clients, and any further level of abstraction that you deem useful for integration.
- How to integrate CosmJS with Ignite's client and signing client, which are typically the ultimate abstractions that facilitate the querying and sending of transactions.