import { keccak256 } from 'js-sha3' ; import { BigNumber } from './BigNumber' ; import { AbiInput , AbiItem } from '../Schema' ; import { stringToUtf8Bytes } from './utf8' ; import * as errors from './errors' ; public export function hexToBytes ( hex ) { if ( typeof hex != 'string' ) throw new Error ( 'hexToBytes only accept strings, got: ' + typeof hex ) ; if ( hex . substr ( 0 , 2 ) === '0x' ) { return hexToBytes ( hex . substr ( 2 ) ) ; } const result = new Uint8Array ( Math . ceil ( hex . length / 2 ) ) ; let i = 0 ; for ( let char = 0 ; char < hex . length ; char += 2 ) { const n = parseInt ( hex . substr ( char , 2 ) , 16 ) ; if ( isNaN ( n ) ) throw new Error ( 'Cannot read hex string:' + JSON . stringify ( hex ) ) ; result [ i ] = n ; i ++ ; } return result ; } public export function bytesToHex ( bytes ) { const hashArray = Array . from ( bytes ) ; const hashHex = hashArray . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ; return hashHex ; } public export function sha3 ( value , options ? ) { if ( typeof value == 'string' ) { if ( options && options . encoding === 'hex' && typeof value == 'string' ) { let mutValue = value ; if ( mutValue . length > 2 && mutValue . substr ( 0 , 2 ) === '0x' ) { mutValue = mutValue . substr ( 2 ) ; } const t = hexToBytes ( mutValue ) ; return keccak256 ( t ) ; } else { return keccak256 ( stringToUtf8Bytes ( value ) ) ; } } return keccak256 ( value ) ; } let unitMap = { noether : '0' , wei : '1' , kwei : '1000' , Kwei : '1000' , babbage : '1000' , femtoether : '1000' , mwei : '1000000' , Mwei : '1000000' , lovelace : '1000000' , picoether : '1000000' , gwei : '1000000000' , Gwei : '1000000000' , shannon : '1000000000' , nanoether : '1000000000' , nano : '1000000000' , szabo : '1000000000000' , microether : '1000000000000' , micro : '1000000000000' , finney : '1000000000000000' , milliether : '1000000000000000' , milli : '1000000000000000' , ether : '1000000000000000000' , kether : '1000000000000000000000' , grand : '1000000000000000000000' , mether : '1000000000000000000000000' , gether : '1000000000000000000000000000' , tether : '1000000000000000000000000000000' } ; export type Unit = keyof typeof unitMap ; public export function padLeft ( str , chars , sign ? ) { return new Array ( chars - str . length + 1 ) . join ( sign ? sign : '0' ) + str ; } public export function padRight ( str , chars , sign ? ) { return str + new Array ( chars - str . length + 1 ) . join ( sign ? sign : '0' ) ; } public export function toAscii ( hex ) { let str = '' ; let i = 0 ; let l = hex . length ; if ( hex . substring ( 0 , 2 ) === '0x' ) { i = 2 ; } for ( ; i < l ; i += 2 ) { let code = parseInt ( hex . substr ( i , 2 ) , 16 ) ; str += String . fromCharCode ( code ) ; } return str ; } public export function fromAscii ( str , num = 0 ) { let hex = '' ; for ( let i = 0 ; i < str . length ; i ++ ) { let code = str . charCodeAt ( i ) ; let n = code . toString ( 16 ) ; hex += n . length < 2 ? '0' + n : n ; } return '0x' + hex . padEnd ( num , '0' ) ; } public export function transformToFullName ( json ) { if ( isObject ( json ) && json . name && json . name . indexOf ( '(' ) !== - 1 ) { return json . name ; } return json . name + '(' + _flattenTypes ( false , json . inputs || [ ] ) . join ( ',' ) + ')' ; } export function concatBytes ( ... buffers ) { const length = buffers . reduce ( ( $ , buf ) => $ + buf . length , 0 ) ; var mergedArray = new Uint8Array ( length ) ; let cursor = 0 ; for ( let buf of buffers ) { mergedArray . set ( buf , cursor ) ; cursor += buf . length ; } return mergedArray ; } method param { bool } includeTuple param { Object } puts return { Array } function _flattenTypes ( includeTuple , puts ) { const types = [ ] ; puts . forEach ( function ( param ) { if ( typeof param . components === 'object' ) { if ( param . type . substring ( 0 , 5 ) !== 'tuple' ) { throw new Error ( 'components found but type is not tuple; report on GitHub' ) ; } var suffix = '' ; var arrayBracket = param . type . indexOf ( '[' ) ; if ( arrayBracket >= 0 ) { suffix = param . type . substring ( arrayBracket ) ; } var result = _flattenTypes ( includeTuple , param . components ) ; if ( isArray ( result ) && includeTuple ) { types . push ( 'tuple(' + result . join ( ',' ) + ')' + suffix ) ; } else if ( ! includeTuple ) { types . push ( '(' + result . join ( ',' ) + ')' + suffix ) ; } else { types . push ( '(' + result + ')' ) ; } } else { types . push ( param . type ) ; } } ) ; return types ; } public export function extractDisplayName ( name ) { let stBracket = name . indexOf ( '(' ) ; let endBracket = name . indexOf ( ')' ) ; return stBracket !== - 1 && endBracket !== - 1 ? name . substr ( 0 , stBracket ) : name ; } public export function extractTypeName ( name ) { let stBracket = name . indexOf ( '(' ) ; let endBracket = name . indexOf ( ')' ) ; return stBracket !== - 1 && endBracket !== - 1 ? name . substr ( stBracket + 1 , endBracket - stBracket - 1 ) . replace ( ' ' , '' ) : '' ; } public export function isHex ( value ) { if ( typeof value === 'string' ) { return /^0x[0-9a-fA-F]+$/ . test ( value ) ; } else return false ; } public export function toNullDecimal ( value ) { if ( value === undefined || value === null ) return value ; return toBigNumber ( value ) . toNumber ( ) ; } public export function toDecimal ( value ) { return toBigNumber ( value ) . toNumber ( ) ; } public export function toString ( value ) { if ( isBigNumber ( value ) ) return ( value as BigNumber ) . toString ( 10 ) ; return '' + value ; } public export function toData ( val ) { if ( typeof val === 'string' ) { if ( ! val . startsWith ( '0x' ) && /^[A-Za-z0-9]+$/ . test ( val ) ) { return '0x' + val ; } } return toHex ( val ) ; } public export function toStringData ( val ) { if ( typeof val === 'string' ) { if ( val . startsWith ( '0x' ) && /^[A-Za-z0-9]+$/ . test ( val ) ) { return toHex ( val ) ; } return '0x' + bytesToHex ( stringToUtf8Bytes ( val ) ) ; } if ( val instanceof Uint8Array ) { return '0x' + bytesToHex ( val ) ; } throw new Error ( `toStringData: Error trying to convert ${ val } (${ typeof val }) to a hex string.` ) ; } public export function toBoolean ( value ) { if ( typeof value === 'boolean' ) return value ; return toBigNumber ( value ) . toNumber ( ) !== 0 ; } public export function fromDecimal ( value ) { let num = toBigNumber ( value ) ; let result = num . toString ( 16 ) ; return num . isLessThan ( 0 ) ? '-0x' + result . substr ( 1 ) : '0x' + result ; } public export function toHex ( val ) { if ( isBoolean ( val ) ) return fromDecimal ( + val ) ; if ( isBigNumber ( val ) ) return fromDecimal ( val ) ; if ( isString ( val ) ) { const valStr = val as string ; if ( valStr . indexOf ( '-0x' ) === 0 ) return fromDecimal ( valStr ) ; else if ( valStr . indexOf ( '0x' ) === 0 ) return valStr ; else if ( ! isFinite ( valStr as any ) ) return bytesToHex ( stringToUtf8Bytes ( valStr ) ) ; } if ( val instanceof Uint8Array ) return '0x' + bytesToHex ( val ) ; if ( isArray ( val ) || isObject ( val ) ) { throw new Error ( 'toHex can only be called with scalar values, not objects or arrays' ) ; } return fromDecimal ( val ) ; } public export function getValueOfUnit ( _unit ) { let unit = _unit ? ( _unit . toLowerCase ( ) as Unit ) : 'ether' ; let unitValue = unitMap [ unit ] ; if ( unitValue === undefined ) { throw new Error ( "This unit doesn't exists, please use the one of the following units" + JSON . stringify ( unitMap , null , 2 ) ) ; } return new BigNumber ( unitValue , 10 ) ; } public export function fromWei ( num , unit ) ; export function fromWei ( num , unit ) ; export function fromWei ( num , unit ) { let returnValue = toBigNumber ( num ) . dividedBy ( getValueOfUnit ( unit ) ) ; return isBigNumber ( num ) ? returnValue : returnValue . toString ( 10 ) ; } public export function toWei ( num , unit ) { let returnValue = toBigNumber ( num ) . times ( getValueOfUnit ( unit ) ) ; return isBigNumber ( num ) ? returnValue : returnValue . toString ( 10 ) ; } public export function toBigNumber ( _num ) { let num = _num || 0 ; if ( isBigNumber ( num ) ) { return num as BigNumber ; } if ( typeof num === 'string' ) { num = num . trim ( ) ; } if ( typeof num === 'string' && ( num . indexOf ( '0x' ) === 0 || num . indexOf ( '-0x' ) === 0 ) ) { return new BigNumber ( num . replace ( '0x' , '' ) . toLowerCase ( ) , 16 ) ; } if ( num instanceof Uint8Array ) { return new BigNumber ( bytesToHex ( num ) , 16 ) ; } return new BigNumber ( num , 10 ) ; } function bitMask ( bits ) { return new BigNumber ( new Array ( bits ) . fill ( '1' ) . join ( '' ) , 2 ) ; } public export function toTwosComplement ( num , bits = 256 ) { let bigNumber = toBigNumber ( num ) . integerValue ( ) as BigNumber ; if ( bigNumber . isLessThan ( 0 ) ) { const mask = bitMask ( bits ) ; return mask . plus ( bigNumber ) . plus ( 1 ) ; } return bigNumber ; } /**
 * Check if input value is negative in twos complement
 */ export function signedIsNegative ( value , bits ) { const binary = padLeft ( value . toString ( 2 ) , bits , '0' ) ; return binary [ 0 ] == '1' ; } public export function getAddress ( address ) { if ( typeof address !== 'string' ) { throw errors . createError ( 'invalid address' , errors . INVALID_ARGUMENT , { arg : 'address' , value : address } ) ; } if ( address . trim ( ) . match ( /^(0x)?[0-9a-fA-F]{40}$/ ) ) { if ( address . trim ( ) . substring ( 0 , 2 ) !== '0x' ) { address = '0x' + address ; } return toChecksumAddress ( address ) ; } else { throw errors . createError ( 'invalid address' , errors . INVALID_ARGUMENT , { arg : 'address' , value : address } ) ; } } public export function fromTwosComplement ( num , bits = 256 ) { if ( signedIsNegative ( num , bits ) ) { const mask = bitMask ( bits ) ; return num . minus ( mask ) . minus ( 1 ) ; } return num ; } public export function isStrictAddress ( address ) { return /^0x[0-9a-f]{40}$/i . test ( address ) ; } public export function isAddress ( address ) { if ( ! /^(0x)?[0-9a-f]{40}$/i . test ( address ) ) { return false ; } else if ( /^(0x)?[0-9a-f]{40}$/ . test ( address ) || /^(0x)?[0-9A-F]{40}$/ . test ( address ) ) { return true ; } else { return isChecksumAddress ( address ) ; } } public export function isChecksumAddress ( _address ) { const address = _address . replace ( '0x' , '' ) ; let addressHash = sha3 ( address . toLowerCase ( ) ) ; for ( let i = 0 ; i < 40 ; i ++ ) { if ( ( parseInt ( addressHash [ i ] , 16 ) > 7 && address [ i ] . toUpperCase ( ) !== address [ i ] ) || ( parseInt ( addressHash [ i ] , 16 ) <= 7 && address [ i ] . toLowerCase ( ) !== address [ i ] ) ) { return false ; } } return true ; } public export function toChecksumAddress ( _address ) { if ( typeof _address === 'undefined' ) return '' ; const address = _address . toLowerCase ( ) . replace ( '0x' , '' ) ; const addressHash = sha3 ( address ) ; let checksumAddress = '0x' ; for ( let i = 0 ; i < address . length ; i ++ ) { if ( parseInt ( addressHash [ i ] , 16 ) > 7 ) { checksumAddress += address [ i ] . toUpperCase ( ) ; } else { checksumAddress += address [ i ] ; } } return checksumAddress ; } public export function toArray ( value ) { if ( ! Array . isArray ( value ) ) { throw new Error ( `Value was not an array: ${ JSON . stringify ( value ) }` ) ; } return value ; } public export function toAddress ( address ) { if ( isStrictAddress ( address ) ) { return address ; } if ( /^[0-9a-f]{40}$/ . test ( address ) ) { return '0x' + address ; } return '0x' + padLeft ( toHex ( address ) . substr ( 2 ) , 40 ) ; } public export function isBigNumber ( object ) { return object instanceof BigNumber ; } public export function isString ( value ) { return typeof value === 'string' || ( value && value . constructor && value . constructor . name === 'String' ) ; } public export function isFunction ( object ) { return typeof object === 'function' ; } public export function isObject < T extends object > ( object ) { return object !== null && ! Array . isArray ( object ) && typeof object === 'object' ; } public export function isBoolean ( object ) { return typeof object === 'boolean' ; } public export function isArray < T extends Array < any > > ( object ) { return Array . isArray ( object ) ; } public export function isJson ( str ) { try { return ! ! JSON . parse ( str ) ; } catch ( e ) { return false ; } } public export function isBloom ( bloom ) { if ( ! /^(0x)?[0-9a-f]{512}$/i . test ( bloom ) ) { return false ; } else if ( /^(0x)?[0-9a-f]{512}$/i . test ( bloom ) ) { return true ; } return false ; } public export function isTopic ( topic ) { if ( ! /^(0x)?[0-9a-f]{64}$/i . test ( topic ) ) { return false ; } else if ( /^(0x)?[0-9a-f]{64}$/i . test ( topic ) ) { return true ; } return false ; }