Skip to content

What can CosmJS do for me?

Simon Warta edited this page Nov 10, 2021 · 15 revisions

How do I...

  • Create various unsigned Cosmos Hub transactions?
  • Sign an unsigned transaction when I have a mnemonic phrase? (12 word? 24 word?)
  • Sign an unsigned transaction when I have a private key?
  • Submit a signed transaction to a Cosmos Hub endpoint?
  • Create a multi-sig signature on an unsigned transaction?
  • Query state from a Cosmos Hub module using the legacy REST endpoint?
  • Query state from a Cosmos Hub module using the gRPC endpoint?
  • Query state from a custom module using the legacy REST endpoint?
  • Query state from a custom module using the gRPC endpoint?
  • Make it easier to submit multiple messages in a single transaction?

Calculating transaction IDs

If you have a list of complete transactions like this

{
  "txs": [
    "9AEoKBapCjCMTXENChQmiMbn/hEgc2noEV+vJkHASIhPShIUU7SyT3IQK/OJv+xZUz5LP9dR4FQKPpIdLk4KFCaIxuf+ESBzaegRX68mQcBIiE9KEhRTtLJPchAr84m/7FlTPks/11HgVBoMCgV1YXRvbRIDMTQ1EhAKCgoFdWF0b20SATAQ4KcSGmoKJuta6YchA9E/2C2Tibu+oguuOS2AwO9sGwy1ejes+u8hBdGJ2guiEkBZ7JjaZhENW56nbqaUH0SdgnuWHmPlPfgWs2LVwbOj6BDacUL+vcKqd4vxmkYRB9ORmqnDlj9ppaupmIPnOQuK",
    "ywEoKBapCkWoo2GaChQxrczb/flZmTC8mDh3uKwKyZCnpRIUWYCZ0lvC4PM8HrDaaR1oSU65PLoaEwoFdWF0b20SCjM4MDQwMDAwMDASEgoMCgV1YXRvbRIDNzUwEMCaDBpqCibrWumHIQMgfOISgYzdsLJnQtnhdT6c+pd1cZX0iiaWLE2i+Fa6xBJAIvmDdjJstWPWqshZxCtMXWXL1CBw06a9oYH8f2l7f19I5RsDDYJz1QTnKwor765rLsdAcF+5ucCBnkUnvy7I+g==",
    "twEoKBapCjCMTXENChQkQV2iWcShdar5UshUoWev/JUSfxIUyTRRx8hJmGRpUL1G+yw8xXQRSHsSEwoNCgV1YXRvbRIEMjUwMBDAzyQaagom61rphyEC1bbqM5eBuRSZ/jeQH1o25g7ybaQw3BlY4zQWmJb+k2QSQOSikSCd7AkDDjeCoNwdhdwgEOPiIZIHzF8TCSQSVdA/Iru/g9xE5eZTA4Cg2Q48idWyIHadYME5cKhVm3S8lHw=",
    "4gIoKBapCkOSHS5OChQ9NPQdHwRlRIc1MUP7Vy+gGRKtixIUYmtWnpmEfXd8qa3Khj8v1Xb//7oaEQoFdWF0b20SCDEwMDAwMDAwCkOSHS5OChQ9NPQdHwRlRIc1MUP7Vy+gGRKtixIUppAElPH3PNriuvHIuI/1/AuKM5waEQoFdWF0b20SCDEwMDAwMDAwCkOSHS5OChQ9NPQdHwRlRIc1MUP7Vy+gGRKtixIUyTRRx8hJmGRpUL1G+yw8xXQRSHsaEQoFdWF0b20SCDEwMDAwMDAwEhQKDgoFdWF0b20SBTIxODc5EJS1NRpqCibrWumHIQIT+U7tHv1kX5BoKGClAbKOf4nTI32tfFVX+AnedBYFnhJA/AedifYkknYTGcztoNn3VH7gMpeJXGsUxydWUMJ6wC8IT8uV5UWeHdmW0HbVsjRSxyKPDO+NLJbO35zwb1cT5CILTGVkZ2VyIExpdmU="
  ]
}

(from Cosmos Hub height 2421996), you can calculate a transaction ID like this:

import { sha256 } from "@cosmjs/crypto";
import { fromBase64, toHex } from "@cosmjs/encoding";

const tx = "9AEoKBapCjCMTXENChQmiMbn/hEgc2noEV+vJkHASIhPShIUU7SyT3IQK/OJv+xZUz5LP9dR4FQKPpIdLk4KFCaIxuf+ESBzaegRX68mQcBIiE9KEhRTtLJPchAr84m/7FlTPks/11HgVBoMCgV1YXRvbRIDMTQ1EhAKCgoFdWF0b20SATAQ4KcSGmoKJuta6YchA9E/2C2Tibu+oguuOS2AwO9sGwy1ejes+u8hBdGJ2guiEkBZ7JjaZhENW56nbqaUH0SdgnuWHmPlPfgWs2LVwbOj6BDacUL+vcKqd4vxmkYRB9ORmqnDlj9ppaupmIPnOQuK";
const transactionHash = sha256(fromBase64(tx));
const transactionId = toHex(transactionHash).toUpperCase();
console.log(transactionId); // 697D4B1394AEFB95676E775236600D2A60FC87593114316DA4238EE0DEC1EC88

Deriving a Cosmos Hub address from a public key

Given a secp256k1 public key, this gives you a Cosmos Hub address:

