Tutorial: How-to Cosmos Pt. 4- Building your first application with Cosmjs

KagemniKarimu
Lava Network
Published in
8 min readJun 4, 2023

We return to our “How To Cosmos" blog series. To see the previous installment in this series click here!

Check out the Github Repository with the complete version of this project: KagemniKarimu/cosmjs-master-app-template

If you’ve been following these tutorials, you should have some base awareness of Cosmjs and its capabilities already. In addition to being considered an SDK of its own, Cosmjs can actually be considered a minimal JavaScript/TypeScript API for CosmosSDK. The main package we’ll use in this tutorial is @cosmjs/stargate , which is actually a client library of Cosmos SDK and our primary engine for driving transactions and reading blockchain data.

In this tutorial, we’ll build a small example app which checks TxHashes and Wallet Addresses and prints out results to the browser. We’ll start by cloning a React.js web application built using MaterialUI . From there, we will use Cosmjs to build a fully functioning web application with two primary features:

  • check the balance on any wallet on the Cosmos Hub Testnet (COS5T)
  • give a description of any COS5T Transaction Hash (Tx Hash)

Before we begin, a note on this: This is a small step in the direction of dApp building. However, in the strictest sense, I hesitate to use the term dApp to describe what we build. Although our backend is technically RPC nodes on the blockchain which delivers our data, there is no use of smart contracts to execute our code — which is part and parcel of many definitions of dApps.

In order to complete the following tutorial, you’ll need:
✅ git [install guide]
✅ Node.js v18 + [download]
✅ Cosmos Wallet with funds loaded on Cosmos Hub [guide]
✅ A completed transaction on Cosmos Hub Testnet (COS5T) [guide]
✅ COS5T RPC node URL [accessible from Lava gateway]

1. Clone the Repo to Get Started

For starters, we’re going to reference the git repository linked here. To save us time, I prepared a repo with all dependencies set up and a pre-configured React environment to save you several steps! Unfortunately, at of the time of this tutorial, Cosmjs has dependencies which are incompatible with create-react-app. For fans of quick starts, this repo is about as good as it gets for now! Feel free to fork to track your own changes.

Let’s clone the repository:

git clone https://github.com/KagemniKarimu/cosmjs-master-app-template.git

The default branch main gives us the base application minus all the modifications we’ll make. It gives us React, Cosmjs, MaterialUI, and some boilerplate so we can get straight into making our application fully featureful.

The branch complete has our fully functional web application available. If you want to skip ahead to the end to see what we build you can use git checkout complete . Otherwise, at any point in this tutoiral, you can use the complete branch to investigate how your code should look!

Let’s go ahead and ensure all of our dependencies from our package files are installed. Ensure you’re in the directory of cosmjs-master-app-template and enact an install:

npm install

2. Activate Stargate

Let’s go ahead and uncomment Lines 2 and Lines 5-21 . Delete the // , /* and */ respectively.


// UNCOMMENT THIS import { StargateClient } from "@cosmjs/stargate"

//UNCOMMENT BELOW
useEffect(() => {
connectStargate();
}, []);


const connectStargate = async () => {
try {
const stargateClient = await StargateClient.connect(
'RPC_ENDPOINT_URL_HERE'
);
setClient(stargateClient);
console.log("CosmJS Stargate Connected @",await stargateClient.getChainId());
} catch (error) {
console.error('Error connecting to Stargate:', error);
}
};

On line 13 we need to replace RPC_ENDPOINT_URL_HERE with the URL to our RPC Node. It is important to use an RPC Endpoint which is both Cosmos Hub Testnet (COS5T) and TendermintRPC. Stargate expects a TendermintRPC endpoint and will throw errors if you try another type of endpoint. If you don’t have this handy, don’t worry! You can always get them easily from the Lava Gateway.

We now have an app that can connect to Cosmos Hub Blockchain and retrieve information! To test, simply run npm start . The app will launch in your browser hosted locally. You can inspect your browser console and verify that you see CosmJS Stargate Connected @ theta-testnet-001 logged. This lets you know that our connection was successful!

3. Create the App Logic

Once we have Stargate working and functioning, we’ll need to add some application logic to make our application actually do something!

The first thing is we need to import useState from React. Modify our Line 1 of App.js to look like this:

import React, { useEffect, useState } from “react”;

This allows us to pass around states from component to component once we’ve collected some user input. It will come in handy later. For now, let’s specify those states within our App const:

    const [client, setClient] = useState();
const [balances, setBalances] = useState([]);
const [transactions, setTransactions] = useState([]);

Staying within our App const , let’s create some async functions that call on our client to get balances and get tx info:

      const handleGetBalance = async (address) => {
if (client) {
try {
const balances = await client.getAllBalances(address);
setBalances(balances);
} catch (error) {
console.error("Error retrieving balances:", error);
}
} else {
console.log("Stargate client is not available");
}
};

const handleGetTx = async (hash) => {
if (client) {
try {
const tx = await client.getTx(hash);
setTransactions([tx]);
} catch (error) {
console.error("Error retrieving transaction:", error);
}
} else {
console.log("Stargate client is not available");
}
};

These functions implement the basic logic of calling our initialized Stargate client to getAllBalancesand getTx . The outcome of these calls are stored in our balances and [tx] states, respectively. The functions themselves will be passed as props to our React components that are called whenever a button is pushed.

4. Create the Necessary Components

A component is a piece of the user interface which is rendered by React with its own logic and appearance. We need several components for the user to be able to input data. To learn more about how React works click here.

We’re going to make two components!

  1. WalletAddressForm stored in WalletInput.js
  2. TxHashForm stored in TxInput.js

Let’s start by making the files! At our app’s root directory, let’s work:

touch ./src/components/WalletInput.js
touch ./src/components/TxInput.js

