packageDocumentation module import { BN , info , throwError , INVALID_ARGUMENT , arrayify , hexlify , padZeros , concat , toChecksumAddress , checkArgumentCount , checkNormalize , Arrayish , checkNew , bytesPadRight , } from '@harmony-js/crypto' ; import { hexToBN , defineReadOnly } from '@harmony-js/utils' ; hidden const NegativeOne = new BN ( - 1 ) ; hidden const One = new BN ( 1 ) ; hidden const Zero = new BN ( 0 ) ; hidden const HashZero = '0x0000000000000000000000000000000000000000000000000000000000000000' ; hidden const MaxUint256 = hexToBN ( '0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' ) ; hidden export type CoerceFunc = ( type : string , value : any ) => any ; hidden export interface ParamType { name ? : string ; type : string ; indexed ? : boolean ; components ? : any [ ] ; } hidden export interface EventFragment { type : string ; name : string ; anonymous : boolean ; inputs : ParamType [ ] ; } hidden export interface FunctionFragment { type : string ; name : string ; constant : boolean ; inputs : ParamType [ ] ; outputs : ParamType [ ] ; payable : boolean ; stateMutability : string | null ; gas : BN | null ; } hidden const paramTypeBytes = new RegExp ( /^bytes([0-9]*)$/ ) ; hidden const paramTypeNumber = new RegExp ( /^(u?int)([0-9]*)$/ ) ; hidden const paramTypeArray = new RegExp ( /^(.*)\[([0-9]*)\]$/ ) ; hidden export const defaultCoerceFunc = ( type , value ) : any => { const match = type . match ( paramTypeNumber ) ; if ( match && parseInt ( match [ 2 ] , 10 ) <= 48 ) { return value . toString ( 'hex' ) ; } return value ; } ; hidden const regexParen = new RegExp ( '^([^)(]*)\\((.*)\\)([^)(]*)$' ) ; hidden const regexIdentifier = new RegExp ( '^[A-Za-z_][A-Za-z0-9_]*$' ) ; hidden function verifyType ( type ) { if ( type . match ( /^uint($|[^1-9])/ ) ) { type = 'uint256' + type . substring ( 4 ) ; } else if ( type . match ( /^int($|[^1-9])/ ) ) { type = 'int256' + type . substring ( 3 ) ; } return type ; } hidden interface ParseState { allowArray ? : boolean ; allowName ? : boolean ; allowParams ? : boolean ; allowType ? : boolean ; readArray ? : boolean ; } hidden interface ParseNode { parent ? : any ; type ? : string ; name ? : string ; state ? : ParseState ; indexed ? : boolean ; components ? : any [ ] ; } hidden function parseParam ( param , allowIndexed ? ) { const originalParam = param ; function throwError ( i ) { throw new Error ( 'unexpected character "' + originalParam [ i ] + '" at position ' + i + ' in "' + originalParam + '"' ) ; } param = param . replace ( /\s/g , ' ' ) ; const parent = { type : '' , name : '' , state : { allowType : true } } ; let node = parent ; for ( let i = 0 ; i < param . length ; i ++ ) { const c = param [ i ] ; switch ( c ) { case '(' : if ( ! node . state || ! node . state . allowParams ) { throwError ( i ) ; } if ( node . state ) { node . state . allowType = false ; } if ( node . type ) { node . type = verifyType ( node . type ) ; } node . components = [ { type : '' , name : '' , parent : node , state : { allowType : true } } ] ; node = node . components [ 0 ] ; break ; case ')' : delete node . state ; if ( allowIndexed && node . name === 'indexed' ) { node . indexed = true ; node . name = '' ; } if ( node . type ) { node . type = verifyType ( node . type ) ; } const child = node ; node = node . parent ; if ( ! node ) { throwError ( i ) ; } delete child . parent ; if ( node . state ) { node . state . allowParams = false ; node . state . allowName = true ; node . state . allowArray = true ; } break ; case ',' : delete node . state ; if ( allowIndexed && node . name === 'indexed' ) { node . indexed = true ; node . name = '' ; } if ( node . type ) { node . type = verifyType ( node . type ) ; } const sibling = { type : '' , name : '' , parent : node . parent , state : { allowType : true } , } ; node . parent . components . push ( sibling ) ; delete node . parent ; node = sibling ; break ; case ' ' : if ( node . state ) { if ( node . state . allowType ) { if ( node . type !== '' && node . type ) { node . type = verifyType ( node . type ) ; delete node . state . allowType ; node . state . allowName = true ; node . state . allowParams = true ; } } if ( node . state . allowName ) { if ( node . name !== '' ) { if ( allowIndexed && node . name === 'indexed' ) { node . indexed = true ; node . name = '' ; } else { node . state . allowName = false ; } } } } break ; case '[' : if ( ! node . state || ! node . state . allowArray ) { throwError ( i ) ; } if ( node . state ) { node . type += c ; node . state . allowArray = false ; node . state . allowName = false ; node . state . readArray = true ; } break ; case ']' : if ( ! node . state || ! node . state . readArray ) { throwError ( i ) ; } if ( node . state ) { node . type += c ; node . state . readArray = false ; node . state . allowArray = true ; node . state . allowName = true ; } break ; default : if ( node . state ) { if ( node . state . allowType ) { node . type += c ; node . state . allowParams = true ; node . state . allowArray = true ; } else if ( node . state . allowName ) { node . name += c ; delete node . state . allowArray ; } else if ( node . state . readArray ) { node . type += c ; } else { throwError ( i ) ; } } } } if ( node . parent ) { throw new Error ( 'unexpected eof' ) ; } delete parent . state ; if ( allowIndexed && node . name === 'indexed' ) { node . indexed = true ; node . name = '' ; } if ( parent . type ) { parent . type = verifyType ( parent . type ) ; } return < ParamType > parent ; } hidden function parseSignatureEvent ( fragment ) { const abi = { anonymous : false , inputs : [ ] , name : '' , type : 'event' , } ; const match = fragment . match ( regexParen ) ; if ( ! match ) { throw new Error ( 'invalid event: ' + fragment ) ; } abi . name = match [ 1 ] . trim ( ) ; splitNesting ( match [ 2 ] ) . forEach ( ( param ) => { param = parseParam ( param , true ) ; param . indexed = ! ! param . indexed ; abi . inputs . push ( param ) ; } ) ; match [ 3 ] . split ( ' ' ) . forEach ( ( modifier ) => { switch ( modifier ) { case 'anonymous' : abi . anonymous = true ; break ; case '' : break ; default : info ( 'unknown modifier: ' + modifier ) ; } } ) ; if ( abi . name && ! abi . name . match ( regexIdentifier ) ) { throw new Error ( 'invalid identifier: "' + abi . name + '"' ) ; } return abi ; } hidden export function parseParamType ( type ) { return parseParam ( type , true ) ; } hidden export function formatParamType ( paramType ) { return getParamCoder ( defaultCoerceFunc , paramType ) . type ; } hidden function parseSignatureFunction ( fragment ) { const abi = { constant : false , gas : null , inputs : [ ] , name : '' , outputs : [ ] , payable : false , stateMutability : null , type : 'function' , } ; let comps = fragment . split ( '@' ) ; if ( comps . length !== 1 ) { if ( comps . length > 2 ) { throw new Error ( 'invalid signature' ) ; } if ( ! comps [ 1 ] . match ( /^[0-9]+$/ ) ) { throw new Error ( 'invalid signature gas' ) ; } abi . gas = new BN ( comps [ 1 ] ) ; fragment = comps [ 0 ] ; } comps = fragment . split ( ' returns ' ) ; const left = comps [ 0 ] . match ( regexParen ) ; if ( ! left ) { throw new Error ( 'invalid signature' ) ; } abi . name = left [ 1 ] . trim ( ) ; if ( ! abi . name . match ( regexIdentifier ) ) { throw new Error ( 'invalid identifier: "' + left [ 1 ] + '"' ) ; } splitNesting ( left [ 2 ] ) . forEach ( ( param ) => { abi . inputs . push ( parseParam ( param ) ) ; } ) ; left [ 3 ] . split ( ' ' ) . forEach ( ( modifier ) => { switch ( modifier ) { case 'constant' : abi . constant = true ; break ; case 'payable' : abi . payable = true ; abi . stateMutability = 'payable' ; break ; case 'pure' : abi . constant = true ; abi . stateMutability = 'pure' ; break ; case 'view' : abi . constant = true ; abi . stateMutability = 'view' ; break ; case 'external' : case 'public' : case '' : break ; default : info ( 'unknown modifier: ' + modifier ) ; } } ) ; if ( comps . length > 1 ) { const right = comps [ 1 ] . match ( regexParen ) ; if ( right === null || right [ 1 ] . trim ( ) !== '' || right [ 3 ] . trim ( ) !== '' ) { throw new Error ( 'unexpected tokens' ) ; } splitNesting ( right [ 2 ] ) . forEach ( ( param ) => { abi . outputs . push ( parseParam ( param ) ) ; } ) ; } if ( abi . name === 'constructor' ) { abi . type = 'constructor' ; if ( abi . outputs . length ) { throw new Error ( 'constructor may not have outputs' ) ; } delete abi . name ; delete abi . outputs ; } return abi ; } hidden export function formatSignature ( fragment ) { return fragment . name + '(' + fragment . inputs . map ( ( i ) => formatParamType ( i ) ) . join ( ',' ) + ')' ; } hidden export function parseSignature ( fragment ) { if ( typeof fragment === 'string' ) { fragment = fragment . replace ( /\s/g , ' ' ) ; fragment = fragment . replace ( /\(/g , ' (' ) . replace ( /\)/g , ') ' ) . replace ( /\s+/g , ' ' ) ; fragment = fragment . trim ( ) ; if ( fragment . substring ( 0 , 6 ) === 'event ' ) { return parseSignatureEvent ( fragment . substring ( 6 ) . trim ( ) ) ; } else { if ( fragment . substring ( 0 , 9 ) === 'function ' ) { fragment = fragment . substring ( 9 ) ; } return parseSignatureFunction ( fragment . trim ( ) ) ; } } throw new Error ( 'unknown signature' ) ; } hidden interface DecodedResult { consumed : number ; value : any ; } hidden abstract class Coder { readonly coerceFunc : CoerceFunc ; readonly name : string ; readonly type : string ; readonly localName ? : string ; readonly dynamic : boolean ; constructor ( coerceFunc , name , type , localName , dynamic ) { this . coerceFunc = coerceFunc ; this . name = name ; this . type = type ; this . localName = localName ; this . dynamic = dynamic ; } abstract encode ( value ) ; abstract decode ( data , offset ) ; } hidden class CoderAnonymous extends Coder { private coder : Coder ; constructor ( coder ) { super ( coder . coerceFunc , coder . name , coder . type , undefined , coder . dynamic ) ; this . coder = coder ; } encode ( value ) { return this . coder . encode ( value ) ; } decode ( data , offset ) { return this . coder . decode ( data , offset ) ; } } hidden class CoderNull extends Coder { constructor ( coerceFunc , localName ) { super ( coerceFunc , 'null' , '' , localName , false ) ; } encode ( value ) { const result = arrayify ( [ ] ) || new Uint8Array ( ) ; return result ; } decode ( data , offset ) { if ( offset > data . length ) { throw new Error ( 'invalid null' ) ; } return { consumed : 0 , value : this . coerceFunc ( 'null' , undefined ) , } ; } } hidden class CoderNumber extends Coder { readonly size : number ; readonly signed : boolean ; constructor ( coerceFunc , size , signed , localName ) { const name = ( signed ? 'int' : 'uint' ) + size * 8 ; super ( coerceFunc , name , name , localName , false ) ; this . size = size ; this . signed = signed ; } encode ( value ) { let result ; try { let v ; if ( typeof value == 'string' && value . startsWith ( '0x' ) ) { v = new BN ( value . slice ( 2 ) , 'hex' ) ; } else { v = new BN ( value ) ; } if ( this . signed ) { let bounds = MaxUint256 . maskn ( this . size * 8 - 1 ) ; if ( v . gt ( bounds ) ) { throw new Error ( 'out-of-bounds' ) ; } bounds = bounds . add ( One ) . mul ( NegativeOne ) ; if ( v . lt ( bounds ) ) { throw new Error ( 'out-of-bounds' ) ; } } else if ( v . lt ( Zero ) || v . gt ( MaxUint256 . maskn ( this . size * 8 ) ) ) { throw new Error ( 'out-of-bounds' ) ; } v = v . toTwos ( this . size * 8 ) . maskn ( this . size * 8 ) ; if ( this . signed ) { v = v . fromTwos ( this . size * 8 ) . toTwos ( 256 ) ; } const vString = v . toString ( 'hex' ) ; result = padZeros ( arrayify ( `0x${ vString }` ) || new Uint8Array ( ) , 32 ) ; } catch ( error ) { throwError ( 'invalid number value' , INVALID_ARGUMENT , { arg : this . localName , coderType : this . name , value , } ) ; } return result || padZeros ( new Uint8Array ( ) , 32 ) ; } decode ( data , offset ) { if ( data . length < offset + 32 ) { throwError ( 'insufficient data for ' + this . name + ' type' , INVALID_ARGUMENT , { arg : this . localName , coderType : this . name , value : hexlify ( data . slice ( offset , offset + 32 ) ) , } ) ; } const junkLength = 32 - this . size ; const dataValue = hexlify ( data . slice ( offset + junkLength , offset + 32 ) ) ; let value = hexToBN ( dataValue ) ; if ( this . signed ) { value = value . fromTwos ( this . size * 8 ) ; } else { value = value . maskn ( this . size * 8 ) ; } return { consumed : 32 , value : this . coerceFunc ( this . name , value ) , } ; } } hidden const uint256Coder = new CoderNumber ( ( type , value ) => { return value ; } , 32 , false , 'none' ) ; hidden class CoderBoolean extends Coder { constructor ( coerceFunc , localName ) { super ( coerceFunc , 'bool' , 'bool' , localName , false ) ; } encode ( value ) { return uint256Coder . encode ( ! ! value ? new BN ( 1 ) : new BN ( 0 ) ) ; } decode ( data , offset ) { let result ; try { result = uint256Coder . decode ( data , offset ) ; } catch ( error ) { if ( error . reason === 'insufficient data for uint256 type' ) { throwError ( 'insufficient data for boolean type' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'boolean' , value : error . value , } ) ; } throw error ; } return { consumed : result . consumed , value : this . coerceFunc ( 'bool' , ! result . value . isZero ( ) ) , } ; } } hidden class CoderFixedBytes extends Coder { readonly length : number ; constructor ( coerceFunc , length , localName ) { const name = 'bytes' + length ; super ( coerceFunc , name , name , localName , false ) ; this . length = length ; } encode ( value ) { const result = new Uint8Array ( this . length ) ; try { const arrayied = arrayify ( value ) ; let data = null ; if ( arrayied !== null ) { const valueToByte = hexlify ( arrayied ) ; data = arrayify ( bytesPadRight ( valueToByte , this . length ) ) ; } else { throw new Error ( 'cannot arraify data' ) ; } if ( data === null || data . length !== this . length ) { throw new Error ( 'incorrect data length' ) ; } result . set ( data ) ; } catch ( error ) { throwError ( 'invalid ' + this . name + ' value' , INVALID_ARGUMENT , { arg : this . localName , coderType : this . name , value : error . value || value , } ) ; } return result ; } decode ( data , offset ) { if ( data . length < offset + 32 ) { throwError ( 'insufficient data for ' + name + ' type' , INVALID_ARGUMENT , { arg : this . localName , coderType : this . name , value : hexlify ( data . slice ( offset , offset + 32 ) ) , } ) ; } return { consumed : 32 , value : this . coerceFunc ( this . name , hexlify ( data . slice ( offset , offset + this . length ) ) ) , } ; } } hidden class CoderAddress extends Coder { constructor ( coerceFunc , localName ) { super ( coerceFunc , 'address' , 'address' , localName , false ) ; } encode ( value ) { const result = new Uint8Array ( 32 ) ; try { const addr = arrayify ( toChecksumAddress ( value ) ) || new Uint8Array ( ) ; result . set ( addr , 12 ) ; } catch ( error ) { throwError ( 'invalid address' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'address' , value , } ) ; } return result ; } decode ( data , offset ) { if ( data . length < offset + 32 ) { throwError ( 'insufficuent data for address type' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'address' , value : hexlify ( data . slice ( offset , offset + 32 ) ) , } ) ; } return { consumed : 32 , value : this . coerceFunc ( 'address' , toChecksumAddress ( hexlify ( data . slice ( offset + 12 , offset + 32 ) ) ) ) , } ; } } hidden function _encodeDynamicBytes ( value ) { const dataLength = 32 * Math . ceil ( value . length / 32 ) ; const padding = new Uint8Array ( dataLength - value . length ) ; return concat ( [ uint256Coder . encode ( new BN ( value . length ) ) , value , padding ] ) ; } hidden function _decodeDynamicBytes ( data , offset , localName ) { if ( data . length < offset + 32 ) { throwError ( 'insufficient data for dynamicBytes length' , INVALID_ARGUMENT , { arg : localName , coderType : 'dynamicBytes' , value : hexlify ( data . slice ( offset , offset + 32 ) ) , } ) ; } let length = uint256Coder . decode ( data , offset ) . value ; try { length = length . toNumber ( ) ; } catch ( error ) { throwError ( 'dynamic bytes count too large' , INVALID_ARGUMENT , { arg : localName , coderType : 'dynamicBytes' , value : length . toString ( ) , } ) ; } if ( data . length < offset + 32 + length ) { throwError ( 'insufficient data for dynamicBytes type' , INVALID_ARGUMENT , { arg : localName , coderType : 'dynamicBytes' , value : hexlify ( data . slice ( offset , offset + 32 + length ) ) , } ) ; } return { consumed : 32 + 32 * Math . ceil ( length / 32 ) , value : data . slice ( offset + 32 , offset + 32 + length ) , } ; } hidden class CoderDynamicBytes extends Coder { constructor ( coerceFunc , localName ) { super ( coerceFunc , 'bytes' , 'bytes' , localName , true ) ; } encode ( value ) { let result = new Uint8Array ( ) ; try { result = _encodeDynamicBytes ( arrayify ( value ) || new Uint8Array ( ) ) ; } catch ( error ) { throwError ( 'invalid bytes value' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'bytes' , value : error . value , } ) ; } return result ; } decode ( data , offset ) { const result = _decodeDynamicBytes ( data , offset , this . localName || '' ) ; result . value = this . coerceFunc ( 'bytes' , hexlify ( result . value ) ) ; return result ; } } hidden class CoderString extends Coder { constructor ( coerceFunc , localName ) { super ( coerceFunc , 'string' , 'string' , localName , true ) ; } encode ( value ) { if ( typeof value !== 'string' ) { throwError ( 'invalid string value' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'string' , value , } ) ; } return _encodeDynamicBytes ( toUtf8Bytes ( value ) ) ; } decode ( data , offset ) { const result = _decodeDynamicBytes ( data , offset , this . localName || '' ) ; result . value = this . coerceFunc ( 'string' , toUtf8String ( result . value ) ) ; return result ; } } hidden function alignSize ( size ) { return 32 * Math . ceil ( size / 32 ) ; } hidden function pack ( coders , values ) { if ( Array . isArray ( values ) ) { } else if ( values && typeof values === 'object' ) { const arrayValues = [ ] ; coders . forEach ( ( coder ) => { arrayValues . push ( ( < any > values ) [ coder . localName || '' ] ) ; } ) ; values = arrayValues ; } else { throwError ( 'invalid tuple value' , INVALID_ARGUMENT , { coderType : 'tuple' , value : values , } ) ; } if ( coders . length !== values . length ) { throwError ( 'types/value length mismatch' , INVALID_ARGUMENT , { coderType : 'tuple' , value : values , } ) ; } const parts = [ ] ; coders . forEach ( ( coder , index ) => { parts . push ( { dynamic : coder . dynamic , value : coder . encode ( values [ index ] ) } ) ; } ) ; let staticSize = 0 ; let dynamicSize = 0 ; parts . forEach ( ( part ) => { if ( part . dynamic ) { staticSize += 32 ; dynamicSize += alignSize ( part . value . length ) ; } else { staticSize += alignSize ( part . value . length ) ; } } ) ; let offset = 0 ; let dynamicOffset = staticSize ; const data = new Uint8Array ( staticSize + dynamicSize ) ; parts . forEach ( ( part ) => { if ( part . dynamic ) { data . set ( uint256Coder . encode ( new BN ( dynamicOffset ) ) , offset ) ; offset += 32 ; data . set ( part . value , dynamicOffset ) ; dynamicOffset += alignSize ( part . value . length ) ; } else { data . set ( part . value , offset ) ; offset += alignSize ( part . value . length ) ; } } ) ; return data ; } hidden function unpack ( coders , data , offset ) { const baseOffset = offset ; let consumed = 0 ; const value = [ ] ; coders . forEach ( ( coder ) => { let result ; if ( coder . dynamic ) { const dynamicOffset = uint256Coder . decode ( data , offset ) ; result = coder . decode ( data , baseOffset + dynamicOffset . value . toNumber ( ) ) ; result . consumed = dynamicOffset . consumed ; } else { result = coder . decode ( data , offset ) ; } if ( result . value !== undefined ) { value . push ( result . value ) ; } offset += result . consumed ; consumed += result . consumed ; } ) ; coders . forEach ( ( coder , index ) => { let name = coder . localName ; if ( ! name ) { return ; } if ( name === 'length' ) { name = '_length' ; } if ( value [ name ] != null ) { return ; } value [ name ] = value [ index ] ; } ) ; return { value , consumed , } ; } hidden class CoderArray extends Coder { readonly coder : Coder ; readonly length : number ; constructor ( coerceFunc , coder , length , localName ) { const type = coder . type + '[' + ( length >= 0 ? length : '' ) + ']' ; const dynamic = length === - 1 || coder . dynamic ; super ( coerceFunc , 'array' , type , localName , dynamic ) ; this . coder = coder ; this . length = length ; } encode ( value ) { if ( ! Array . isArray ( value ) ) { throwError ( 'expected array value' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'array' , value , } ) ; } let count = this . length ; let result = new Uint8Array ( 0 ) ; if ( count === - 1 ) { count = value . length ; result = uint256Coder . encode ( new BN ( count ) ) ; } checkArgumentCount ( count , value . length , ' in coder array' + ( this . localName ? ' ' + this . localName : '' ) ) ; const coders = [ ] ; for ( let i = 0 ; i < value . length ; i ++ ) { coders . push ( this . coder ) ; } return concat ( [ result , pack ( coders , value ) ] ) ; } decode ( data , offset ) { let consumed = 0 ; let count = this . length ; let decodedLength = { consumed : 0 , value : undefined } ; if ( count === - 1 ) { try { decodedLength = uint256Coder . decode ( data , offset ) ; } catch ( error ) { throwError ( 'insufficient data for dynamic array length' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'array' , value : error . value , } ) ; } try { count = decodedLength . value . toNumber ( ) ; } catch ( error ) { throwError ( 'array count too large' , INVALID_ARGUMENT , { arg : this . localName , coderType : 'array' , value : decodedLength . value . toString ( ) , } ) ; } consumed += decodedLength . consumed ; offset += decodedLength . consumed ; } const coders = [ ] ; for ( let i = 0 ; i < count ; i ++ ) { coders . push ( new CoderAnonymous ( this . coder ) ) ; } const result = unpack ( coders , data , offset ) ; result . consumed += consumed ; result . value = this . coerceFunc ( this . type , result . value ) ; return result ; } } hidden class CoderTuple extends Coder { readonly coders : Coder [ ] ; constructor ( coerceFunc , coders , localName ) { let dynamic = false ; const types = [ ] ; coders . forEach ( ( coder ) => { if ( coder . dynamic ) { dynamic = true ; } types . push ( coder . type ) ; } ) ; const type = 'tuple(' + types . join ( ',' ) + ')' ; super ( coerceFunc , 'tuple' , type , localName , dynamic ) ; this . coders = coders ; } encode ( value ) { return pack ( this . coders , value ) ; } decode ( data , offset ) { const result = unpack ( this . coders , data , offset ) ; result . value = this . coerceFunc ( this . type , result . value ) ; return result ; } } hidden function splitNesting ( value ) { value = value . trim ( ) ; const result = [ ] ; let accum = '' ; let depth = 0 ; for ( let offset = 0 ; offset < value . length ; offset ++ ) { const c = value [ offset ] ; if ( c === ',' && depth === 0 ) { result . push ( accum ) ; accum = '' ; } else { accum += c ; if ( c === '(' ) { depth ++ ; } else if ( c === ')' ) { depth -- ; if ( depth === - 1 ) { throw new Error ( 'unbalanced parenthsis' ) ; } } } } if ( accum ) { result . push ( accum ) ; } return result ; } hidden const paramTypeSimple = { address : CoderAddress , bool : CoderBoolean , string : CoderString , bytes : CoderDynamicBytes , } ; hidden function getTupleParamCoder ( coerceFunc , components , localName ) { if ( ! components ) { components = [ ] ; } const coders = [ ] ; components . forEach ( ( component ) => { coders . push ( getParamCoder ( coerceFunc , component ) ) ; } ) ; return new CoderTuple ( coerceFunc , coders , localName ) ; } hidden function getParamCoder ( coerceFunc , param ) { const coder = paramTypeSimple [ param . type ] ; if ( coder ) { return new coder ( coerceFunc , param . name ) ; } const matcher = param . type . match ( paramTypeNumber ) ; if ( matcher ) { const size = parseInt ( matcher [ 2 ] || '256' , 10 ) ; if ( size === 0 || size > 256 || size % 8 !== 0 ) { throwError ( 'invalid ' + matcher [ 1 ] + ' bit length' , INVALID_ARGUMENT , { arg : 'param' , value : param , } ) ; } return new CoderNumber ( coerceFunc , size / 8 , matcher [ 1 ] === 'int' , param . name || '' ) ; } const matcher2 = param . type . match ( paramTypeBytes ) ; if ( matcher2 ) { const size = parseInt ( matcher2 [ 1 ] , 10 ) ; if ( size === 0 || size > 32 ) { throwError ( 'invalid bytes length' , INVALID_ARGUMENT , { arg : 'param' , value : param , } ) ; } return new CoderFixedBytes ( coerceFunc , size , param . name || '' ) ; } const matcher3 = param . type . match ( paramTypeArray ) ; if ( matcher3 ) { const size = parseInt ( matcher3 [ 2 ] || '-1' , 10 ) ; param = shallowCopy ( param ) ; param . type = matcher3 [ 1 ] ; param = deepCopy ( param ) ; return new CoderArray ( coerceFunc , getParamCoder ( coerceFunc , param ) , size , param . name || '' ) ; } if ( param . type . substring ( 0 , 5 ) === 'tuple' ) { return getTupleParamCoder ( coerceFunc , param . components || [ ] , param . name || '' ) ; } if ( param . type === '' ) { return new CoderNull ( coerceFunc , param . name || '' ) ; } throwError ( 'invalid type' , INVALID_ARGUMENT , { arg : 'type' , value : param . type , } ) ; } hidden export enum UnicodeNormalizationForm { current = '' , NFC = 'NFC' , NFD = 'NFD' , NFKC = 'NFKC' , NFKD = 'NFKD' } hidden export function toUtf8Bytes ( str , form = UnicodeNormalizationForm . current ) { if ( form !== UnicodeNormalizationForm . current ) { checkNormalize ( ) ; str = str . normalize ( form ) ; } const result = [ ] ; for ( let i = 0 ; i < str . length ; i ++ ) { let c = str . charCodeAt ( i ) ; if ( c < 0x80 ) { result . push ( c ) ; } else if ( c < 0x800 ) { result . push ( ( c >> 6 ) | 0xc0 ) ; result . push ( ( c & 0x3f ) | 0x80 ) ; } else if ( ( c & 0xfc00 ) === 0xd800 ) { i ++ ; const c2 = str . charCodeAt ( i ) ; if ( i >= str . length || ( c2 & 0xfc00 ) !== 0xdc00 ) { throw new Error ( 'invalid utf-8 string' ) ; } c = 0x10000 + ( ( c & 0x03ff ) << 10 ) + ( c2 & 0x03ff ) ; result . push ( ( c >> 18 ) | 0xf0 ) ; result . push ( ( ( c >> 12 ) & 0x3f ) | 0x80 ) ; result . push ( ( ( c >> 6 ) & 0x3f ) | 0x80 ) ; result . push ( ( c & 0x3f ) | 0x80 ) ; } else { result . push ( ( c >> 12 ) | 0xe0 ) ; result . push ( ( ( c >> 6 ) & 0x3f ) | 0x80 ) ; result . push ( ( c & 0x3f ) | 0x80 ) ; } } return arrayify ( result ) || new Uint8Array ( ) ; } hidden export function toUtf8String ( bytes , ignoreErrors ? ) { bytes = arrayify ( bytes ) || new Uint8Array ( ) ; let result = '' ; let i = 0 ; while ( i < bytes . length ) { const c = bytes [ i ++ ] ; if ( c >> 7 === 0 ) { result += String . fromCharCode ( c ) ; continue ; } let extraLength = null ; let overlongMask = null ; if ( ( c & 0xe0 ) === 0xc0 ) { extraLength = 1 ; overlongMask = 0x7f ; } else if ( ( c & 0xf0 ) === 0xe0 ) { extraLength = 2 ; overlongMask = 0x7ff ; } else if ( ( c & 0xf8 ) === 0xf0 ) { extraLength = 3 ; overlongMask = 0xffff ; } else { if ( ! ignoreErrors ) { if ( ( c & 0xc0 ) === 0x80 ) { throw new Error ( 'invalid utf8 byte sequence; unexpected continuation byte' ) ; } throw new Error ( 'invalid utf8 byte sequence; invalid prefix' ) ; } continue ; } if ( i + extraLength > bytes . length ) { if ( ! ignoreErrors ) { throw new Error ( 'invalid utf8 byte sequence; too short' ) ; } for ( ; i < bytes . length ; i ++ ) { if ( bytes [ i ] >> 6 !== 0x02 ) { break ; } } continue ; } let res = c & ( ( 1 << ( 8 - extraLength - 1 ) ) - 1 ) ; for ( let j = 0 ; j < extraLength ; j ++ ) { const nextChar = bytes [ i ] ; if ( ( nextChar & 0xc0 ) !== 0x80 ) { res = null ; break ; } res = ( res << 6 ) | ( nextChar & 0x3f ) ; i ++ ; } if ( res === null ) { if ( ! ignoreErrors ) { throw new Error ( 'invalid utf8 byte sequence; invalid continuation byte' ) ; } continue ; } if ( res <= overlongMask ) { if ( ! ignoreErrors ) { throw new Error ( 'invalid utf8 byte sequence; overlong' ) ; } continue ; } if ( res > 0x10ffff ) { if ( ! ignoreErrors ) { throw new Error ( 'invalid utf8 byte sequence; out-of-range' ) ; } continue ; } if ( res >= 0xd800 && res <= 0xdfff ) { if ( ! ignoreErrors ) { throw new Error ( 'invalid utf8 byte sequence; utf-16 surrogate' ) ; } continue ; } if ( res <= 0xffff ) { result += String . fromCharCode ( res ) ; continue ; } res -= 0x10000 ; result += String . fromCharCode ( ( ( res >> 10 ) & 0x3ff ) + 0xd800 , ( res & 0x3ff ) + 0xdc00 ) ; } return result ; } hidden export function formatBytes32String ( text ) { const bytes = toUtf8Bytes ( text ) ; if ( bytes . length > 31 ) { throw new Error ( 'bytes32 string must be less than 32 bytes' ) ; } return hexlify ( concat ( [ bytes , HashZero ] ) . slice ( 0 , 32 ) ) ; } hidden export function parseBytes32String ( bytes ) { const data = arrayify ( bytes ) || new Uint8Array ( ) ; if ( data . length !== 32 ) { throw new Error ( 'invalid bytes32 - not 32 bytes long' ) ; } if ( data [ 31 ] !== 0 ) { throw new Error ( 'invalid bytes32 sdtring - no null terminator' ) ; } let length = 31 ; while ( data [ length - 1 ] === 0 ) { length -- ; } return toUtf8String ( data . slice ( 0 , length ) ) ; } hidden export function isType ( object , type ) { return object && object . _ethersType === type ; } hidden export function shallowCopy ( object ) { const result = { } ; for ( const key in object ) { result [ key ] = object [ key ] ; } return result ; } hidden const opaque = { boolean : true , number : true , string : true , } ; hidden export function deepCopy ( object , frozen ? ) { if ( object === undefined || object === null || opaque [ typeof object ] ) { return object ; } if ( Array . isArray ( object ) ) { const result = object . map ( ( item ) => deepCopy ( item , frozen ) ) ; if ( frozen ) { Object . freeze ( result ) ; } return result ; } if ( typeof object === 'object' ) { if ( isType ( object , 'BigNumber' ) ) { return object ; } if ( isType ( object , 'Description' ) ) { return object ; } if ( isType ( object , 'Indexed' ) ) { return object ; } const result = { } ; for ( const key in object ) { const value = object [ key ] ; if ( value === undefined ) { continue ; } defineReadOnly ( result , key , deepCopy ( value , frozen ) ) ; } if ( frozen ) { Object . freeze ( result ) ; } return result ; } if ( typeof object === 'function' ) { return object ; } throw new Error ( 'Cannot deepCopy ' + typeof object ) ; } hidden export class AbiCoder { coerceFunc : CoerceFunc ; constructor ( coerceFunc ? ) { checkNew ( this , AbiCoder ) ; if ( ! coerceFunc ) { coerceFunc = defaultCoerceFunc ; } this . coerceFunc = coerceFunc ; } encode ( types , values ) { if ( types . length !== values . length ) { throwError ( 'types/values length mismatch' , INVALID_ARGUMENT , { count : { types : types . length , values : values . length } , value : { types , values } , } ) ; } const coders = [ ] ; types . forEach ( ( type ) => { let typeObject = null ; if ( typeof type === 'string' ) { typeObject = parseParam ( type ) ; } else { typeObject = type ; } coders . push ( getParamCoder ( this . coerceFunc , typeObject ) ) ; } , this ) ; const encodedArray = new CoderTuple ( this . coerceFunc , coders , '_' ) . encode ( values ) ; return hexlify ( encodedArray ) ; } decode ( types , data ) { const coders = [ ] ; types . forEach ( ( type ) => { let typeObject = null ; if ( typeof type === 'string' ) { typeObject = parseParam ( type ) ; } else { typeObject = deepCopy ( type ) ; } coders . push ( getParamCoder ( this . coerceFunc , typeObject ) ) ; } , this ) ; const result = new CoderTuple ( this . coerceFunc , coders , '_' ) . decode ( arrayify ( data ) || new Uint8Array ( ) , 0 ) . value ; return result ; } } hidden export const defaultAbiCoder = new AbiCoder ( ) ;