import { pubkeyToAddress } from "@cosmjs/amino";

const pubkey = {
  type: "tendermint/PubKeySecp256k1",
  value: "AtQaCqFnshaZQp6rIkvAPyzThvCvXSDO+9AzbxVErqJP",
};
const address = pubkeyToAddress(pubkey, "cosmos");
console.log(address); // cosmos1h806c7khnvmjlywdrkdgk2vrayy2mmvf9rxk2r

Deriving a Tendermint address from a consensus pubkey

Given a consensus pubkey like coralvalconspub1zcjduepqafdn8uprnsr35ptz653409ar3ft6uuu065f33f42fee67s67k3lqud5zqu, this derives an address in the Tendermint format. This is used e.g. in "proposer_address": "A8C66A16AC1FF07B390504D0DB211128440754F1" from /blockchain of Tendermint RPC.

import { sha256 } from "@cosmjs/crypto";
import { fromBase64, toHex } from "@cosmjs/encoding";
import { decodeBech32Pubkey } from "@cosmjs/launchpad";

const pubkey = decodeBech32Pubkey(
  "coralvalconspub1zcjduepqafdn8uprnsr35ptz653409ar3ft6uuu065f33f42fee67s67k3lqud5zqu",
);
const addressData = sha256(fromBase64(pubkey.value)).slice(0, 20);
const address = toHex(addressData).toUpperCase();
console.log(address); // 0C0A3D4E86CD4B104BF2972851647161074D1BB8

Derive valconspub and valcons from base64 encoded pubkey

The *valconspub1* identifiers are bech32 encoded Amino pubkeys. Usually Ed25519 is used for consensus (=Tendermint) keypairs. But this can change. *valcons1* are bech32 encoded Tendermint addresses, which are derived differently than Cosmos SDK addresses.

import { sha256 } from "@cosmjs/crypto";
import { Bech32, fromBase64 } from "@cosmjs/encoding";
import { encodeBech32Pubkey } from "@cosmjs/launchpad";

// Ed25519 pubkey from genesis
const pubkey = {
  type: "tendermint/PubKeyEd25519",
  value: "iARPJIXVwen//D6qB5CoQT1KrTK7ffGOkIstFF3KIgk=",
};

const bech32Pubkey = encodeBech32Pubkey(pubkey, "regen:valconspub");
console.log(bech32Pubkey); // regen:valconspub1zcjduepq3qzy7fy96hq7nllu864q0y9ggy754tfjhd7lrr5s3vk3ghw2ygyspmhej4

const ed25519PubkeyRaw = fromBase64(pubkey.value);
const addressData = sha256(ed25519PubkeyRaw).slice(0, 20);
const bech32Address = Bech32.encode("regen:valcons", addressData);
console.log(bech32Address); // regen:valcons14y3uv3g3fp5k473qtdenmn5cv89y2s5nz7cshu

Derive Ethereum public key from private key

import { fromHex, toHex } from "@cosmjs/encoding";
import { keccak256, Secp256k1 } from "@cosmjs/crypto";

const privkey = fromHex("1da6847600b0ee25e9ad9a52abbd786dd2502fa4005dd5af9310b7cc7a3b25db");
const keypair = await Secp256k1.makeKeypair(privkey);
toHex(keypair.pubkey) // 04b9e72dfd423bcf95b3801ac93f4392be5ff22143f9980eb78b3a860c4843bfd04829ae61cdba4b3b1978ac5fc64f5cc2f4350e35a108a9c9a92a81200a60cd64

const compressed = Secp256k1.compressPubkey(keypair.pubkey);
toHex(compressed) // 02b9e72dfd423bcf95b3801ac93f4392be5ff22143f9980eb78b3a860c4843bfd0

const address = `0x${toHex(keccak256(keypair.pubkey.slice(1)).slice(-20))}`; // 0x71cb05ee1b1f506ff321da3dac38f25c0c9ce6e1

Compress secp256k1 public key

Cosmos SDK expects compressed secp256k1 public keys. Those are 33 bytes long, starting with 0x02 or 0x03. If you have an uncompressed pubkey key from an external source, you con compress it.

import { fromHex, toHex } from "@cosmjs/encoding";
import { Secp256k1 } from "@cosmjs/crypto";

// Uncompressed format (65 bytes, starting with 0x04)
const pubkey = fromHex("044f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c7029013b587a681e836cc187a8164b98a5848a2b89b3173315fdd0740d5032e259cd5");
const compressed = Secp256k1.compressPubkey(pubkey);
console.log(toHex(compressed)); // 034f04181eeba35391b858633a765c4a0c189697b40d216354d50890d350c70290

Parse Cosmos SDK Decimal type

Given a delegation rewards response like

{
  rewards: [
    {
      validatorAddress: 'junovaloper1juczud9nep06t0khghvm643hf9usw45r23gsmr',
      reward: [Array]
    }
  ],
  total: [ { denom: 'ujuno', amount: '1519872422095573900000' } ]
}

you can use CosmJS to parse the amount in a human readable representation without loosing precision as follows:

import { Decimal } from "@cosmjs/math";

const amount = Decimal.fromAtomics("1519872422095573900000", 18);
console.log(amount.toString()); // "1519.8724220955739"
console.log(amount.atomics); // "1519872422095573900000"
console.log(amount.fractionalDigits); // 18
console.log(amount.toFloatApproximation()); // 1519.872422095574 (please note the rounding in the last position!)