Convert addresses
Within this document, we'll outline some examples on how to convert addresses between different formats in Titan Chain.
Convert Hex <> Bech32 address
Titan Chain addresses are compatible with both Ethereum addresses (hex format) and Cosmos addresses (bech32 format). You can convert between the two formats easily using the same private key.
Key Classes Overview
PrivKeySecp256k1 Class
The PrivKeySecp256k1
class handles private key operations:
export class PrivKeySecp256k1 {
// Generate a random private key
static generateRandomKey(): PrivKeySecp256k1;
// Create from Uint8Array
constructor(protected readonly privKey: Uint8Array);
// Convert to bytes
toBytes(): Uint8Array;
// Get corresponding public key
getPubKey(): PubKeySecp256k1;
// Sign a 32-byte digest
signDigest32(digest: Uint8Array): {
readonly r: Uint8Array;
readonly s: Uint8Array;
readonly v: number | null;
};
}
PubKeySecp256k1 Class
The PubKeySecp256k1
class handles public key operations and address generation:
export class PubKeySecp256k1 {
// Create from compressed (33 bytes) or uncompressed (65 bytes) public key
constructor(protected readonly pubKey: Uint8Array);
// Get public key bytes (compressed or uncompressed)
toBytes(uncompressed?: boolean): Uint8Array;
// Generate Cosmos-style address (sha256 + ripemd160)
getCosmosAddress(): Uint8Array;
// Generate Ethereum-style address (keccak256)
getEthAddress(): Uint8Array;
// Verify a 32-byte digest signature
verifyDigest32(digest: Uint8Array, signature: Uint8Array): boolean;
}
Using TypeScript
You can easily convert between a Titan address and Ethereum address by using our utility functions:
import { PrivKeySecp256k1 } from "lib/utils/key";
import { toBech32 } from "@cosmjs/encoding";
import { getAddress } from "ethers";
// From private key hex
const privateKeyHex = "your_private_key_hex";
const privateKey = new PrivKeySecp256k1(Buffer.from(privateKeyHex, "hex"));
const publicKey = privateKey.getPubKey();
// Generate ETH address (hex format)
const ethAddressBytes = publicKey.getEthAddress();
const ethAddress = getAddress(
"0x" + Buffer.from(ethAddressBytes).toString("hex")
);
// Generate Cosmos address and convert to bech32
const cosmosAddressBytes = publicKey.getCosmosAddress();
const titanAddress = toBech32("titan", cosmosAddressBytes);
console.log("ETH address => ", ethAddress);
console.log("Titan address => ", titanAddress);
Convert ETH Address to Titan Bech32
For chains using ETH-style addressing with bech32 encoding (slip44 = 60):
import { PrivKeySecp256k1 } from "lib/utils/key";
import { toBech32 } from "@cosmjs/encoding";
const privateKey = new PrivKeySecp256k1(Buffer.from(privateKeyHex, "hex"));
const publicKey = privateKey.getPubKey();
// ETH address bytes to bech32 format
const ethAddressBytes = publicKey.getEthAddress();
const ethToBech32 = toBech32("titan", ethAddressBytes);
console.log("ETH address in bech32 format => ", ethToBech32);
Generate Random Key and Addresses
Create a new random private key and generate all address formats:
import { PrivKeySecp256k1 } from "lib/utils/key";
import { toBech32 } from "@cosmjs/encoding";
import { getAddress } from "ethers";
// Generate random private key
const privateKey = PrivKeySecp256k1.generateRandomKey();
const publicKey = privateKey.getPubKey();
// Get private key as hex
const privateKeyHex = Buffer.from(privateKey.toBytes()).toString("hex");
// Generate all address formats
const ethAddressBytes = publicKey.getEthAddress();
const ethAddress = getAddress(
"0x" + Buffer.from(ethAddressBytes).toString("hex")
);
const cosmosAddressBytes = publicKey.getCosmosAddress();
const titanAddress = toBech32("titan", cosmosAddressBytes);
console.log({
privateKeyHex,
ethAddress,
titanAddress,
});
Multi-chain Address Generation
Generate addresses for multiple chains using the same private key:
import { PrivKeySecp256k1 } from "lib/utils/key";
import { toBech32 } from "@cosmjs/encoding";
import { getAddress } from "ethers";
function generateAddresses(privateKeyHex: string) {
const privateKey = new PrivKeySecp256k1(Buffer.from(privateKeyHex, "hex"));
const publicKey = privateKey.getPubKey();
const addressMap = new Map<string, string>();
// ETH address for EVM chains
const ethAddressBytes = publicKey.getEthAddress();
const ethAddress = getAddress(
"0x" + Buffer.from(ethAddressBytes).toString("hex")
);
// Cosmos address for Cosmos chains
const cosmosAddressBytes = publicKey.getCosmosAddress();
// Chain-specific addresses
const chains = [
{ chainId: "eip155:1", prefix: null, type: "eip155" },
{ chainId: "titan_18888-1", prefix: "titan", type: "cosmos", slip44: 60 },
{ chainId: "cosmoshub-4", prefix: "cosmos", type: "cosmos", slip44: 118 },
];
chains.forEach((chain) => {
if (chain.type === "eip155") {
addressMap.set(chain.chainId, ethAddress);
} else if (chain.type === "cosmos") {
const address =
chain.slip44 === 60
? toBech32(chain.prefix, ethAddressBytes)
: toBech32(chain.prefix, cosmosAddressBytes);
addressMap.set(chain.chainId, address);
}
});
return addressMap;
}
const addresses = generateAddresses("your_private_key_hex");
console.log("Multi-chain addresses => ", Object.fromEntries(addresses));
Signing and Verification
Sign and verify messages using the secp256k1 curve:
import { PrivKeySecp256k1 } from "lib/utils/key";
import { sha256 } from "@noble/hashes/sha2";
const privateKey = PrivKeySecp256k1.generateRandomKey();
const publicKey = privateKey.getPubKey();
// Message to sign
const message = "Hello Titan Chain";
const messageBytes = new TextEncoder().encode(message);
const digest = sha256(messageBytes);
// Sign the digest
const signature = privateKey.signDigest32(digest);
// Verify the signature
const signatureBytes = new Uint8Array([...signature.r, ...signature.s]);
const isValid = publicKey.verifyDigest32(digest, signatureBytes);
console.log("Signature valid:", isValid);
Key Differences
ETH addressing: Uses uncompressed public key + keccak256 hash
Cosmos addressing: Uses compressed public key + sha256 + ripemd160 hash
Chain-specific logic: Some chains use ETH addressing even with bech32 format (slip44 = 60)
Key generation: Uses secp256k1 curve for both private and public keys
Signing: Implements deterministic ECDSA with lowS for compatibility