2018-06-13 22:39:39 +03:00
'use strict' ;
var _ _importStar = ( this && this . _ _importStar ) || function ( mod ) {
if ( mod && mod . _ _esModule ) return mod ;
var result = { } ;
if ( mod != null ) for ( var k in mod ) if ( Object . hasOwnProperty . call ( mod , k ) ) result [ k ] = mod [ k ] ;
result [ "default" ] = mod ;
return result ;
} ;
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
2018-06-14 03:02:28 +03:00
var interface _1 = require ( "./interface" ) ;
2018-06-18 12:42:41 +03:00
var provider _1 = require ( "../providers/provider" ) ;
var wallet _1 = require ( "../wallet/wallet" ) ;
2018-06-14 04:10:41 +03:00
var address _1 = require ( "../utils/address" ) ;
2018-06-17 23:47:28 +03:00
var bytes _1 = require ( "../utils/bytes" ) ;
2018-06-14 03:02:28 +03:00
var bignumber _1 = require ( "../utils/bignumber" ) ;
2018-06-13 22:39:39 +03:00
var properties _1 = require ( "../utils/properties" ) ;
var errors = _ _importStar ( require ( "../utils/errors" ) ) ;
var allowedTransactionKeys = {
data : true , from : true , gasLimit : true , gasPrice : true , nonce : true , to : true , value : true
} ;
2018-06-18 12:42:41 +03:00
// Recursively replaces ENS names with promises to resolve the name and
// stalls until all promises have returned
2018-06-14 03:02:28 +03:00
// @TODO: Expand this to resolve any promises too
function resolveAddresses ( provider , value , paramType ) {
if ( Array . isArray ( paramType ) ) {
var promises = [ ] ;
paramType . forEach ( function ( paramType , index ) {
var v = null ;
if ( Array . isArray ( value ) ) {
v = value [ index ] ;
}
else {
v = value [ paramType . name ] ;
}
promises . push ( resolveAddresses ( provider , v , paramType ) ) ;
} ) ;
return Promise . all ( promises ) ;
}
if ( paramType . type === 'address' ) {
return provider . resolveName ( value ) ;
}
if ( paramType . components ) {
return resolveAddresses ( provider , value , paramType . components ) ;
}
return Promise . resolve ( value ) ;
}
2018-06-13 22:39:39 +03:00
function runMethod ( contract , functionName , estimateOnly ) {
var method = contract . interface . functions [ functionName ] ;
return function ( ) {
var params = [ ] ;
for ( var _i = 0 ; _i < arguments . length ; _i ++ ) {
params [ _i ] = arguments [ _i ] ;
}
2018-06-18 12:42:41 +03:00
var tx = { } ;
2018-06-13 22:39:39 +03:00
// If 1 extra parameter was passed in, it contains overrides
if ( params . length === method . inputs . length + 1 && typeof ( params [ params . length - 1 ] ) === 'object' ) {
2018-06-18 12:42:41 +03:00
tx = properties _1 . shallowCopy ( params . pop ( ) ) ;
2018-06-13 22:39:39 +03:00
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
2018-06-18 12:42:41 +03:00
for ( var key in tx ) {
2018-06-13 22:39:39 +03:00
if ( ! allowedTransactionKeys [ key ] ) {
throw new Error ( 'unknown transaction override ' + key ) ;
}
}
}
if ( params . length != method . inputs . length ) {
throw new Error ( 'incorrect number of arguments' ) ;
}
// Check overrides make sense
[ 'data' , 'to' ] . forEach ( function ( key ) {
2018-06-18 12:42:41 +03:00
if ( tx [ key ] != null ) {
errors . throwError ( 'cannot override ' + key , errors . UNSUPPORTED _OPERATION , { operation : key } ) ;
2018-06-13 22:39:39 +03:00
}
} ) ;
// Send to the contract address
2018-06-18 12:42:41 +03:00
tx . to = contract . addressPromise ;
2018-06-14 03:02:28 +03:00
return resolveAddresses ( contract . provider , params , method . inputs ) . then ( function ( params ) {
2018-06-18 12:42:41 +03:00
tx . data = method . encode ( params ) ;
2018-06-14 03:02:28 +03:00
if ( method . type === 'call' ) {
// Call (constant functions) always cost 0 ether
if ( estimateOnly ) {
return Promise . resolve ( bignumber _1 . ConstantZero ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
if ( ! contract . provider ) {
errors . throwError ( 'call (constant functions) require a provider or a signer with a provider' , errors . UNSUPPORTED _OPERATION , { operation : 'call' } ) ;
}
2018-06-14 03:02:28 +03:00
// Check overrides make sense
[ 'gasLimit' , 'gasPrice' , 'value' ] . forEach ( function ( key ) {
2018-06-18 12:42:41 +03:00
if ( tx [ key ] != null ) {
2018-06-14 03:02:28 +03:00
throw new Error ( 'call cannot override ' + key ) ;
2018-06-13 22:39:39 +03:00
}
} ) ;
2018-06-18 12:42:41 +03:00
if ( tx . from == null && contract . signer ) {
tx . from = contract . signer . getAddress ( ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
return contract . provider . call ( tx ) . then ( function ( value ) {
try {
var result = method . decode ( value ) ;
if ( method . outputs . length === 1 ) {
result = result [ 0 ] ;
2018-06-14 03:02:28 +03:00
}
2018-06-18 12:42:41 +03:00
return result ;
}
catch ( error ) {
if ( value === '0x' && method . outputs . length > 0 ) {
errors . throwError ( 'call exception' , errors . CALL _EXCEPTION , {
address : contract . address ,
method : method . signature ,
value : params
} ) ;
2018-06-14 03:02:28 +03:00
}
2018-06-18 12:42:41 +03:00
throw error ;
}
2018-06-13 22:39:39 +03:00
} ) ;
}
2018-06-14 03:02:28 +03:00
else if ( method . type === 'transaction' ) {
// Only computing the transaction estimate
if ( estimateOnly ) {
2018-06-18 12:42:41 +03:00
if ( ! contract . provider ) {
errors . throwError ( 'estimate gas require a provider or a signer with a provider' , errors . UNSUPPORTED _OPERATION , { operation : 'estimateGas' } ) ;
2018-06-14 03:02:28 +03:00
}
2018-06-18 12:42:41 +03:00
if ( tx . from == null && contract . signer ) {
tx . from = contract . signer . getAddress ( ) ;
2018-06-14 03:02:28 +03:00
}
2018-06-18 12:42:41 +03:00
return contract . provider . estimateGas ( tx ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
if ( ! contract . signer ) {
errors . throwError ( 'sending a transaction require a signer' , errors . UNSUPPORTED _OPERATION , { operation : 'sendTransaction' } ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
// Make sure they aren't overriding something they shouldn't
if ( tx . from != null ) {
errors . throwError ( 'cannot override from in a transaction' , errors . UNSUPPORTED _OPERATION , { operation : 'sendTransaction' } ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
return contract . signer . sendTransaction ( tx ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-14 03:02:28 +03:00
throw new Error ( 'invalid type - ' + method . type ) ;
return null ;
} ) ;
2018-06-13 22:39:39 +03:00
} ;
}
var Contract = /** @class */ ( function ( ) {
2018-06-14 03:02:28 +03:00
// https://github.com/Microsoft/TypeScript/issues/5453
2018-06-18 12:42:41 +03:00
// Once this issue is resolved (there are open PR) we can do this nicer
// by making addressOrName default to null for 2 operand calls. :)
2018-06-13 22:39:39 +03:00
function Contract ( addressOrName , contractInterface , signerOrProvider ) {
var _this = this ;
2018-06-14 04:10:41 +03:00
errors . checkNew ( this , Contract ) ;
2018-06-13 22:39:39 +03:00
// @TODO: Maybe still check the addressOrName looks like a valid address or name?
//address = getAddress(address);
2018-06-14 03:02:28 +03:00
if ( contractInterface instanceof interface _1 . Interface ) {
2018-06-13 22:39:39 +03:00
properties _1 . defineReadOnly ( this , 'interface' , contractInterface ) ;
}
else {
2018-06-14 03:02:28 +03:00
properties _1 . defineReadOnly ( this , 'interface' , new interface _1 . Interface ( contractInterface ) ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
if ( signerOrProvider instanceof wallet _1 . Signer ) {
2018-06-14 04:10:41 +03:00
properties _1 . defineReadOnly ( this , 'provider' , signerOrProvider . provider ) ;
properties _1 . defineReadOnly ( this , 'signer' , signerOrProvider ) ;
2018-06-13 22:39:39 +03:00
}
2018-06-18 12:42:41 +03:00
else if ( signerOrProvider instanceof provider _1 . Provider ) {
2018-06-14 04:10:41 +03:00
properties _1 . defineReadOnly ( this , 'provider' , signerOrProvider ) ;
properties _1 . defineReadOnly ( this , 'signer' , null ) ;
2018-06-14 03:02:28 +03:00
}
2018-06-18 12:42:41 +03:00
else {
errors . throwError ( 'invalid signer or provider' , errors . INVALID _ARGUMENT , { arg : 'signerOrProvider' , value : signerOrProvider } ) ;
}
2018-06-13 22:39:39 +03:00
properties _1 . defineReadOnly ( this , 'estimate' , { } ) ;
properties _1 . defineReadOnly ( this , 'events' , { } ) ;
properties _1 . defineReadOnly ( this , 'functions' , { } ) ;
2018-06-14 04:10:41 +03:00
// Not connected to an on-chain instance, so do not connect functions and events
if ( ! addressOrName ) {
properties _1 . defineReadOnly ( this , 'address' , null ) ;
properties _1 . defineReadOnly ( this , 'addressPromise' , Promise . resolve ( null ) ) ;
return ;
}
2018-06-18 12:42:41 +03:00
properties _1 . defineReadOnly ( this , 'address' , addressOrName ) ;
properties _1 . defineReadOnly ( this , 'addressPromise' , this . provider . resolveName ( addressOrName ) ) ;
2018-06-13 22:39:39 +03:00
Object . keys ( this . interface . functions ) . forEach ( function ( name ) {
var run = runMethod ( _this , name , false ) ;
if ( _this [ name ] == null ) {
properties _1 . defineReadOnly ( _this , name , run ) ;
}
else {
console . log ( 'WARNING: Multiple definitions for ' + name ) ;
}
if ( _this . functions [ name ] == null ) {
properties _1 . defineReadOnly ( _this . functions , name , run ) ;
properties _1 . defineReadOnly ( _this . estimate , name , runMethod ( _this , name , true ) ) ;
}
} ) ;
Object . keys ( this . interface . events ) . forEach ( function ( eventName ) {
var eventInfo = _this . interface . events [ eventName ] ;
var eventCallback = null ;
2018-06-14 04:10:41 +03:00
var contract = _this ;
2018-06-13 22:39:39 +03:00
function handleEvent ( log ) {
2018-06-14 04:10:41 +03:00
contract . addressPromise . then ( function ( address ) {
2018-06-13 22:39:39 +03:00
// Not meant for us (the topics just has the same name)
if ( address != log . address ) {
return ;
}
try {
var result = eventInfo . decode ( log . data , log . topics ) ;
// Some useful things to have with the log
log . args = result ;
log . event = eventName ;
log . parse = eventInfo . parse ;
log . removeListener = function ( ) {
2018-06-18 12:42:41 +03:00
contract . provider . removeListener ( [ eventInfo . topic ] , handleEvent ) ;
2018-06-13 22:39:39 +03:00
} ;
2018-06-14 04:10:41 +03:00
log . getBlock = function ( ) { return contract . provider . getBlock ( log . blockHash ) ; ; } ;
log . getTransaction = function ( ) { return contract . provider . getTransaction ( log . transactionHash ) ; } ;
log . getTransactionReceipt = function ( ) { return contract . provider . getTransactionReceipt ( log . transactionHash ) ; } ;
2018-06-13 22:39:39 +03:00
log . eventSignature = eventInfo . signature ;
eventCallback . apply ( log , Array . prototype . slice . call ( result ) ) ;
}
catch ( error ) {
console . log ( error ) ;
}
} ) ;
}
var property = {
enumerable : true ,
get : function ( ) {
return eventCallback ;
} ,
set : function ( value ) {
if ( ! value ) {
value = null ;
}
2018-06-18 12:42:41 +03:00
if ( ! contract . provider ) {
errors . throwError ( 'events require a provider or a signer with a provider' , errors . UNSUPPORTED _OPERATION , { operation : 'events' } ) ;
}
2018-06-13 22:39:39 +03:00
if ( ! value && eventCallback ) {
2018-06-18 12:42:41 +03:00
contract . provider . removeListener ( [ eventInfo . topic ] , handleEvent ) ;
2018-06-13 22:39:39 +03:00
}
else if ( value && ! eventCallback ) {
2018-06-18 12:42:41 +03:00
contract . provider . on ( [ eventInfo . topic ] , handleEvent ) ;
2018-06-13 22:39:39 +03:00
}
eventCallback = value ;
}
} ;
var propertyName = 'on' + eventName . toLowerCase ( ) ;
if ( _this [ propertyName ] == null ) {
Object . defineProperty ( _this , propertyName , property ) ;
}
Object . defineProperty ( _this . events , eventName , property ) ;
} , this ) ;
}
2018-06-22 09:18:19 +03:00
Contract . prototype . fallback = function ( overrides ) {
if ( ! this . signer ) {
errors . throwError ( 'sending a transaction require a signer' , errors . UNSUPPORTED _OPERATION , { operation : 'sendTransaction(fallback)' } ) ;
}
var tx = properties _1 . shallowCopy ( overrides || { } ) ;
[ 'from' , 'to' ] . forEach ( function ( key ) {
if ( tx . to == null ) {
return ;
}
errors . throwError ( 'cannot override ' + key , errors . UNSUPPORTED _OPERATION , { operation : key } ) ;
} ) ;
tx . to = this . addressPromise ;
return this . signer . sendTransaction ( tx ) ;
} ;
Contract . prototype . callFallback = function ( overrides ) {
if ( ! this . provider ) {
errors . throwError ( 'call (constant functions) require a provider or a signer with a provider' , errors . UNSUPPORTED _OPERATION , { operation : 'call(fallback)' } ) ;
}
var tx = properties _1 . shallowCopy ( overrides || { } ) ;
[ 'to' , 'value' ] . forEach ( function ( key ) {
if ( tx . to == null ) {
return ;
}
errors . throwError ( 'cannot override ' + key , errors . UNSUPPORTED _OPERATION , { operation : key } ) ;
} ) ;
tx . to = this . addressPromise ;
return this . provider . call ( tx ) ;
} ;
2018-06-14 04:10:41 +03:00
// Reconnect to a different signer or provider
2018-06-13 22:39:39 +03:00
Contract . prototype . connect = function ( signerOrProvider ) {
return new Contract ( this . address , this . interface , signerOrProvider ) ;
} ;
2018-06-14 04:10:41 +03:00
// Deploy the contract with the bytecode, resolving to the deployed address.
// Use contract.deployTransaction.wait() to wait until the contract has
// been mined.
2018-06-13 22:39:39 +03:00
Contract . prototype . deploy = function ( bytecode ) {
2018-06-14 04:10:41 +03:00
var _this = this ;
2018-06-13 22:39:39 +03:00
var args = [ ] ;
for ( var _i = 1 ; _i < arguments . length ; _i ++ ) {
args [ _i - 1 ] = arguments [ _i ] ;
}
if ( this . signer == null ) {
throw new Error ( 'missing signer' ) ; // @TODO: errors.throwError
}
2018-06-22 09:18:19 +03:00
// A lot of common tools do not prefix bytecode with a 0x
if ( typeof ( bytecode ) === 'string' && bytecode . match ( /^[0-9a-f]*$/i ) && ( bytecode . length % 2 ) == 0 ) {
bytecode = '0x' + bytecode ;
}
2018-06-17 23:47:28 +03:00
if ( ! bytes _1 . isHexString ( bytecode ) ) {
2018-06-17 23:32:57 +03:00
errors . throwError ( 'bytecode must be a valid hex string' , errors . INVALID _ARGUMENT , { arg : 'bytecode' , value : bytecode } ) ;
}
if ( ( bytecode . length % 2 ) !== 0 ) {
errors . throwError ( 'bytecode must be valid data (even length)' , errors . INVALID _ARGUMENT , { arg : 'bytecode' , value : bytecode } ) ;
}
2018-06-13 22:39:39 +03:00
// @TODO: overrides of args.length = this.interface.deployFunction.inputs.length + 1
return this . signer . sendTransaction ( {
data : this . interface . deployFunction . encode ( bytecode , args )
2018-06-14 04:10:41 +03:00
} ) . then ( function ( tx ) {
2018-06-18 12:42:41 +03:00
var contract = new Contract ( address _1 . getContractAddress ( tx ) , _this . interface , _this . signer || _this . provider ) ;
2018-06-14 04:10:41 +03:00
properties _1 . defineReadOnly ( contract , 'deployTransaction' , tx ) ;
return contract ;
2018-06-13 22:39:39 +03:00
} ) ;
} ;
return Contract ;
} ( ) ) ;
exports . Contract = Contract ;