import base58 from './libs/base58' import * as Base64 from 'base64-js' import {concat} from './libs/utils' import * as Long from 'long' const stringToUint8Array = (str: string) => Uint8Array.from([...unescape(encodeURIComponent(str))].map(c => c.charCodeAt(0))) type Option = T | null | undefined export type TSerializer = (value: T) => Uint8Array export const empty: Uint8Array = Uint8Array.from([]) export const zero: Uint8Array = Uint8Array.from([0]) export const one: Uint8Array = Uint8Array.from([1]) export const BASE58_STRING: TSerializer = (value: string) => base58.decode(value) export const BASE64_STRING: TSerializer = (value: string) => Base64.toByteArray(value.replace('base64:', '')) export const STRING: TSerializer> = (value: Option) => value ? stringToUint8Array(value) : empty export const BYTE: TSerializer = (value: number) => Uint8Array.from([value]) export const BOOL: TSerializer = (value: boolean) => BYTE(value == true ? 1 : 0) export const BYTES: TSerializer = (value: Uint8Array | number[]) => Uint8Array.from(value) export const SHORT: TSerializer = (value: number) => { const s = Long.fromNumber(value, true) return Uint8Array.from(s.toBytesBE().slice(6)) } export const INT: TSerializer = (value: number) => { const i = Long.fromNumber(value, true) return Uint8Array.from(i.toBytesBE().slice(4)) } export const OPTION = (s: TSerializer): TSerializer => (value: R) => value == null || (typeof value == 'string' && value.length == 0) ? zero : concat(one, s(value as any)) export const LEN = (lenSerializer: TSerializer) => (valueSerializer: TSerializer): TSerializer => (value: T) => { const data = valueSerializer(value) const len = lenSerializer(data.length) return concat(len, data) } export const COUNT = (countSerializer: TSerializer) => (itemSerializer: TSerializer) => (items: T[]) => { const data = concat(...items.map(x => itemSerializer(x))) const len = countSerializer(items.length) return concat(len, data) } export const LONG: TSerializer = (value: number | string) => { let l: Long if (typeof value === 'number') { if (value > 2 ** 53 - 1) { throw new Error(`${value} is too big to be precisely represented as js number. Use string instead`) } l = Long.fromNumber(value) } else { l = Long.fromString(value.toString()) } return Uint8Array.from(l.toBytesBE()) } export const SCRIPT: TSerializer = (script) => OPTION(LEN(SHORT)(BASE64_STRING))(script ? script.slice(7) : null) export const ALIAS: TSerializer = val => { const [_, byte, alias] = val.split(':'); if (!byte || byte.length !== 1) throw new Error('Invalid network byte in alias') if (!alias || alias.length === 0) throw new Error('Invalid alias body') return concat([2], [byte.charCodeAt(0)], LEN(SHORT)(STRING)(alias)) } export const ADDRESS_OR_ALIAS: TSerializer = val => val.startsWith('alias') ? ALIAS(val) : BASE58_STRING(val)