Creating Signers
This document provides a reference for creating and using signers in Telescope generated clients.
Overview
To broadcast transactions to Cosmos-based blockchains, you need to sign them with a valid account. Telescope supports multiple signer implementations that work with its generated clients.
Signer Types
OfflineSigner
The OfflineSigner interface from @cosmjs/proto-signing is the base interface for all signers in Telescope. It provides the following methods:
- getAccounts(): Returns the accounts controlled by this signer
- signDirect(signerAddress, signDoc): Signs a transaction using the direct signing method
DirectSecp256k1Wallet
A wallet implementation that uses the Secp256k1 elliptic curve for signing.
import { DirectSecp256k1Wallet } from '@cosmjs/proto-signing';
 
// Create a wallet from a private key
const wallet = await DirectSecp256k1Wallet.fromKey(privateKey, prefix);
 
// Create a random wallet
const wallet = await DirectSecp256k1Wallet.generate(prefix);Amino Signer vs Proto Signer
Telescope supports both Amino and Protobuf transaction signing:
Amino Signer
Most applications should use the Amino signer for compatibility with wallets like Keplr:
import { getOfflineSigner as getOfflineSignerAmino } from 'cosmjs-utils';
 
const signer = await getOfflineSignerAmino({
    mnemonic,
    chain
});Proto Signer
For direct Protobuf signing:
import { getOfflineSigner as getOfflineSignerProto } from 'cosmjs-utils';
 
const signer = await getOfflineSignerProto({
    mnemonic,
    chain
});Wallet Integration
Keplr Wallet
Telescope clients integrate well with the Keplr browser extension:
// Request connection to Keplr
await window.keplr.enable(chainId);
 
// Get the offline signer from Keplr
const signer = window.keplr.getOfflineSigner(chainId);
 
// Use with your Telescope client
const client = await getSigningClient({
    rpcEndpoint,
    signer
});Using Mnemonics (Development Only)
You can create signers from mnemonics for development purposes:
import { chains } from 'chain-registry';
 
const mnemonic = 'unfold client turtle either pilot stock floor glow toward bullet car science';
const chain = chains.find(({ chain_name }) => chain_name === 'osmosis');
const signer = await getOfflineSignerAmino({
    mnemonic,
    chain
});WARNING: Never use plain-text mnemonics in production. Always use secure key management practices.
Custom Registry Configuration
When creating signers for custom chains, you may need to configure the registry and amino types:
import { Registry } from '@cosmjs/proto-signing';
import { AminoTypes } from '@cosmjs/stargate';
import { 
    cosmosAminoConverters, cosmosProtoRegistry,
    cosmwasmAminoConverters, cosmwasmProtoRegistry,
    ibcAminoConverters, ibcProtoRegistry,
    myChainAminoConverters, myChainProtoRegistry
} from 'my-chain-js';
 
const protoRegistry = [
    ...cosmosProtoRegistry,
    ...cosmwasmProtoRegistry,
    ...ibcProtoRegistry,
    ...myChainProtoRegistry
];
 
const aminoConverters = {
    ...cosmosAminoConverters,
    ...cosmwasmAminoConverters,
    ...ibcAminoConverters,
    ...myChainAminoConverters
};
 
const registry = new Registry(protoRegistry);
const aminoTypes = new AminoTypes(aminoConverters);
 
const stargateClient = await SigningStargateClient.connectWithSigner(
    rpcEndpoint, 
    signer, 
    {
        registry,
        aminoTypes
    }
);Key Management Best Practices
When working with signers in production applications:
- Never store private keys or mnemonics in client-side code
- Use hardware wallets or browser extensions when possible
- Consider using secure enclave or TPM storage for server-side applications
- Implement proper key rotation and revocation procedures
- Use AES encryption for any stored key material
Common Issues and Solutions
Chain Prefix Mismatch
If you encounter address verification errors, ensure you're using the correct chain prefix:
// For Cosmos Hub
const wallet = await DirectSecp256k1Wallet.generate('cosmos');
 
// For Osmosis
const wallet = await DirectSecp256k1Wallet.generate('osmo');Transaction Simulation
Before broadcasting transactions, it's recommended to simulate them first:
// Simulate the transaction to estimate gas
const gasEstimated = await stargateClient.simulate(address, msgs, memo);
 
// Add buffer for safety
const gasWithBuffer = Math.round(gasEstimated * 1.3);
 
// Create fee with estimated gas
const fee = {
    amount: coins(0, 'uatom'),
    gas: gasWithBuffer.toString()
};