UNPKG

raiden-ts

Version:

Raiden Light Client Typescript/Javascript SDK

190 lines 7.18 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { getAddress } from '@ethersproject/address'; import { BigNumber } from '@ethersproject/bignumber'; import { hexDataLength, isHexString } from '@ethersproject/bytes'; import { Two, Zero } from '@ethersproject/constants'; import { isLeft } from 'fp-ts/lib/Either'; import * as t from 'io-ts'; import { PathReporter } from 'io-ts/lib/PathReporter'; import memoize from 'lodash/memoize'; import { RaidenError } from './error'; function reporterAssert(value) { if (isLeft(value)) { throw new Error(PathReporter.report(value).join('\n')); } } /** * Decode/validate like codec.decode, but throw or return right instead of Either * * @param codec - io-ts codec to be used for decoding/validation * @param data - data to decode/validate * @param customError - Message or error to throw if the decoding fails * @param log - Logger to log error to * @returns Decoded value of codec type */ export function decode(codec, data, customError, log) { try { const decoded = codec.decode(data); reporterAssert(decoded); return decoded.right; } catch (originalError) { log?.('__decode failed:', codec.name, codec, data, originalError); throw customError ? customError instanceof Error ? Object.assign(customError, { data }) : new RaidenError(customError, { data }) : Object.assign(originalError, { data }); } } /** * Test for value's non-nulliness * Like lodash's negate(isNil), but also works as type guard (e.g. useful for filters) * * @param value - to be tested * @returns true if value is not null nor undefined */ export function isntNil(value) { return value != null; } export const BigNumberC = new t.Type('BigNumber', BigNumber.isBigNumber, (u, c) => { try { // BigNumber.from is able to decode number, strings and JSON.parse'd {_hex:<str>} objects return t.success(BigNumber.from(u)); } catch (err) { return t.failure(u, c); } }, (a) => a.toString()); /** * Helper function to create codecs to validate an arbitrary or variable-sized hex bytestring * A branded codec to indicate validated hex-strings * * @param size - Required number of bytes. Pass undefined or zero to have a variable-sized type * @returns branded codec for hex-encoded bytestrings */ export const HexString = memoize(function (size) { return t.brand(t.string, (n) => typeof n === 'string' && (size ? hexDataLength(n) === size : isHexString(n)), 'HexString'); }); /** * Helper function to create codecs to validate an arbitrary or variable-sized BigNumbers * A branded codec/type to indicate size-validated BigNumbers * * @param size - Required number of bytes. Pass undefined to have a variable-sized type * @returns branded codec for hex-encoded bytestrings */ export const Int = memoize(function (size) { const min = size ? Zero.sub(Two.pow(size * 8 - 1)) : undefined, max = size ? Two.pow(size * 8 - 1) : undefined; return t.brand(BigNumberC, (n) => BigNumberC.is(n) && (!min || !max || (n.gte(min) && n.lt(max))), 'Int'); }); /** * Helper function to create codecs to validate an arbitrary or variable-sized BigNumbers * A branded codec/type to indicate size-validated BigNumbers * * @param size - Required number of bytes. Pass undefined to have a variable-sized type * @returns branded codec for hex-encoded bytestrings */ export const UInt = memoize(function (size) { const min = size ? Zero : undefined, max = size ? Two.pow(size * 8) : undefined; return t.brand(BigNumberC, (n) => BigNumberC.is(n) && (!min || !max || (n.gte(min) && n.lt(max))), 'UInt'); }); // specific types // strig brand: ECDSA signature as an hex-string export const Signature = HexString(65); // string brand: 256-bit hash, usually keccak256 or sha256 export const Hash = HexString(32); // string brand: a secret bytearray, 32 bytes export const Secret = HexString(32); // string brand: ECDSA private key, 32 bytes export const PrivateKey = HexString(32); // uncompressed secp256k1 public key export const PublicKey = HexString(65); function isAddress(u) { try { return getAddress(u) === u; } catch (e) { return false; } } export const Address = new t.RefinementType('Address', isAddress, (u, c) => { try { return t.success(getAddress(u)); } catch (e) { return t.failure(u, c); } }, t.identity, HexString(20), isAddress); /** * Helper function to create codecs to validate derived types containing a timestamp ts * * @param codec - Codec to compose with a ts timestamp property * @returns Codec validating such subtype */ export const Timed = memoize((codec) => t.intersection([codec, t.readonly(t.type({ ts: t.number }))])); /** * Given a value of type T, returns a Timed<T> with current time as 'ts' member * * @param v - Value to return with time * @param ts - Timestamp to use, defaults to now * @returns copy of v added of a ts numeric timestamp */ export function timed(v, ts = Date.now()) { return { ...v, ts }; } /** * Remove ts timestamp field (from timed) from object passed as parameter (immutably) * * @param v - Timed object * @returns return a copy of v without ts property */ export function untime(v) { const { ts: _, ...withoutTs } = v; return withoutTs; } export const Signed = memoize((codec) => t.intersection([codec, t.readonly(t.type({ signature: Signature }))])); /** * Memoized factory to create codecs validating an arbitrary class C * * @param name - Class to create a codec for * @returns Codec validating class C */ export const instanceOf = memoize((name) => new t.Type(`instanceOf(${name})`, (v) => v?.constructor?.name === name, (i, c) => (i?.constructor?.name === name ? t.success(i) : t.failure(i, c)), t.identity)); /** * Like lodash's last, but properly infer return type when argument is a tuple * * @param arr - Tuple or array to get last element from * @returns Last element from arr */ export function last(arr) { return arr[arr.length - 1]; } /** * Math.max for BigNumbers * * @param args - Parameters to compare, must have at least one element * @returns Maxium of parameters as per BigNumber's lt comparison */ export function bnMax(...args) { return args.reduce((a, b) => (a.lt(b) ? b : a)); } /** * Creates a refinement of t.string which validates a template literal string * * @param regex - Regex which matches the generic parameter L * @param name - codec name * @returns refinement type of string to tempalte literal */ export function templateLiteral(regex, name) { const regex_ = typeof regex === 'string' ? new RegExp(regex) : regex; const predicate = (u) => regex_.test(u); return new t.RefinementType(name ?? `TemplateLiteral<${regex_.source}>`, (u) => t.string.is(u) && predicate(u), (i, c) => { const e = t.string.validate(i, c); if (isLeft(e)) { return e; } const a = e.right; return predicate(a) ? t.success(a) : t.failure(a, c); }, t.string.encode, t.string, predicate); } //# sourceMappingURL=types.js.map