Telescope
Composing Messages

Composing Messages

This document provides a reference for composing messages using Telescope-generated types to interact with Cosmos SDK blockchains.

Message Structure

ComponentDescription
Message TypeProtocol Buffer message type with corresponding TypeScript interface
Message ContentData conforming to the message structure
EncodingBinary or JSON encoding of the message
SigningCryptographic signature of the message for on-chain verification

Available Message Types

Message types are typically organized by module. Common modules include:

ModuleDescriptionExamples
bankToken transfersMsgSend, MsgMultiSend
stakingStaking and delegationMsgDelegate, MsgUndelegate
govGovernanceMsgSubmitProposal, MsgVote
distributionFee distributionMsgWithdrawDelegatorReward
authzAuthorizationMsgExec, MsgGrant

Creating Messages

Messages can be created using the .fromPartial() method:

import { MsgSend } from "./cosmos/bank/v1beta1/tx";
 
// Create a bank send message
const sendMsg = MsgSend.fromPartial({
  fromAddress: "cosmos1...",
  toAddress: "cosmos1...",
  amount: [
    { denom: "uatom", amount: "1000000" }
  ]
});

Message Validation

Telescope-generated types perform runtime validation when using helper methods:

// Will throw an error if invalid
const validatedMsg = MsgSend.fromJSON({
  from_address: "cosmos1...",
  to_address: "cosmos1...",
  amount: [
    { denom: "uatom", amount: "1000000" }
  ]
});

Message Composition Methods

MethodDescriptionExample
fromPartialCreates a message with default values for missing fieldsMsgSend.fromPartial({...})
fromJSONCreates a message from a JSON objectMsgSend.fromJSON({...})
encodeEncodes a message to binaryMsgSend.encode(msg, writer)
decodeDecodes a binary messageMsgSend.decode(bytes)

MessageComposer

For convenience, Telescope generates a MessageComposer class for each module:

import { bankComposer } from "./cosmos/bank/v1beta1/tx.composer";
 
const composedSendMsg = bankComposer.send({
  fromAddress: "cosmos1...",
  toAddress: "cosmos1...", 
  amount: [{ denom: "uatom", amount: "1000000" }]
});

MessageComposer Methods

Each MessageComposer provides:

PropertyDescription
typeUrlThe type URL for the message
valueThe message content

Transaction Composition

Multiple messages can be combined in a single transaction:

// Compose multiple messages
const messages = [
  bankComposer.send({
    fromAddress: "cosmos1...",
    toAddress: "cosmos1...",
    amount: [{ denom: "uatom", amount: "1000000" }]
  }),
  stakingComposer.delegate({
    delegatorAddress: "cosmos1...",
    validatorAddress: "cosmosvaloper1...",
    amount: { denom: "uatom", amount: "5000000" }
  })
];

Protobuf vs. Amino Encoding

Telescope supports both Protobuf and Amino encoding formats:

FormatUsageType Field
ProtobufModern Cosmos SDKstypeUrl (e.g., "/cosmos.bank.v1beta1.MsgSend")
AminoLegacy systems, some walletstype (e.g., "cosmos-sdk/MsgSend")

Amino Conversion Example

import { AminoTypes } from "@cosmjs/stargate";
import { aminoConverters } from "./amino/converters";
 
const aminoTypes = new AminoTypes(aminoConverters);
 
// Convert Protobuf to Amino format
const aminoMsg = aminoTypes.toAmino({
  typeUrl: "/cosmos.bank.v1beta1.MsgSend",
  value: sendMsg
});
 
// Convert back to Protobuf format
const protoMsg = aminoTypes.fromAmino(aminoMsg);

Message Registry

The registry maps message type URLs to their corresponding Protobuf types:

import { registry } from "./registry";
import { Registry } from "@cosmjs/proto-signing";
 
const protoRegistry = new Registry();
registry.forEach(([typeUrl, type]) => {
  protoRegistry.register(typeUrl, type);
});

Using Messages with Clients

With Stargate Client

import { SigningStargateClient } from "@cosmjs/stargate";
import { MsgSend } from "./cosmos/bank/v1beta1/tx";
 
// Create a message
const sendMsg = MsgSend.fromPartial({
  fromAddress: "cosmos1...",
  toAddress: "cosmos1...",
  amount: [{ denom: "uatom", amount: "1000000" }]
});
 
// Send transaction with message
const result = await signingClient.signAndBroadcast(
  "cosmos1...",
  [
    {
      typeUrl: "/cosmos.bank.v1beta1.MsgSend",
      value: sendMsg
    }
  ],
  fee
);

With RPC Client

import { createTxClient } from "./tx";
 
const txClient = await createTxClient({
  signer: wallet,
  rpcEndpoint: "https://rpc.cosmos.network"
});
 
const result = await txClient.signAndBroadcast(
  "cosmos1...",
  [sendMsg],
  fee
);

Common Message Types

Bank Messages

// MsgSend
const msgSend = MsgSend.fromPartial({
  fromAddress: "cosmos1...",
  toAddress: "cosmos1...",
  amount: [{ denom: "uatom", amount: "1000000" }]
});
 
// MsgMultiSend
const msgMultiSend = MsgMultiSend.fromPartial({
  inputs: [
    { address: "cosmos1...", coins: [{ denom: "uatom", amount: "1000000" }] }
  ],
  outputs: [
    { address: "cosmos1a...", coins: [{ denom: "uatom", amount: "500000" }] },
    { address: "cosmos1b...", coins: [{ denom: "uatom", amount: "500000" }] }
  ]
});

Staking Messages

// MsgDelegate
const msgDelegate = MsgDelegate.fromPartial({
  delegatorAddress: "cosmos1...",
  validatorAddress: "cosmosvaloper1...",
  amount: { denom: "uatom", amount: "1000000" }
});
 
// MsgUndelegate
const msgUndelegate = MsgUndelegate.fromPartial({
  delegatorAddress: "cosmos1...",
  validatorAddress: "cosmosvaloper1...",
  amount: { denom: "uatom", amount: "1000000" }
});

Governance Messages

// MsgSubmitProposal
const msgSubmitProposal = MsgSubmitProposal.fromPartial({
  content: Any.pack(textProposal, "/cosmos.gov.v1beta1.TextProposal"),
  initialDeposit: [{ denom: "uatom", amount: "10000000" }],
  proposer: "cosmos1..."
});
 
// MsgVote
const msgVote = MsgVote.fromPartial({
  proposalId: 1,
  voter: "cosmos1...",
  option: VoteOption.VOTE_OPTION_YES
});

Best Practices

  1. Always use the .fromPartial() method to ensure default values are properly set
  2. Keep type URLs consistent with the SDK version you're targeting
  3. Register all message types you plan to use with the registry
  4. Use MessageComposer for convenience when composing multiple messages
  5. Verify message structure before sending to avoid on-chain errors