> ## Documentation Index
> Fetch the complete documentation index at: https://www.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Cosmos

> Derive Cosmos SDK chain addresses and sign transactions using Dynamic embedded wallets in React Native

## Overview

Cosmos SDK chains use the secp256k1 elliptic curve — the same as Ethereum. You recover the compressed public key from the EVM wallet, then derive bech32-encoded addresses. The same key works across all Cosmos chains by changing the bech32 prefix (e.g., `cosmos` for Cosmos Hub, `osmo` for Osmosis).

| Property       | Value                                    |
| -------------- | ---------------------------------------- |
| Curve          | secp256k1                                |
| Root Wallet    | EVM                                      |
| Address Format | bech32 (prefix varies by chain)          |
| Hashing        | SHA-256 + RIPEMD-160                     |
| Serialization  | Amino JSON (sign) / Protobuf (broadcast) |
| Smallest Unit  | Varies (e.g., uatom for Cosmos Hub)      |

## Dependencies

<CodeGroup>
  ```bash npm theme={"system"}
  npm install @noble/curves @noble/hashes bech32
  ```

  ```bash yarn theme={"system"}
  yarn add @noble/curves @noble/hashes bech32
  ```

  ```bash pnpm theme={"system"}
  pnpm add @noble/curves @noble/hashes bech32
  ```

  ```bash bun theme={"system"}
  bun add @noble/curves @noble/hashes bech32
  ```
</CodeGroup>

## Derive Address

Recover the compressed secp256k1 public key from the EVM wallet, then compute RIPEMD-160(SHA-256(pubkey)) and bech32-encode:

```typescript theme={"system"}
import { sha256 } from "@noble/hashes/sha2";
import { ripemd160 } from "@noble/hashes/legacy";
import { bech32 } from "bech32";

function addressFromPubkey(compressedPubkey: Uint8Array, prefix: string): string {
  const hash160 = ripemd160(sha256(compressedPubkey));
  return bech32.encode(prefix, bech32.toWords(hash160));
}

async function deriveCosmosAddress(bech32Prefix: string = "cosmos"): Promise<{
  address: string;
  publicKey: string;
}> {
  const compressedPubkey = await recoverEvmPubkey("COSMOS_PUBKEY_RECOVERY");
  return {
    address: addressFromPubkey(compressedPubkey, bech32Prefix),
    publicKey: bytesToHex(compressedPubkey),
  };
}
```

Cache the `compressedPubkey` — key recovery only needs to happen once per session.

## Sign a Message

```typescript theme={"system"}
import { sha256 } from "@noble/hashes/sha2";

async function signCosmosMessage(
  message: string,
  displayDenom: string = "ATOM",
): Promise<string> {
  const wallet = dynamicClient.wallets.userWallets.find(w => w.chain === "EVM")!;

  const messageBytes = new TextEncoder().encode(message);
  const prefix = new TextEncoder().encode(`\x19${displayDenom} Signed Message:\n`);
  const lengthStr = new TextEncoder().encode(String(messageBytes.length));
  const digest = sha256(concatBytes(prefix, lengthStr, messageBytes));

  const signature = await dynamicClient.wallets.waas.signRawMessage(wallet.id, {
    accountAddress: wallet.address,
    message: bytesToHex(digest),
  });

  return signature;
}
```

## Sign a Transaction

Build an Amino JSON sign document, sign the SHA-256 hash, normalize to low-S, then encode in Protobuf for broadcasting:

```typescript theme={"system"}
import { sha256 } from "@noble/hashes/sha2";

async function signCosmosTransaction(signDoc: object): Promise<Uint8Array> {
  const wallet = dynamicClient.wallets.userWallets.find(w => w.chain === "EVM")!;

  const signDocBytes = new TextEncoder().encode(JSON.stringify(signDoc));
  const digest = sha256(signDocBytes);

  const signature = await dynamicClient.wallets.waas.signRawMessage(wallet.id, {
    accountAddress: wallet.address,
    message: bytesToHex(digest),
  });

  // Normalize to low-S (required by Cosmos SDK)
  const sigRaw = hexToBytes(signature.replace(/^0x/, ""));
  const SECP256K1_N = BigInt("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
  const HALF_N = SECP256K1_N / BigInt(2);
  const s = BigInt("0x" + bytesToHex(sigRaw.slice(32, 64)));
  const sNorm = s > HALF_N ? SECP256K1_N - s : s;

  const result = new Uint8Array(64);
  result.set(sigRaw.slice(0, 32));
  const sBytes = hexToBytes(sNorm.toString(16).padStart(64, "0"));
  result.set(sBytes, 32);
  return result;
}
```

For full transaction serialization (Protobuf encoding, broadcasting) see the [JavaScript Cosmos guide](/javascript/reference/tier-2-chains/cosmos). The only difference in React Native is the signing call.

## Verify a Signature

```typescript theme={"system"}
import { secp256k1 } from "@noble/curves/secp256k1";
import { sha256 } from "@noble/hashes/sha2";

function verifyCosmosSignature(
  message: string,
  signature: string,
  publicKey: string,
  displayDenom: string = "ATOM",
): boolean {
  const messageBytes = new TextEncoder().encode(message);
  const prefix = new TextEncoder().encode(`\x19${displayDenom} Signed Message:\n`);
  const lengthStr = new TextEncoder().encode(String(messageBytes.length));
  const digest = sha256(concatBytes(prefix, lengthStr, messageBytes));

  const sigBytes = hexToBytes(signature.replace(/^0x/, ""));
  const rsHex = bytesToHex(sigBytes.slice(0, 64));
  let v = sigBytes[64];
  if (v >= 27) v -= 27;

  try {
    const sig = secp256k1.Signature.fromHex(rsHex).addRecoveryBit(v);
    return sig.recoverPublicKey(digest).toHex(true) === publicKey.toLowerCase();
  } catch {
    return false;
  }
}
```

## Check Balance

```typescript theme={"system"}
async function getCosmosBalance(address: string, lcdUrl: string, denom: string): Promise<string> {
  const res = await fetch(`${lcdUrl}/cosmos/bank/v1beta1/balances/${address}`);
  if (!res.ok) return "0";
  const data = await res.json();
  const coin = (data.balances || []).find((b: { denom: string }) => b.denom === denom);
  if (!coin) return "0";
  return (Number(BigInt(coin.amount)) / 1e6).toString();
}
```
