/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
/**
* @file index.js
* @author Fabian Vogelsteller
* @date 2017
*/
"use strict";
var core = require('web3-core');
var helpers = require('web3-core-helpers');
var Subscriptions = require('web3-core-subscriptions').subscriptions;
var Method = require('web3-core-method');
var utils = require('web3-utils');
var Net = require('web3-net');
var ENS = require('web3-eth-ens');
var Personal = require('web3-eth-personal');
var BaseContract = require('web3-eth-contract');
var Iban = require('web3-eth-iban');
var Accounts = require('web3-eth-accounts');
var abi = require('web3-eth-abi');
var getNetworkType = require('./getNetworkType.js');
var formatter = helpers.formatters;
var blockCall = function (args) {
return (typeof args[0] === 'string' && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber";
};
var transactionFromBlockCall = function (args) {
return (typeof args[0] === 'string' && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex';
};
var uncleCall = function (args) {
return (typeof args[0] === 'string' && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex';
};
var getBlockTransactionCountCall = function (args) {
return (typeof args[0] === 'string' && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber';
};
var uncleCountCall = function (args) {
return (typeof args[0] === 'string' && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber';
};
var Eth = function Eth() {
var _this = this;
// sets _requestmanager
core.packageInit(this, arguments);
// overwrite package setRequestManager
var setRequestManager = this.setRequestManager;
this.setRequestManager = function (manager) {
setRequestManager(manager);
_this.net.setRequestManager(manager);
_this.personal.setRequestManager(manager);
_this.accounts.setRequestManager(manager);
_this.Contract._requestManager = _this._requestManager;
_this.Contract.currentProvider = _this._provider;
return true;
};
// overwrite setProvider
var setProvider = this.setProvider;
this.setProvider = function () {
setProvider.apply(_this, arguments);
_this.setRequestManager(_this._requestManager);
// Set detectedAddress/lastSyncCheck back to null because the provider could be connected to a different chain now
_this.ens._detectedAddress = null;
_this.ens._lastSyncCheck = null;
};
var handleRevert = false;
var defaultAccount = null;
var defaultBlock = 'latest';
var transactionBlockTimeout = 50;
var transactionConfirmationBlocks = 24;
var transactionPollingTimeout = 750;
var blockHeaderTimeout = 10; // 10 seconds
var maxListenersWarningThreshold = 100;
var defaultChain, defaultHardfork, defaultCommon;
Object.defineProperty(this, 'handleRevert', {
get: function () {
return handleRevert;
},
set: function (val) {
handleRevert = val;
// also set on the Contract object
_this.Contract.handleRevert = handleRevert;
// update handleRevert
methods.forEach(function (method) {
method.handleRevert = handleRevert;
});
},
enumerable: true
});
Object.defineProperty(this, 'defaultCommon', {
get: function () {
return defaultCommon;
},
set: function (val) {
defaultCommon = val;
// also set on the Contract object
_this.Contract.defaultCommon = defaultCommon;
// update defaultBlock
methods.forEach(function (method) {
method.defaultCommon = defaultCommon;
});
},
enumerable: true
});
Object.defineProperty(this, 'defaultHardfork', {
get: function () {
return defaultHardfork;
},
set: function (val) {
defaultHardfork = val;
// also set on the Contract object
_this.Contract.defaultHardfork = defaultHardfork;
// update defaultBlock
methods.forEach(function (method) {
method.defaultHardfork = defaultHardfork;
});
},
enumerable: true
});
Object.defineProperty(this, 'defaultChain', {
get: function () {
return defaultChain;
},
set: function (val) {
defaultChain = val;
// also set on the Contract object
_this.Contract.defaultChain = defaultChain;
// update defaultBlock
methods.forEach(function (method) {
method.defaultChain = defaultChain;
});
},
enumerable: true
});
Object.defineProperty(this, 'transactionPollingTimeout', {
get: function () {
return transactionPollingTimeout;
},
set: function (val) {
transactionPollingTimeout = val;
// also set on the Contract object
_this.Contract.transactionPollingTimeout = transactionPollingTimeout;
// update defaultBlock
methods.forEach(function (method) {
method.transactionPollingTimeout = transactionPollingTimeout;
});
},
enumerable: true
});
Object.defineProperty(this, 'transactionConfirmationBlocks', {
get: function () {
return transactionConfirmationBlocks;
},
set: function (val) {
transactionConfirmationBlocks = val;
// also set on the Contract object
_this.Contract.transactionConfirmationBlocks = transactionConfirmationBlocks;
// update defaultBlock
methods.forEach(function (method) {
method.transactionConfirmationBlocks = transactionConfirmationBlocks;
});
},
enumerable: true
});
Object.defineProperty(this, 'transactionBlockTimeout', {
get: function () {
return transactionBlockTimeout;
},
set: function (val) {
transactionBlockTimeout = val;
// also set on the Contract object
_this.Contract.transactionBlockTimeout = transactionBlockTimeout;
// update defaultBlock
methods.forEach(function (method) {
method.transactionBlockTimeout = transactionBlockTimeout;
});
},
enumerable: true
});
Object.defineProperty(this, 'blockHeaderTimeout', {
get: function () {
return blockHeaderTimeout;
},
set: function (val) {
blockHeaderTimeout = val;
// also set on the Contract object
_this.Contract.blockHeaderTimeout = blockHeaderTimeout;
// update defaultBlock
methods.forEach(function (method) {
method.blockHeaderTimeout = blockHeaderTimeout;
});
},
enumerable: true
});
Object.defineProperty(this, 'defaultAccount', {
get: function () {
return defaultAccount;
},
set: function (val) {
if (val) {
defaultAccount = utils.toChecksumAddress(formatter.inputAddressFormatter(val));
}
// also set on the Contract object
_this.Contract.defaultAccount = defaultAccount;
_this.personal.defaultAccount = defaultAccount;
// update defaultBlock
methods.forEach(function (method) {
method.defaultAccount = defaultAccount;
});
return val;
},
enumerable: true
});
Object.defineProperty(this, 'defaultBlock', {
get: function () {
return defaultBlock;
},
set: function (val) {
defaultBlock = val;
// also set on the Contract object
_this.Contract.defaultBlock = defaultBlock;
_this.personal.defaultBlock = defaultBlock;
// update defaultBlock
methods.forEach(function (method) {
method.defaultBlock = defaultBlock;
});
return val;
},
enumerable: true
});
Object.defineProperty(this, 'maxListenersWarningThreshold', {
get: function () {
return maxListenersWarningThreshold;
},
set: function (val) {
if (_this.currentProvider && _this.currentProvider.setMaxListeners) {
maxListenersWarningThreshold = val;
_this.currentProvider.setMaxListeners(val);
}
},
enumerable: true
});
this.clearSubscriptions = _this._requestManager.clearSubscriptions.bind(_this._requestManager);
this.removeSubscriptionById = _this._requestManager.removeSubscription.bind(_this._requestManager);
// add net
this.net = new Net(this);
// add chain detection
this.net.getNetworkType = getNetworkType.bind(this);
// add accounts
this.accounts = new Accounts(this);
// add personal
this.personal = new Personal(this);
this.personal.defaultAccount = this.defaultAccount;
// set warnings threshold
this.maxListenersWarningThreshold = maxListenersWarningThreshold;
// create a proxy Contract type for this instance, as a Contract's provider
// is stored as a class member rather than an instance variable. If we do
// not create this proxy type, changing the provider in one instance of
// web3-eth would subsequently change the provider for _all_ contract
// instances!
var self = this;
var Contract = function Contract() {
BaseContract.apply(this, arguments);
// when Eth.setProvider is called, call packageInit
// on all contract instances instantiated via this Eth
// instances. This will update the currentProvider for
// the contract instances
var _this = this;
var setProvider = self.setProvider;
self.setProvider = function () {
setProvider.apply(self, arguments);
core.packageInit(_this, [self]);
};
};
Contract.setProvider = function () {
BaseContract.setProvider.apply(this, arguments);
};
// make our proxy Contract inherit from web3-eth-contract so that it has all
// the right functionality and so that instanceof and friends work properly
Contract.prototype = Object.create(BaseContract.prototype);
Contract.prototype.constructor = Contract;
// add contract
this.Contract = Contract;
this.Contract.defaultAccount = this.defaultAccount;
this.Contract.defaultBlock = this.defaultBlock;
this.Contract.transactionBlockTimeout = this.transactionBlockTimeout;
this.Contract.transactionConfirmationBlocks = this.transactionConfirmationBlocks;
this.Contract.transactionPollingTimeout = this.transactionPollingTimeout;
this.Contract.blockHeaderTimeout = this.blockHeaderTimeout;
this.Contract.handleRevert = this.handleRevert;
this.Contract._requestManager = this._requestManager;
this.Contract._ethAccounts = this.accounts;
this.Contract.currentProvider = this._requestManager.provider;
// add IBAN
this.Iban = Iban;
// add ABI
this.abi = abi;
// add ENS
this.ens = new ENS(this);
var methods = [
new Method({
name: 'getNodeInfo',
call: 'web3_clientVersion'
}),
new Method({
name: 'getProtocolVersion',
call: 'eth_protocolVersion',
params: 0
}),
new Method({
name: 'getCoinbase',
call: 'eth_coinbase',
params: 0
}),
new Method({
name: 'isMining',
call: 'eth_mining',
params: 0
}),
new Method({
name: 'getHashrate',
call: 'eth_hashrate',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'isSyncing',
call: 'eth_syncing',
params: 0,
outputFormatter: formatter.outputSyncingFormatter
}),
new Method({
name: 'getGasPrice',
call: 'eth_gasPrice',
params: 0,
outputFormatter: formatter.outputBigNumberFormatter
}),
new Method({
name: 'getFeeHistory',
call: 'eth_feeHistory',
params: 3,
inputFormatter: [utils.numberToHex, formatter.inputBlockNumberFormatter, null]
}),
new Method({
name: 'getAccounts',
call: 'eth_accounts',
params: 0,
outputFormatter: utils.toChecksumAddress
}),
new Method({
name: 'getBlockNumber',
call: 'eth_blockNumber',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getBalance',
call: 'eth_getBalance',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter],
outputFormatter: formatter.outputBigNumberFormatter
}),
new Method({
name: 'getStorageAt',
call: 'eth_getStorageAt',
params: 3,
inputFormatter: [formatter.inputAddressFormatter, utils.numberToHex, formatter.inputDefaultBlockNumberFormatter]
}),
new Method({
name: 'getCode',
call: 'eth_getCode',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter]
}),
new Method({
name: 'getBlock',
call: blockCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, function (val) { return !!val; }],
outputFormatter: formatter.outputBlockFormatter
}),
new Method({
name: 'getUncle',
call: uncleCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, utils.numberToHex],
outputFormatter: formatter.outputBlockFormatter,
}),
new Method({
name: 'getBlockTransactionCount',
call: getBlockTransactionCountCall,
params: 1,
inputFormatter: [formatter.inputBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getBlockUncleCount',
call: uncleCountCall,
params: 1,
inputFormatter: [formatter.inputBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getTransaction',
call: 'eth_getTransactionByHash',
params: 1,
inputFormatter: [null],
outputFormatter: formatter.outputTransactionFormatter
}),
new Method({
name: 'getTransactionFromBlock',
call: transactionFromBlockCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, utils.numberToHex],
outputFormatter: formatter.outputTransactionFormatter
}),
new Method({
name: 'getTransactionReceipt',
call: 'eth_getTransactionReceipt',
params: 1,
inputFormatter: [null],
outputFormatter: formatter.outputTransactionReceiptFormatter
}),
new Method({
name: 'getTransactionCount',
call: 'eth_getTransactionCount',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'sendSignedTransaction',
call: 'eth_sendRawTransaction',
params: 1,
inputFormatter: [null],
abiCoder: abi
}),
new Method({
name: 'signTransaction',
call: 'eth_signTransaction',
params: 1,
inputFormatter: [formatter.inputTransactionFormatter]
}),
new Method({
name: 'sendTransaction',
call: 'eth_sendTransaction',
params: 1,
inputFormatter: [formatter.inputTransactionFormatter],
abiCoder: abi
}),
new Method({
name: 'sign',
call: 'eth_sign',
params: 2,
inputFormatter: [formatter.inputSignFormatter, formatter.inputAddressFormatter],
transformPayload: function (payload) {
payload.params.reverse();
return payload;
}
}),
new Method({
name: 'call',
call: 'eth_call',
params: 2,
inputFormatter: [formatter.inputCallFormatter, formatter.inputDefaultBlockNumberFormatter],
abiCoder: abi
}),
new Method({
name: 'estimateGas',
call: 'eth_estimateGas',
params: 1,
inputFormatter: [formatter.inputCallFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'submitWork',
call: 'eth_submitWork',
params: 3
}),
new Method({
name: 'getWork',
call: 'eth_getWork',
params: 0
}),
new Method({
name: 'getPastLogs',
call: 'eth_getLogs',
params: 1,
inputFormatter: [formatter.inputLogFormatter],
outputFormatter: formatter.outputLogFormatter
}),
new Method({
name: 'getChainId',
call: 'eth_chainId',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'requestAccounts',
call: 'eth_requestAccounts',
params: 0,
outputFormatter: utils.toChecksumAddress
}),
new Method({
name: 'getProof',
call: 'eth_getProof',
params: 3,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputStorageKeysFormatter, formatter.inputDefaultBlockNumberFormatter],
outputFormatter: formatter.outputProofFormatter
}),
new Method({
name: 'getPendingTransactions',
call: 'eth_pendingTransactions',
params: 0,
outputFormatter: formatter.outputTransactionFormatter
}),
new Method({
name: 'createAccessList',
call: 'eth_createAccessList',
params: 2,
inputFormatter: [formatter.inputTransactionFormatter, formatter.inputDefaultBlockNumberFormatter],
}),
// subscriptions
new Subscriptions({
name: 'subscribe',
type: 'eth',
subscriptions: {
'newBlockHeaders': {
// TODO rename on RPC side?
subscriptionName: 'newHeads',
params: 0,
outputFormatter: formatter.outputBlockFormatter
},
'pendingTransactions': {
subscriptionName: 'newPendingTransactions',
params: 0
},
'logs': {
params: 1,
inputFormatter: [formatter.inputLogFormatter],
outputFormatter: formatter.outputLogFormatter,
// DUBLICATE, also in web3-eth-contract
subscriptionHandler: function (output) {
if (output.removed) {
this.emit('changed', output);
}
else {
this.emit('data', output);
}
if (typeof this.callback === 'function') {
this.callback(null, output, this);
}
}
},
'syncing': {
params: 0,
outputFormatter: formatter.outputSyncingFormatter,
subscriptionHandler: function (output) {
var _this = this;
// fire TRUE at start
if (this._isSyncing !== true) {
this._isSyncing = true;
this.emit('changed', _this._isSyncing);
if (typeof this.callback === 'function') {
this.callback(null, _this._isSyncing, this);
}
setTimeout(function () {
_this.emit('data', output);
if (typeof _this.callback === 'function') {
_this.callback(null, output, _this);
}
}, 0);
// fire sync status
}
else {
this.emit('data', output);
if (typeof _this.callback === 'function') {
this.callback(null, output, this);
}
// wait for some time before fireing the FALSE
clearTimeout(this._isSyncingTimeout);
this._isSyncingTimeout = setTimeout(function () {
if (output.currentBlock > output.highestBlock - 200) {
_this._isSyncing = false;
_this.emit('changed', _this._isSyncing);
if (typeof _this.callback === 'function') {
_this.callback(null, _this._isSyncing, _this);
}
}
}, 500);
}
}
}
}
})
];
methods.forEach(function (method) {
method.attachToObject(_this);
method.setRequestManager(_this._requestManager, _this.accounts); // second param is the eth.accounts module (necessary for signing transactions locally)
method.defaultBlock = _this.defaultBlock;
method.defaultAccount = _this.defaultAccount;
method.transactionBlockTimeout = _this.transactionBlockTimeout;
method.transactionConfirmationBlocks = _this.transactionConfirmationBlocks;
method.transactionPollingTimeout = _this.transactionPollingTimeout;
method.handleRevert = _this.handleRevert;
});
};
// Adds the static givenProvider and providers property to the Eth module
core.addProviders(Eth);
module.exports = Eth;