# Your First CosmJS Actions
Take your first steps with CosmJS. Use it to send some simple transactions.
In this section, you will:
- Download and install CosmJS.
- Create a small experiment.
- Prepare a simple testnet.
- Establish your connection.
- Inspect a balance.
- Send transactions.
Now that you know what CosmJS is, you should take your first steps in using it. A basic feature of a Cosmos chain is the ability to send tokens via the bank
module. CosmJS naturally offers functions to cover this facility. You are going to:
- Use an existing test network (testnet) with a key of your own.
- Run basic CosmJS commands in a script that you run using the CLI.
Additionally, you can choose to:
- Start a local chain that exposes RPCs instead of using a testnet.
- Run the same basic CosmJS commands, but for this local chain.
Along the way, you learn the basic CosmJS concepts needed to start interacting with the Interchain Ecosystem.
# Script preparation
A small, ready-made repository exists so you can experiment with CosmJS. Clone it from here (opens new window). You need NodeJs (opens new window), or Docker, in which to run NodeJs. If you open the folder in Visual Studio Code (opens new window), the IDE should give you all the coding help you require. In the cloned folder you need to install the required modules:
Create a new file named experiment.ts
. In it, put these lines to confirm it works:
To execute, this TypeScript file needs to be compiled into JavaScript before being interpreted by NodeJs. Add this as a run target in package.json
:
Confirm that it does what you want:
This returns:
You will soon make this script more meaningful. With the basic script ready, you need to prepare some elements.
# Testnet preparation
The Interchain Ecosystem has a number of testnets running. The Cosmos Hub is currently running a public testnet (opens new window) for the Theta upgrade that you are connecting to and running your script on. You need to connect to a public node so that you can query information and broadcast transactions. One of the available nodes is:
You need a wallet address on the testnet and you must create a 24-word mnemonic in order to do so. CosmJS can generate one for you. Create a new file generate_mnemonic.ts
with the following script:
Now create a key for our imaginary user Alice:
You likely need to update Node.js to a later version if this fails. Find a guide here (opens new window).
When done, it should also tell you the address of the first account:
Temporarily keep this address for convenience, although CosmJS can always recalculate it from the mnemonic. Privately examine the file to confirm it contains your 24 words.
Important considerations:
process.stdout.write
was used to avoid any line return. Be careful not to add any empty lines or any other character in your.key
file (this occurs with VSCode under certain conditions). If you add any characters, CosmJS may not be able to parse it.Adjust the
.gitignore
file to not commit your.key
file by mistake:
For your convenience, all the code and files you've added so far are available here (opens new window) at the file-preparation
branch.
# Add your imports
You need a small, simple interface to a blockchain, one which could eventually have users. Good practice is to refrain from requesting a user address until necessary (e.g. when a user clicks a relevant button). Therefore, in experiment.ts
you first use the read-only client. Import it at the top of the file:
Note that VSCode assists you to auto-complete StargateClient
(opens new window) if you type CTRL-Space inside the {}
of the import
line.
# Define your connection
Next, you need to tell the client how to connect to the RPC port of your blockchain:
Inside the runAll
function you initialize the connection (opens new window) and immediately check (opens new window) you connected to the right place:
Run again to check with npm run experiment
, and you get:
# Get a balance
Normally you would not yet have access to your user's address. However, for this exercise you need to know how many tokens Alice has, so add a temporary new command inside runAll
:
getAllBalances
is used because the default token name is not yet known. When you run it again, you get:
If you just created this account, Alice's balance is zero. Alice needs tokens to be able to send transactions and participate in the network. A common practice with testnets is to expose faucets (services that send you test tokens for free, within limits).
The Cosmos Hub Testnet faucet has a dedicated Discord channel (opens new window) where you can ask for tokens once per day per Discord user.
Go to the faucet channel and request tokens for Alice by entering this command in the channel:
The faucet bot replies with a link to the transaction from the block explorer:
Check that Alice received the tokens with npm run experiment
, which should return:
uatom
is the indivisible token unit on the Testnet. It is short for micro-ATOM, or µ-ATOM. So 10 million uatom
equal 10 ATOM. After this confirmation you can comment out the balance query.
# Get the faucet address
As an exercise you want Alice to send some tokens back to the faucet, so you need its address. You can request this from the faucet bot, but it is also possible to get it using the transaction hash in experiment.ts
.
First you need to get the transaction.
Add the necessary import at the top:
Then, make sure you replace the hash with the one you received from the faucet bot.
# Deserialize the transaction
What does faucetTx
contain? Add the following line to find out:
Then run the script again using npm run experiment
. You find the following output, which in your case contains different values:
The structure of this output is JSON. There is a serialized faucetTx.tx
of the type Uint8Array
. The serialized transaction are the bytes (i.e. Uint8
) of the actual transaction that was sent over the testnet by the faucet. It is unintelligible to humans until you deserialize it properly. Use the methods offered by cosmjs-types
Tx
(opens new window) to deserialize it.
Add the necessary import at the top:
Then deserialize the transaction:
Which, on your next npm run experiment
, prints:
The faucet address information you are looking for is inside the body.messages
, and must be printed. Add:
Which, on your next npm run experiment
, prints:
Deserializing the transaction has not fully deserialized any messages that it contains, nor their value
, which is again a Uint8Array
. The transaction deserializer knows how to properly decode any transaction, but it does not know how to do the same for messages. Messages can in fact be of any type, and each type has its own deserializer. This is not something that the Tx.decode
transaction deserializer function knows.
# What is this long string?
Note the typeUrl: "/cosmos.bank.v1beta1.MsgSend"
string. This comes from the Protobuf definitions and is a mixture of:
The
package
whereMsgSend
is initially declared:And the name of the message itself,
MsgSend
:
This typeUrl
string you see in the decoded message is the canonical identifier of the type of message that's serialized under the value
array. There are many different message types, each coming from different modules or base layers from the Cosmos SDK, and you can find an overview of them here (opens new window).
The blockchain client itself knows how to serialize or deserialize it only because this "/cosmos.bank.v1beta1.MsgSend"
string is passed along. With this typeUrl
, the blockchain client and CosmJS are able to pick the right deserializer. This object is also named MsgSend
in cosmjs-types
. But in this tutorial, you have picked the deserializer manually.
To learn how to make your own types for your own blockchain project, head to Create Custom CosmJS Interfaces.
# Deserialize the message
Now that you know the only message in the transaction is a MsgSend
, you need to deserialize it. First add the necessary import at the top:
Then you deserialize the message. Add:
Which, on your next npm run experiment
, prints:
In this message, the fromAddress
is that of the faucet:
Similar to how you got the balance for Alice, you get the faucet's balance as well. Try this by copying (opens new window) the code to print Alice's balances. When running, you should get:
Instead of using the decode
functions that come with the Tx
and MsgSend
imports, you process the data yourself via alternative means. If you would like to experiment more, parse the rawLog
manually as opposed to deserializing the transaction as suggested previously.
Note the conceptual difference between Tx
and the rawLog
. The Tx
, or MsgSend
, object is an input to the computation that takes place when the transaction is included in a block. The rawLog
is the resulting output of said computation and its content depends on what the blockchain code emitted when executing the transaction.
In particular, if the transaction failed you would be able to extract the faucet address from the Tx
but not from the rawLog
, because the log would contain the error message.
From the IndexedTx
you see that there is a rawLog
(opens new window), which happens to be a stringified JSON.
The structure of the raw log is not always obvious, but in this example it contains:
Because this is a JSON file, you're able to fetch the faucet's address as follows:
Although this is a perfectly valid way to extract specific values from a transaction's message, it is not the recommended way to do so. The benefit of importing the message types is that you do not have to manually dig through the raw log of each Tx
you're looking to use in your application.
These actions are example uses of the read-only StargateClient
and of the serialization tools that come with CosmJS.
Get the result of the above steps here (opens new window).
Now it is time for Alice to send some tokens back to the faucet.
# Prepare a signing client
If you go through the methods inside StargateClient
(opens new window), you see that it only contains query-type methods and none for facilitating the preparation of transactions. It does have BroadcastTx
(opens new window), a function that can send ready-made transactions.
Now, for Alice to send transactions she needs to be able to sign them. And to be able to sign transactions she needs access to her private keys or mnemonics (or rather she needs a client that has access to them). That is where SigningStargateClient
(opens new window) comes in. Conveniently, SigningStargateClient
inherits from StargateClient
.
Update your import line:
Look at its declaration by right-clicking on the SigningStargateClient
in your imports and choosing Go to Definition.
When you instantiate SigningStargateClient
by using the connectWithSigner
(opens new window) method, you need to pass it a signer (opens new window). In this case, use the OfflineDirectSigner
(opens new window) interface.
The recommended way to encode messages is by using OfflineDirectSigner
, which uses Protobuf. However, hardware wallets such as Ledger do not support this and still require the legacy Amino encoder. If your app requires Amino support, you have to use the OfflineAminoSigner
.
Read more about encoding here (opens new window).
The signer needs access to Alice's private key, and there are several ways to accomplish this. In this example, use Alice's saved mnemonic. To load the mnemonic as text in your code you need this import:
There are several implementations of OfflineDirectSigner
available. The DirectSecp256k1HdWallet
(opens new window) implementation is most relevant to us due to its fromMnemonic
(opens new window) method. Add the import:
The fromMnemonic
factory function needs a string with the mnemonic. You read this string from the mnemonic file. Create a new top-level function that returns an OfflineDirectSigner
:
The Cosmos Hub Testnet uses the cosmos
address prefix. This is the default used by DirectSecp256k1HdWallet
, but you are encouraged to explicitly define it as you might be working with different prefixes on different blockchains. Lower down in your runAll
function, add:
As a first step, confirm that it recovers Alice's address as expected:
Now add the line that finally creates the signing client:
Check that it works like the read-only client that you used earlier, and from which it inherits (opens new window), by adding:
Run it with npm run experiment
.
Get the result of the previous steps here (opens new window).
# Send tokens
Alice can now send some tokens back to the faucet, but to do so she also needs to pay the network's gas fee. How much gas should she use, and at what price?
She can copy what the faucet did. To discover this, add:
When you run it, it prints:
With the gas information now decided, how does Alice structure her command so that she sends 1% of her holdings, i.e. 100000uatom
, back to the faucet? SigningStargateClient
's sendTokens
(opens new window) function takes a Coin[]
as input. Coin
is simply defined as:
Alice can pick any denom
and any amount
as long as she owns them, the signing client signs the transaction and broadcasts it. In this case it is:
With this gas and coin information, add the command:
To confirm that it worked, add another balance check:
Run this with npm run experiment
and you should get:
According to the rawLog
, the faucet received 100000uatom
. Since Alice ends up with "9899500uatom"
, it means she also paid 500uatom
for gas.
This concludes your first use of CosmJS to send tokens.
Find the result of all the previous steps here (opens new window).
You connected to a publicly running testnet. Therefore, you depended on someone else to have a blockchain running with an open and publicly available RPC port and faucet. What if you wanted to try connecting to your own locally running blockchain?
# With a locally started chain
The easiest option is to reuse the simd
chain that you started in another tutorial. Make sure that you have created two accounts, Alice and Bob. You also sent tokens using simd
. Be sure to credit enough tokens to Alice.
When you finally launch simd
:
You see the line:
Port 26657
is the default port for RPC endpoints built with the SDK, unless otherwise configured in ~/.simapp/config/config.toml
. 127.0.0.1:26657
is the URL you need to add to your script later.
Make a copy of your experiment.ts
script, with some adjustments. Name it experiment-local.ts
. Add a new run target in package.json
:
# Preparing your keys
Although you have Alice's address, you may not have her mnemonic or private key. The private key is stored in your operating system's keyring backend. For the purpose of this exercise, extract it - generally this is an unsafe operation:
You get a 64-digit-long hex value. Copy-paste it into a new simd.alice.private.key
file in your cosmjs-sandbox
folder. The .gitignore
was already configured earlier to ignore it, which mitigates the risk.
If you cannot remember which alias you gave your keys, list
them:
Which returns:
# Update your script
With the new elements in place, update your experiment-local.ts
script. Change rpc
:
And skip the lengthy process to get the faucet address. Just set faucet
to Bob's address:
Next, you need to replace the function to create Alice's signer because you're using a private key instead of a mnemonic, so the fromMnemonic
method that comes with DirectSecp256k1HdWallet
does not work. The fromKey
(opens new window) method that comes with DirectSecp256k1Wallet
is the more appropriate choice this time.
Adjust the import:
In DirectSecp256k1Wallet
the fromKey
factory function needs a Uint8Array
. Fortunately, CosmJS includes a utility to convert a hexadecimal string into a Uint8Array
. Import it:
Now create a new function to get a signer to replace the previous one:
Replace getAliceSignerFromMnemonic
with the newly created getAliceSignerFromPriKey
:
Also change the token unit from uatom
to stake
(opens new window) in your sendTokens
transaction, because this is the default token when using simapp
. Experiment with adjusting the values as desired. Run it with:
And confirm the output is as expected. For instance something like:
You have now used CosmJS's bank module on a locally running Cosmos blockchain.
Find the complete set of files here (opens new window).
If you would like to see how to do more actions when listening to events from your own checkers game, you can go straight to the related exercise in CosmJS for Your Chain.
More specifically, you can jump to:
- Backend Scripts for Game Indexing, for a Web2.0 server listening to game events.
To summarize, this section has explored:
- How to gain familiarity with CosmJS by implementing a basic feature of the Interchain Ecosystem, the ability to send tokens via the
bank
module. - How to clone a ready-made test repository and install the required modules in order to experiment with CosmJS, for which NodeJs and Visual Studio Code will be required.
- How to connect to a public node in the Interchain Ecosystem, acquire a wallet address on a testnet, and create a key for an imaginary user for the purposes of experimenting.
- How to add your imports, define your connection, get a balance, get the faucet address, prepare a signing client, and successfully send tokens on a chain being run by someone else.
- How to connect with your own locally running blockchain, including how to prepare your keys and update your script.