621 lines
23 KiB
JavaScript
621 lines
23 KiB
JavaScript
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/**
|
|
* @file index.js
|
|
* @author Fabian Vogelsteller <fabian@ethereum.org>
|
|
* @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;
|