import * as utils from './utils/utils' ; import * as formatters from './utils/formatters' ; import * as errors from './utils/errors' ; import { coder } from './solidity/coder' ; import { RequestManager } from './RequestManager' ; import { Contract } from './Contract' ; import { AbiFunction , AbiInput , AbiOutput , Quantity } from './Schema' ; /**
 * This prototype should be used to call/sendTransaction to solidity functions
 */ export class SolidityFunction { _inputTypes : AbiInput [ ] ; _outputTypes : AbiOutput [ ] ; _constant : boolean ; _name : string ; _payable : boolean ; needsToBeTransaction : boolean ; constructor ( public json ) { this . _inputTypes = json . inputs || [ ] ; this . _outputTypes = json . outputs || [ ] ; this . _constant = ! ! json . constant ; this . _payable = ! ! json . payable || json . stateMutability === 'payable' ; this . needsToBeTransaction = this . _payable || ( 'constant' in json && ! json . constant ) || json . stateMutability === 'nonpayable' ; this . _name = utils . transformToFullName ( json ) ; } extractDefaultBlock ( args ) { if ( args . length > this . _inputTypes . length && ! utils . isObject ( args [ args . length - 1 ] ) ) { return formatters . inputDefaultBlockNumberFormatter ( args . pop ( ) ) || 'latest' ; } return 'latest' ; } param arguments validateArgs ( args ) { if ( args . some ( ( $ ) => typeof $ === 'undefined' ) ) { throw new Error ( 'Invalid call, some arguments are undefined' ) ; } let inputArgs = args . filter ( function ( a ) { return ! ( utils . isObject ( a ) === true && utils . isArray ( a ) === false && utils . isBigNumber ( a ) === false && ! ( a instanceof Uint8Array ) ) ; } ) ; if ( inputArgs . length !== this . _inputTypes . length ) { throw errors . InvalidNumberOfSolidityArgs ( inputArgs . length , this . _inputTypes . length ) ; } } param solidity param optional toPayload ( args ) { let options = { to : undefined , data : undefined , value : undefined , from : undefined } ; if ( args . length > this . _inputTypes . length && utils . isObject ( args [ args . length - 1 ] ) ) { options = args . pop ( ) ; } this . validateArgs ( args ) ; const signature = this . signature ( ) ; let params = coder . encodeParams ( this . _inputTypes , args ) ; if ( params . indexOf ( '0x' ) == 0 ) params = params . substr ( 2 ) ; options . data = '0x' + signature + params ; return options ; } /**
     * Should be used to get function signature
     */ signature ( ) { return utils . sha3 ( this . _name ) . slice ( 0 , 8 ) ; } unpackOutput ( output ) { if ( ! output ) { return ; } const encodedOutput = output . length >= 2 ? output . slice ( 2 ) : output ; let result = coder . decodeParams ( this . _outputTypes , encodedOutput ) ; return result . length === 1 ? result [ 0 ] : result ; } param requestManager async execute ( requestManager , address , ... args ) { if ( ! requestManager ) { throw new Error ( `Cannot call function ${ this . displayName ( ) } because there is no requestManager` ) ; } if ( this . needsToBeTransaction ) { const payload = this . toPayload ( args ) ; payload . to = address ; if ( payload . value > 0 && ! this . _payable ) { throw new Error ( 'Cannot send value to non-payable function' ) ; } if ( ! payload . from ) { throw new Error ( 'Missing "from" in transaction options' ) ; } const txHash = await requestManager . eth_sendTransaction ( payload ) ; return txHash ; } else { const defaultBlock = this . extractDefaultBlock ( args ) ; const payload = this . toPayload ( args ) ; payload . to = address ; const output = await requestManager . eth_call ( payload , defaultBlock ) ; return this . unpackOutput ( output ) ; } } /**
     * Should be used to estimateGas of solidity function
     */ estimateGas ( requestManager , address , ... args ) { if ( ! ( requestManager instanceof RequestManager ) ) throw new Error ( 'estimateGas needs to receive a RequestManager as first argument' ) ; let payload = this . toPayload ( args ) ; payload . to = address ; return requestManager . eth_estimateGas ( payload ) ; } /**
     * Should be used to get function display name
     */ displayName ( ) { return utils . extractDisplayName ( this . _name ) ; } /**
     * Should be used to get function type name
     */ typeName ( ) { return utils . extractTypeName ( this . _name ) || 'void' ; } param contract attachToContract ( contract ) { let displayName = this . displayName ( ) ; const execute = Object . assign ( ( ... args ) => this . execute ( contract . requestManager , contract . address , ... args ) , { estimateGas : this . estimateGas . bind ( this , contract . requestManager , contract . address ) , toPayload : ( ... args ) => this . toPayload ( args ) } ) ; if ( ! ( contract as any ) [ displayName ] ) { ; ( contract as any ) [ displayName ] = execute ; } ; ( contract as any ) [ displayName ] [ this . typeName ( ) ] = execute ; } }