UNPKG

@parity/api

Version:

The Parity Promise-based API library for interfacing with Ethereum over RPC

290 lines (236 loc) 6.87 kB
// Copyright 2015-2019 Parity Technologies (UK) Ltd. // This file is part of Parity. // // SPDX-License-Identifier: MIT /* eslint-disable @typescript-eslint/no-use-before-define */ import BigNumber from 'bignumber.js'; import { BlockNumber, Condition, DeriveObject, Derive, FilterObject, FilterOptions, Options, Topic } from '../types'; import { isArray, isHex, isInstanceOf, isString } from '../util/types'; import { padLeft, toHex } from '../util/format'; import { SerializedCondition, SerializedTransaction } from './types.serialized'; /** * Validate input address. * * @param address - Input address to validate. */ export const inAddress = (address?: string) => { // TODO: address validation if we have upper-lower addresses return inHex(address); }; /** * Validate input addresses. * * @param addresses - Input addresses to validate. */ export const inAddresses = (addresses?: (string | undefined)[]) => { return (addresses || []).map(inAddress); }; export const inBlockNumber = (blockNumber?: BlockNumber) => { if (isString(blockNumber)) { switch (blockNumber) { case 'earliest': case 'latest': case 'pending': return blockNumber; } } return inNumber16(blockNumber); }; export const inData = (data: string) => { if (data && data.length && !isHex(data)) { data = data .split('') .map(chr => `0${chr.charCodeAt(0).toString(16)}`.slice(-2)) .join(''); } return inHex(data); }; export const inHash = (hash: string) => { return inHex(hash); }; export const inTopics = (topics?: (Topic | Topic[])[]): (string | null)[] => { if (!topics) { return [] as string[]; } // @ts-ignore I don't want to deal with resursive nested arrays // TODO https://stackoverflow.com/questions/52638149/recursive-nested-array-type-can-it-be-done return topics .filter((topic: Topic | Topic[]) => topic === null || topic) .map((topic: Topic | Topic[]) => { if (topic === null) { return null; } if (Array.isArray(topic)) { return inTopics(topic); } return padLeft(topic, 32); }); }; export const inFilter = (options: FilterOptions) => { const result: { [key in keyof FilterOptions]: number | string | string[] | Topic[]; } = {}; if (options) { Object.keys(options).forEach(key => { switch (key) { case 'address': if (isArray(options[key])) { result[key] = (options[key] as string[]).map(inAddress); } else { result[key] = inAddress(options[key] as string); } break; case 'fromBlock': case 'toBlock': result[key] = inBlockNumber(options[key]); break; case 'limit': result[key] = inNumber10(options[key]); break; case 'topics': result[key] = inTopics(options[key]); break; default: // @ts-ignore Here, we explicitly pass down extra keys, if they exist result[key] = options[key]; } }); } return result; }; export const inHex = (str?: string) => toHex(str); export const inNumber10 = (n?: BlockNumber) => { if (isInstanceOf(n, BigNumber)) { return (n as BigNumber).toNumber(); } return new BigNumber(n || 0).toNumber(); }; export const inNumber16 = (n?: BigNumber | string | number) => { const bn = isInstanceOf(n, BigNumber) ? (n as BigNumber) : new BigNumber(n || 0); if (!bn.isInteger()) { throw new Error( `[format/input::inNumber16] the given number is not an integer: ${bn.toFormat()}` ); } return inHex(bn.toString(16)); }; export const inOptionsCondition = (condition?: Condition | null) => { if (condition) { return { block: condition.block ? inNumber10(condition.block) : null, time: condition.time ? inNumber10(Math.floor(condition.time.getTime() / 1000)) : null } as SerializedCondition; } return null; }; export const inOptions = (_options: Options = {}) => { const options = Object.assign({}, _options); const result: SerializedTransaction = {}; Object.keys(options).forEach(key => { switch (key) { case 'to': // Don't encode the `to` option if it's empty // (eg. contract deployments) if (options[key]) { result.to = inAddress(options[key]); } break; case 'from': result[key] = inAddress(options[key]); break; case 'condition': result[key] = inOptionsCondition(options[key]); break; case 'gas': case 'gasPrice': result[key] = inNumber16( new BigNumber(options[key] as BigNumber) // TODO Round number ); break; case 'value': case 'nonce': result[key] = inNumber16(options[key]); break; case 'data': result[key] = inData(options[key]); break; default: // @ts-ignore Here, we explicitly pass down extra keys, if they exist result[key] = options[key]; } }); return result; }; export const inTraceFilter = (filterObject: FilterObject) => { const result: { [key in keyof FilterObject]: string | string[] } = {}; if (filterObject) { Object.keys(filterObject).forEach(key => { switch (key) { case 'fromAddress': case 'toAddress': result[key] = ([] as string[]) .concat(filterObject[key] as string) .map(address => inAddress(address)); break; case 'toBlock': case 'fromBlock': result[key] = inBlockNumber(filterObject[key]); break; default: // @ts-ignore Here, we explicitly pass down extra keys, if they exist result[key] = filterObject[key]; } }); } return result; }; export const inTraceType = (whatTrace: string | string[]) => { if (isString(whatTrace)) { return [whatTrace]; } return whatTrace; }; export const inDeriveType = (derive?: Derive) => { return derive && (derive as DeriveObject).type === 'hard' ? 'hard' : 'soft'; }; export const inDeriveHash = (derive?: Derive | string) => { const hash = derive && (derive as DeriveObject).hash ? (derive as DeriveObject).hash : (derive as string); const type = inDeriveType(derive as Derive); return { hash: inHex(hash as string), type }; }; export const inDeriveIndex = (derive: Derive | Derive[]) => { if (!derive) { return []; } const deriveAsArray: Derive[] = isArray(derive) ? derive : [derive]; return deriveAsArray.map(item => { const index = inNumber10( item && (item as DeriveObject).index ? (item as DeriveObject).index : (item as number) ); return { index, type: inDeriveType(item) }; }); };