const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; const ALPHABET_MAP = new Map(); for (let i = 0; i < BASE58_ALPHABET.length; i++) { ALPHABET_MAP.set(BASE58_ALPHABET[i], i); } export function base58Decode(str: string): Uint8Array { if (str.length === 0) return new Uint8Array(0); // Count leading '1's (zero bytes) let leadingZeros = 0; while (leadingZeros < str.length && str[leadingZeros] === '1') { leadingZeros++; } // Decode base58 to big integer (stored as byte array) const size = Math.ceil(str.length * 733 / 1000) + 1; // log(58) / log(256) const bytes = new Uint8Array(size); for (let i = leadingZeros; i < str.length; i++) { const val = ALPHABET_MAP.get(str[i]); if (val === undefined) throw new Error(`Invalid base58 character: ${str[i]}`); let carry = val; for (let j = size - 1; j >= 0; j--) { carry += 256 * bytes[j]; bytes[j] = carry % 256; carry = Math.floor(carry / 256); } } // Skip leading zeros in the decoded bytes let start = 0; while (start < size && bytes[start] === 0) { start++; } const result = new Uint8Array(leadingZeros + (size - start)); // Leading zeros from '1' characters for (let i = 0; i < leadingZeros; i++) { result[i] = 0; } // Decoded bytes for (let i = start; i < size; i++) { result[leadingZeros + (i - start)] = bytes[i]; } return result; } export function base58Encode(bytes: Uint8Array): string { if (bytes.length === 0) return ''; // Count leading zero bytes let leadingZeros = 0; while (leadingZeros < bytes.length && bytes[leadingZeros] === 0) { leadingZeros++; } // Encode to base58 const size = Math.ceil(bytes.length * 138 / 100) + 1; // log(256) / log(58) const digits = new Uint8Array(size); for (let i = leadingZeros; i < bytes.length; i++) { let carry = bytes[i]; for (let j = size - 1; j >= 0; j--) { carry += 256 * digits[j]; digits[j] = carry % 58; carry = Math.floor(carry / 58); } } // Skip leading zeros in base58 output let start = 0; while (start < size && digits[start] === 0) { start++; } let result = '1'.repeat(leadingZeros); for (let i = start; i < size; i++) { result += BASE58_ALPHABET[digits[i]]; } return result; } /** * Convert an SS58 address to a base58-encoded raw pubkey (Cesium+ v1 format). * * SS58 layout: [prefix (1 or 2 bytes)] [32 bytes pubkey] [2 bytes checksum] * - If first byte has bit 6 set (& 0x40), prefix is 2 bytes * - Otherwise prefix is 1 byte */ export function ss58ToV1Pubkey(ss58: string): string { const raw = base58Decode(ss58); // Determine prefix length const prefixLen = (raw[0] & 0x40) ? 2 : 1; // Extract 32-byte pubkey (skip prefix, drop 2-byte checksum) const pubkey = raw.slice(prefixLen, prefixLen + 32); if (pubkey.length !== 32) { throw new Error(`Invalid SS58 address: expected 32-byte pubkey, got ${pubkey.length}`); } return base58Encode(pubkey); }