WalletInput.js

Copy and paste the following code into ./src/components/WalletInput.js

import { TextField, Button } from '@mui/material';
import React, { useState } from 'react';

const WalletAddressForm = ({ onGetBalance }) => {
const [value, setValue] = useState("");

const handleChange = (event) => {
setValue(event.target.value);
};

const handleSubmit = (event) => {
event.preventDefault();
onGetBalance(value);
};


return (
<center>
<form onSubmit={handleSubmit}>
<TextField
label="cosmos address"
value={value}
onChange={handleChange}
/>
<Button variant="contained" type="submit">
Get Balance
</Button>
</form>
</center>
);
};

export default WalletAddressForm;

Let’s make sure this is accessible for when we build our UI; add this line to our imports at the top of App.js:

import WalletAddressForm from './components/WalletInput.js'

TxInput.js

Copy and paste the following code into ./src/components/TxInput.js

import { TextField, Button } from '@mui/material';
import React, { useState } from 'react';


const TxHashForm = ({ onGetTx }) => {
const [value, setValue] = useState('');

const handleChange = (event) => {
setValue(event.target.value);
};

const handleSubmit = (event) => {
event.preventDefault();
onGetTx(value);
};

return (
<form onSubmit={handleSubmit}>
<TextField
label="tx hash"
value={value}
onChange={handleChange}
/>
<Button variant="contained" type="submit">
Query Tx
</Button>
</form>
);
};

export default TxHashForm;

Once again, we need to make sure we import what we’ve just created for further use:

import TxHashForm from "./components/TxInput.js";

5. Furnish the UI

We’re almost there. Let’s revisit the return statement in the App constof App.js. Here, we need to feed React some JSX that will help render the components we created so that they can be seen and interacted with via browser. This is really the final step in making our app functional.

First we need to add some instances of our components. On the underside of where it says <h1>The Beginning of a Masterful App!</h1> insert the following:

<WalletAddressForm onGetBalance={handleGetBalance} />
<br />
<TxHashForm onGetTx={handleGetTx} />

This gives us a way to collect our inputs! Specifically, it points the Forms at the handler functions we wrote in our application logic earlier. Now, we just need to display outputs.

First, let’s do balances:

      <div>
<h2>Balances:</h2>
<ul>
{balances.map((balance, index) => (
<li key={index}>
{balance.amount} {balance.denom}
</li>
))}
</ul>
</div>

This will effectively list all of our balances {amount, denom} on our browser page!

Next, let’s do transactions:

      <br />
<div>
<h2>Transactions:</h2>
<ul>
{transactions.map((tx, index) => (
<li key={index}>{JSON.stringify(tx)}</li>
))}
</ul>
</div>

This will prettify and print the JSON which is returned when we find a transaction! It still won’t be perfectly friendly to the human eye, but it’ll be good enough for our purposes!

Let’s enclose our entire operation in <center> </center> tags if we haven’t already! We want to be neat after all. 😄

6. Test

Now, to the fruits of our many labors! Let’s see how this application runs and functions. If you don’t already have it running, restart the program:

npm start

Pull your cosmos address from any previous tutorial and give it a go. If you don’t remember your address and need Cosmos addresses to pull from try typing gaiad keys list | grep address into the command line. It should output a list of usable keys! If you don’t have gaiad installed, you can use an address from below:

  address: cosmos15dgrn5rl8eah6s3lcjm5gjsn78w6kg5plmn6ml
address: cosmos1qxpu7dc0vz2cha0rucdezevejvcwltw4ytqyd2

You’ll also need to pull a transaction for your test! You can use either address above or one of your own choosing and type gaiad q txs — events ‘message.sender=<address>’ | grep txhash . Of course you’ll need to replace <address> with the actual address you’ll use. This will return you some txhashes (if they exist on the given address!). If you haven’t made any transactions yet, you can use one of mine!

  txhash: 08E2A44573E9FB241FCD91F9E5C2951B812B0DBEC0C0D08D57E3787B5949BE64
txhash: 496BCE70E3515503F25C964D33347BC9F217300DEF6DA1793804D249D05927BC
txhash: 0F4843D34855FEB18B6DBC07396A97099D8B73A2E37EC1530391D4A44A9AEE72

🟢 Once you’ve tested both fields, your final screen should look like what’s displayed below!

✔️Success! That’s all on this one. If you had any struggles with this, don’t worry. It was a lot to follow! Check out the supplementary resources and don’t hesitate to reach out. Remember to git checkout complete if you want to compare your final product to mine!

Next time, we’ll be digging even deeper with more example functionality. Additionally, we’re inching our way towards working with CosmWASM :). Remember, there’s always a lot more you can do on this — see can you edit your application to send transactions as we did in the last tutorial!

Further Resources

🚪https://gateway.lavanet.xyz

💻How to Cosmos Part 1
💻
How to Cosmos Part 2
💻
How to Cosmos Part 3

⚛️ Official CosmJs Tutorial
⚛️
Official CosmJs documentation

About the Author🧑🏿‍💻

KagemniKarimu is current Developer Relations Engineer for Lava Network and former Developer Relations at Skynet Labs. He’s a self-proclaimed Rubyist, new Rust learner, and friendly Web3 enthusiast who entertains all conversations about tech. Follow him on Twitter or say hi to him on Lava’s Discord where he can be found lurking.

About Lava 🌋

Lava is a decentralized network of top-tier API providers that developers can subscribe to for access to any blockchain. Providers are rewarded for their quality of service, meaning apps and users get maximum speed, data integrity and uptime. RPC requests are routed to multiple endpoints at once, meaning anyone can privately query or send transactions on-chain, while being matched to the most performant and reliable providers.

We help developers build web3 apps on any chain, with reliable, performant and decentralized node infrastructure.

--

--