raiden-ts
Version:
Raiden Light Client Typescript/Javascript SDK
190 lines • 7.18 kB
JavaScript
/* 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