export enum BaseConversionError { tooLong = 'An alphabet may be no longer than 254 characters.' , ambiguousCharacter = 'A character code may only appear once in a single alphabet.' , unknownCharacter = 'Encountered an unknown character for this alphabet.' } export interface BaseConverter { decode : ( source : string ) => Uint8Array | BaseConversionError . unknownCharacter ; encode : ( input : Uint8Array ) => string ; } param alphabet privateRemarks export const createBaseConverter = ( alphabet ) : BaseConversionError | BaseConverter => { const undefinedValue = 255 ; const uint8ArrayBase = 256 ; if ( alphabet . length >= undefinedValue ) return BaseConversionError . tooLong ; const alphabetMap = new Uint8Array ( uint8ArrayBase ) . fill ( undefinedValue ) ; for ( let index = 0 ; index < alphabet . length ; index ++ ) { const characterCode = alphabet . charCodeAt ( index ) ; if ( alphabetMap [ characterCode ] !== undefinedValue ) { return BaseConversionError . ambiguousCharacter ; } alphabetMap [ characterCode ] = index ; } const base = alphabet . length ; const paddingCharacter = alphabet . charAt ( 0 ) ; const factor = Math . log ( base ) / Math . log ( uint8ArrayBase ) ; const inverseFactor = Math . log ( uint8ArrayBase ) / Math . log ( base ) ; return { decode : ( input ) => { if ( input . length === 0 ) return Uint8Array . of ( ) ; const firstNonZeroIndex = input . split ( '' ) . findIndex ( ( character ) => character !== paddingCharacter ) ; if ( firstNonZeroIndex === - 1 ) { return new Uint8Array ( input . length ) ; } const requiredLength = Math . floor ( ( input . length - firstNonZeroIndex ) * factor + 1 ) ; const decoded = new Uint8Array ( requiredLength ) ; let nextByte = firstNonZeroIndex ; let remainingBytes = 0 ; while ( ( input [ nextByte ] as string | undefined ) !== undefined ) { let carry = alphabetMap [ input . charCodeAt ( nextByte ) ] ; if ( carry === undefinedValue ) return BaseConversionError . unknownCharacter ; let digit = 0 ; for ( let steps = requiredLength - 1 ; ( carry !== 0 || digit < remainingBytes ) && steps !== - 1 ; steps -- , digit ++ ) { carry += Math . floor ( base * decoded [ steps ] ) ; decoded [ steps ] = Math . floor ( carry % uint8ArrayBase ) ; carry = Math . floor ( carry / uint8ArrayBase ) ; } remainingBytes = digit ; nextByte ++ ; } const firstNonZeroResultDigit = decoded . findIndex ( ( value ) => value !== 0 ) ; const bin = new Uint8Array ( firstNonZeroIndex + ( requiredLength - firstNonZeroResultDigit ) ) ; bin . set ( decoded . slice ( firstNonZeroResultDigit ) , firstNonZeroIndex ) ; return bin ; } , encode : ( input ) => { if ( input . length === 0 ) return '' ; const firstNonZeroIndex = input . findIndex ( ( byte ) => byte !== 0 ) ; if ( firstNonZeroIndex === - 1 ) { return paddingCharacter . repeat ( input . length ) ; } const requiredLength = Math . floor ( ( input . length - firstNonZeroIndex ) * inverseFactor + 1 ) ; const encoded = new Uint8Array ( requiredLength ) ; let nextByte = firstNonZeroIndex ; let remainingBytes = 0 ; while ( nextByte !== input . length ) { let carry = input [ nextByte ] ; let digit = 0 ; for ( let steps = requiredLength - 1 ; ( carry !== 0 || digit < remainingBytes ) && steps !== - 1 ; steps -- , digit ++ ) { carry += Math . floor ( uint8ArrayBase * encoded [ steps ] ) ; encoded [ steps ] = Math . floor ( carry % base ) ; carry = Math . floor ( carry / base ) ; } remainingBytes = digit ; nextByte ++ ; } const firstNonZeroResultDigit = encoded . findIndex ( ( value ) => value !== 0 ) ; const padding = paddingCharacter . repeat ( firstNonZeroIndex ) ; return encoded . slice ( firstNonZeroResultDigit ) . reduce ( ( all , digit ) => all + alphabet . charAt ( digit ) , padding ) ; } , } ; } ; export const bitcoinBase58Alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' ; const base58 = createBaseConverter ( bitcoinBase58Alphabet ) as BaseConverter ; param input export const base58ToBin = base58 . decode ; param input export const binToBase58 = base58 . encode ;