Compare commits

..

15 Commits

Author SHA1 Message Date
Richard Moore
be0488a1a0 Updated dist files. 2018-11-08 16:03:33 -05:00
Richard Moore
28a52cd485 Fix for when blockTag is specified as a null equivalent value in contract overrides (#329). 2018-11-08 16:01:48 -05:00
Richard Moore
3a19f43844 Added "debug" event for providers; do not depend on the format as it may change, but this should help debugging in most cases (#320). 2018-11-08 15:59:30 -05:00
Richard Moore
4852e837d2 Fix for Kovan filters without an address (#339). 2018-11-08 15:56:35 -05:00
Richard Moore
fa68385cfe Updated dist files. 2018-10-14 19:05:38 -04:00
Richard Moore
d54609a458 Fixed lingering polling timer when no events left to process in a provider. 2018-10-14 19:01:53 -04:00
Richard Moore
f682861e0b Fixed utils.poll from mutating passed variables. 2018-10-14 19:01:09 -04:00
Richard Moore
023a20ff47 Fixed and refactored populating transaction values for signers (#306). 2018-10-14 19:00:15 -04:00
Richard Moore
e39cd84923 Fixed test cases for phantomjs (must use ES3 syntax). 2018-10-13 17:27:19 -04:00
Richard Moore
5020897f10 Updated dist files. 2018-10-13 17:19:07 -04:00
Richard Moore
6ac2d923b7 Fixed filtering with null non-indexed parameters (#305). 2018-10-13 17:17:02 -04:00
Richard Moore
6996dd86f4 Updated dist files. 2018-10-11 16:50:52 -04:00
Richard Moore
493273d698 Added optional blockTag to call; note that this may not behave as expected on all nodes (#226). 2018-10-11 16:03:18 -04:00
Richard Moore
84344ac4c2 Check all transaction parameters are valid; protect against typos (#299). 2018-10-11 15:16:31 -04:00
Richard Moore
9b118af304 Updated dist files. 2018-10-07 01:13:10 -04:00
41 changed files with 814 additions and 366 deletions

2
_version.d.ts vendored
View File

@@ -1 +1 @@
export declare const version = "4.0.3";
export declare const version = "4.0.8";

View File

@@ -1,3 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "4.0.3";
exports.version = "4.0.8";

View File

@@ -112,9 +112,14 @@ function runMethod(contract, functionName, estimateOnly) {
params[_i] = arguments[_i];
}
var tx = {};
var blockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
tx = properties_1.shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
@@ -154,7 +159,7 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx).then(function (value) {
return contract.provider.call(tx, blockTag).then(function (value) {
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {

303
dist/ethers.js vendored
View File

@@ -1,7 +1,7 @@
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ethers = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.version = "4.0.3";
exports.version = "4.0.8";
},{}],2:[function(require,module,exports){
"use strict";
@@ -160,9 +160,14 @@ function runMethod(contract, functionName, estimateOnly) {
params[_i] = arguments[_i];
}
var tx = {};
var blockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof (params[params.length - 1]) === 'object') {
tx = properties_1.shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
if (!allowedTransactionKeys[key]) {
@@ -202,7 +207,7 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from == null && contract.signer) {
tx.from = contract.signer.getAddress();
}
return contract.provider.call(tx).then(function (value) {
return contract.provider.call(tx, blockTag).then(function (value) {
if ((bytes_1.hexDataLength(value) % 32) === 4 && bytes_1.hexDataSlice(value, 0, 4) === '0x08c379a0') {
var reason = abi_coder_1.defaultAbiCoder.decode(['string'], bytes_1.hexDataSlice(value, 4));
errors.throwError('call revert exception', errors.CALL_EXCEPTION, {
@@ -10213,12 +10218,12 @@ function getEventTag(eventName) {
return 'address:' + address_1.getAddress(eventName);
}
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
}
else if (bytes_1.hexDataLength(eventName) === 32) {
if (bytes_1.hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
}
else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -10279,8 +10284,7 @@ var BaseProvider = /** @class */ (function (_super) {
if (_this._lastBlockNumber === -2) {
_this._lastBlockNumber = blockNumber - 1;
}
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
var _loop_1 = function (i) {
if (_this._emitted.block < i) {
_this._emitted.block = i;
// Evict any transaction hashes or block hashes over 12 blocks
@@ -10295,6 +10299,10 @@ var BaseProvider = /** @class */ (function (_super) {
});
}
_this.emit('block', i);
};
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
_loop_1(i);
}
// Sweep balances and remove addresses we no longer have events for
var newBalances = {};
@@ -10331,14 +10339,16 @@ var BaseProvider = /** @class */ (function (_super) {
break;
}
case 'filter': {
var address = comps[1];
var topics = deserializeTopics(comps[2]);
var filter_1 = {
address: address,
address: comps[1],
fromBlock: _this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
};
if (!filter_1.address) {
delete filter_1.address;
}
_this.getLogs(filter_1).then(function (logs) {
if (logs.length === 0) {
return;
@@ -10590,13 +10600,14 @@ var BaseProvider = /** @class */ (function (_super) {
};
return result;
};
BaseProvider.prototype.call = function (transaction) {
BaseProvider.prototype.call = function (transaction, blockTag) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
return this.ready.then(function () {
return properties_1.resolveProperties(tx).then(function (tx) {
return properties_1.resolveProperties({ blockTag: blockTag, tx: tx }).then(function (_a) {
var blockTag = _a.blockTag, tx = _a.tx;
return _this._resolveNames(tx, ['to', 'from']).then(function (tx) {
var params = { transaction: checkTransactionRequest(tx) };
var params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return _this.perform('call', params).then(function (result) {
return bytes_1.hexlify(result);
});
@@ -10630,12 +10641,12 @@ var BaseProvider = /** @class */ (function (_super) {
return properties_1.resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(function (_a) {
var blockHashOrBlockTag = _a.blockHashOrBlockTag;
try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) {
var blockHash_1 = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash_1) === 32) {
return web_1.poll(function () {
return _this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then(function (block) {
return _this.perform('getBlock', { blockHash: blockHash_1, includeTransactions: !!includeTransactions }).then(function (block) {
if (block == null) {
if (_this._emitted['b:' + blockHash] == null) {
if (_this._emitted['b:' + blockHash_1] == null) {
return null;
}
return undefined;
@@ -10932,6 +10943,9 @@ var BaseProvider = /** @class */ (function (_super) {
result = true;
return !(event.once);
});
if (this.listenerCount() === 0) {
this.polling = false;
}
return result;
};
BaseProvider.prototype.listenerCount = function (eventName) {
@@ -11105,41 +11119,52 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
var get = function (url, procFunc) {
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
_this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: _this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getResult);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
return get(url).catch(function (error) {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -11166,33 +11191,39 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&boolean=false';
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
return get(url);
case 'call': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'estimateGas':
return get(url);
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
@@ -11222,7 +11253,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += apiKey;
var self = this;
return web_1.fetchJson(url, null, getResult).then(function (logs) {
return get(url, getResult).then(function (logs) {
var txs = {};
var seq = Promise.resolve();
logs.forEach(function (log) {
@@ -11251,7 +11282,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += '/api?module=stats&action=ethprice';
url += apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
return get(url, getResult).then(function (result) {
return parseFloat(result.ethusd);
});
default:
@@ -11261,6 +11292,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
};
// @TODO: Allow startBlock and endBlock to be Promises
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -11278,6 +11310,12 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
_this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: _this
});
var output = [];
result.forEach(function (tx) {
['contractAddress', 'to'].forEach(function (key) {
@@ -11527,13 +11565,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var base_provider_1 = require("./base-provider");
var abstract_signer_1 = require("../abstract-signer");
var errors = __importStar(require("../errors"));
var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var transaction_1 = require("../utils/transaction");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
var errors = __importStar(require("../errors"));
function timer(timeout) {
return new Promise(function (resolve) {
setTimeout(function () {
@@ -11584,14 +11623,6 @@ var JsonRpcSigner = /** @class */ (function (_super) {
}
return _this;
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
JsonRpcSigner.prototype.getAddress = function () {
var _this = this;
if (this._address) {
@@ -11613,20 +11644,18 @@ var JsonRpcSigner = /** @class */ (function (_super) {
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then(function (address) {
if (!address) {
return null;
}
return address.toLowerCase();
});
}
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
}
return properties_1.resolveProperties(tx).then(function (tx) {
return _this.provider.send('eth_sendTransaction', [JsonRpcProvider.hexlifyTransaction(tx)]).then(function (hash) {
// Once populateTransaction resolves, the from address will be populated from getAddress
var from = null;
var getAddress = this.getAddress().then(function (address) {
if (address) {
from = address.toLowerCase();
}
return from;
});
return transaction_1.populateTransaction(transaction, this.provider, getAddress).then(function (tx) {
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = from;
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
@@ -11678,6 +11707,9 @@ var JsonRpcSigner = /** @class */ (function (_super) {
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
var JsonRpcProvider = /** @class */ (function (_super) {
__extends(JsonRpcProvider, _super);
function JsonRpcProvider(url, network) {
@@ -11730,13 +11762,22 @@ var JsonRpcProvider = /** @class */ (function (_super) {
});
};
JsonRpcProvider.prototype.send = function (method, params) {
var _this = this;
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult);
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
_this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: _this
});
return result;
});
};
JsonRpcProvider.prototype.perform = function (method, params) {
switch (method) {
@@ -11783,9 +11824,9 @@ var JsonRpcProvider = /** @class */ (function (_super) {
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
case 'call':
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest']);
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction)]);
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
@@ -11843,10 +11884,21 @@ var JsonRpcProvider = /** @class */ (function (_super) {
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
JsonRpcProvider.hexlifyTransaction = function (transaction) {
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
// Check only allowed properties are given
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (var key in allowExtra) {
if (allowExtra[key]) {
allowed[key] = true;
}
}
}
properties_1.checkProperties(transaction, allowed);
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
if (transaction[key] == null) {
return;
@@ -11869,7 +11921,7 @@ var JsonRpcProvider = /** @class */ (function (_super) {
}(base_provider_1.BaseProvider));
exports.JsonRpcProvider = JsonRpcProvider;
},{"../abstract-signer":2,"../errors":5,"../utils/address":59,"../utils/bytes":62,"../utils/networks":70,"../utils/properties":72,"../utils/utf8":83,"../utils/web":84,"./base-provider":50}],57:[function(require,module,exports){
},{"../abstract-signer":2,"../errors":5,"../utils/address":59,"../utils/bytes":62,"../utils/networks":70,"../utils/properties":72,"../utils/transaction":81,"../utils/utf8":83,"../utils/web":84,"./base-provider":50}],57:[function(require,module,exports){
'use strict';
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
@@ -13615,6 +13667,7 @@ var HDNode = /** @class */ (function () {
properties_1.defineReadOnly(this, 'keyPair', new secp256k1_1.KeyPair(privateKey));
properties_1.defineReadOnly(this, 'privateKey', this.keyPair.privateKey);
properties_1.defineReadOnly(this, 'publicKey', this.keyPair.compressedPublicKey);
properties_1.defineReadOnly(this, 'address', secp256k1_1.computeAddress(this.publicKey));
properties_1.defineReadOnly(this, 'chainCode', bytes_1.hexlify(chainCode));
properties_1.defineReadOnly(this, 'index', index);
properties_1.defineReadOnly(this, 'depth', depth);
@@ -13890,6 +13943,7 @@ exports.randomBytes = random_bytes_1.randomBytes;
var networks_1 = require("./networks");
exports.getNetwork = networks_1.getNetwork;
var properties_1 = require("./properties");
exports.checkProperties = properties_1.checkProperties;
exports.deepCopy = properties_1.deepCopy;
exports.defineReadOnly = properties_1.defineReadOnly;
exports.resolveProperties = properties_1.resolveProperties;
@@ -13905,8 +13959,10 @@ exports.verifyMessage = secp256k1_1.verifyMessage;
var signing_key_1 = require("./signing-key");
exports.SigningKey = signing_key_1.SigningKey;
var transaction_1 = require("./transaction");
exports.parseTransaction = transaction_1.parse;
exports.serializeTransaction = transaction_1.serialize;
exports.populateTransaction = transaction_1.populateTransaction;
var transaction_2 = require("./transaction");
exports.parseTransaction = transaction_2.parse;
exports.serializeTransaction = transaction_2.serialize;
var utf8_1 = require("./utf8");
exports.formatBytes32String = utf8_1.formatBytes32String;
exports.parseBytes32String = utf8_1.parseBytes32String;
@@ -14060,15 +14116,17 @@ var _EventDescription = /** @class */ (function (_super) {
topics.push(this.topic);
}
params.forEach(function (arg, index) {
if (arg === null) {
topics.push(null);
return;
}
var param = _this.inputs[index];
if (!param.indexed) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
if (arg != null) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
}
return;
}
if (param.type === 'string') {
if (arg == null) {
topics.push(null);
}
else if (param.type === 'string') {
topics.push(hash_1.id(arg));
}
else if (param.type === 'bytes') {
@@ -14527,7 +14585,15 @@ exports.pbkdf2 = pbkdf2;
},{"../utils/bytes":62,"./hmac":65}],72:[function(require,module,exports){
'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 });
var errors = __importStar(require("../errors"));
function defineReadOnly(object, name, value) {
Object.defineProperty(object, name, {
enumerable: true,
@@ -14566,6 +14632,24 @@ function resolveProperties(object) {
});
}
exports.resolveProperties = resolveProperties;
function checkProperties(object, properties) {
if (!object || typeof (object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach(function (key) {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
exports.checkProperties = checkProperties;
function shallowCopy(object) {
var result = {};
for (var key in object) {
@@ -14639,7 +14723,7 @@ function inheritable(parent) {
}
exports.inheritable = inheritable;
},{}],73:[function(require,module,exports){
},{"../errors":5}],73:[function(require,module,exports){
(function (global){
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
@@ -15502,7 +15586,9 @@ var address_1 = require("./address");
var bignumber_1 = require("./bignumber");
var bytes_1 = require("./bytes");
var keccak256_1 = require("./keccak256");
var properties_1 = require("./properties");
var RLP = __importStar(require("./rlp"));
var abstract_provider_1 = require("../providers/abstract-provider");
///////////////////////////////
function handleAddress(value) {
if (value === '0x') {
@@ -15524,7 +15610,11 @@ var transactionFields = [
{ name: 'value', maxLength: 32 },
{ name: 'data' },
];
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
function serialize(transaction, signature) {
properties_1.checkProperties(transaction, allowedTransactionKeys);
var raw = [];
transactionFields.forEach(function (fieldInfo) {
var value = transaction[fieldInfo.name] || ([]);
@@ -15627,8 +15717,37 @@ function parse(rawTransaction) {
return tx;
}
exports.parse = parse;
function populateTransaction(transaction, provider, from) {
if (!abstract_provider_1.Provider.isProvider(provider)) {
errors.throwError('missing provider', errors.INVALID_ARGUMENT, {
argument: 'provider',
value: provider
});
}
properties_1.checkProperties(transaction, allowedTransactionKeys);
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = provider.getTransactionCount(from);
}
if (tx.gasLimit == null) {
var estimate = properties_1.shallowCopy(tx);
estimate.from = from;
tx.gasLimit = provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = provider.getNetwork().then(function (network) { return network.chainId; });
}
return properties_1.resolveProperties(tx);
}
exports.populateTransaction = populateTransaction;
},{"../constants":3,"../errors":5,"./address":59,"./bignumber":61,"./bytes":62,"./keccak256":69,"./rlp":74,"./secp256k1":75}],82:[function(require,module,exports){
},{"../constants":3,"../errors":5,"../providers/abstract-provider":49,"./address":59,"./bignumber":61,"./bytes":62,"./keccak256":69,"./properties":72,"./rlp":74,"./secp256k1":75}],82:[function(require,module,exports){
'use strict';
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
@@ -16002,6 +16121,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var xmlhttprequest_1 = require("xmlhttprequest");
var base64_1 = require("./base64");
var properties_1 = require("./properties");
var utf8_1 = require("./utf8");
var errors = __importStar(require("../errors"));
function fetchJson(connection, json, processFunc) {
@@ -16137,6 +16257,7 @@ function poll(func, options) {
if (!options) {
options = {};
}
options = properties_1.shallowCopy(options);
if (options.floor == null) {
options.floor = 0;
}
@@ -16203,7 +16324,7 @@ function poll(func, options) {
}
exports.poll = poll;
},{"../errors":5,"./base64":60,"./utf8":83,"xmlhttprequest":48}],85:[function(require,module,exports){
},{"../errors":5,"./base64":60,"./properties":72,"./utf8":83,"xmlhttprequest":48}],85:[function(require,module,exports){
(function (global){
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
@@ -16346,7 +16467,7 @@ var Wallet = /** @class */ (function (_super) {
return properties_1.resolveProperties(transaction).then(function (tx) {
var rawTx = transaction_1.serialize(tx);
var signature = _this.signingKey.signDigest(keccak256_1.keccak256(rawTx));
return Promise.resolve(transaction_1.serialize(tx, signature));
return transaction_1.serialize(tx, signature);
});
};
Wallet.prototype.signMessage = function (message) {
@@ -16365,30 +16486,12 @@ var Wallet = /** @class */ (function (_super) {
return this.provider.getTransactionCount(this.address, blockTag);
};
Wallet.prototype.sendTransaction = function (transaction) {
if (!this.provider) {
throw new Error('missing provider');
}
if (!transaction || typeof (transaction) !== 'object') {
throw new Error('invalid transaction object');
}
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasLimit == null) {
tx.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(tx);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then(function (network) { return network.chainId; });
}
return this.provider.sendTransaction(this.sign(tx));
var _this = this;
return transaction_1.populateTransaction(transaction, this.provider, this.address).then(function (tx) {
return _this.sign(tx).then(function (signedTransaction) {
return _this.provider.sendTransaction(signedTransaction);
});
});
};
Wallet.prototype.encrypt = function (password, options, progressCallback) {
if (typeof (options) === 'function' && !progressCallback) {

2
dist/ethers.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

25
dist/ethers.types.txt vendored
View File

@@ -152,7 +152,7 @@ declare module 'ethers/wallet' {
* Static methods to create Wallet instances.
*/
static createRandom(options?: any): Wallet;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet>;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet>;
static fromMnemonic(mnemonic: string, path?: string, wordlist?: Wordlist): Wallet;
}
}
@@ -219,10 +219,11 @@ declare module 'ethers/utils' {
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from 'ethers/utils/solidity';
import { randomBytes } from 'ethers/utils/random-bytes';
import { getNetwork } from 'ethers/utils/networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from 'ethers/utils/properties';
import * as RLP from 'ethers/utils/rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from 'ethers/utils/secp256k1';
import { SigningKey } from 'ethers/utils/signing-key';
import { populateTransaction } from 'ethers/utils/transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from 'ethers/utils/transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from 'ethers/utils/utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from 'ethers/utils/units';
@@ -238,7 +239,7 @@ declare module 'ethers/utils' {
import { ConnectionInfo, OnceBlockable, PollOptions } from 'ethers/utils/web';
import { EncryptOptions, ProgressCallback } from 'ethers/utils/secret-storage';
import { Wordlist } from 'ethers/utils/wordlist';
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, populateTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
}
declare module 'ethers/wordlists' {
@@ -260,7 +261,7 @@ declare module 'ethers/utils/shims' {
}
declare module 'ethers/_version' {
export const version = "4.0.3";
export const version = "4.0.8";
}
declare module 'ethers/utils/bignumber' {
@@ -412,6 +413,7 @@ declare module 'ethers/utils/transaction' {
import { BigNumber } from 'ethers/utils/bignumber';
import { Arrayish, Signature } from 'ethers/utils/bytes';
import { BigNumberish } from 'ethers/utils/bignumber';
import { Provider } from 'ethers/providers/abstract-provider';
export type UnsignedTransaction = {
to?: string;
nonce?: number;
@@ -437,6 +439,7 @@ declare module 'ethers/utils/transaction' {
}
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string;
export function parse(rawTransaction: Arrayish): Transaction;
export function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction>;
}
declare module 'ethers/providers/abstract-provider' {
@@ -523,7 +526,7 @@ declare module 'ethers/providers/abstract-provider' {
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;
@@ -622,6 +625,7 @@ declare module 'ethers/utils/hdnode' {
export class HDNode {
readonly privateKey: string;
readonly publicKey: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
readonly chainCode: string;
@@ -740,7 +744,7 @@ declare module 'ethers/providers/base-provider' {
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
call(transaction: TransactionRequest): Promise<string>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;
@@ -838,7 +842,11 @@ declare module 'ethers/providers/json-rpc-provider' {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}
}
@@ -931,6 +939,9 @@ declare module 'ethers/utils/properties' {
export function setType(object: any, type: string): void;
export function isType(object: any, type: string): boolean;
export function resolveProperties(object: any): Promise<any>;
export function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export function shallowCopy(object: any): any;
export function deepCopy(object: any, frozen?: boolean): any;
export function inheritable(parent: any): (child: any) => void;

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.3",
"version": "4.0.8",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.3",
"version": "4.0.8",
"description": "Ethereum wallet library.",
"main": "./index.js",
"types": "./index.d.ts",
@@ -53,7 +53,7 @@
"vinyl-source-stream": "^2.0.0",
"web3-providers-http": "1.0.0-beta.35"
},
"browser": "./dist/ethers.js",
"browser": "./dist/ethers.min.js",
"keywords": [
"ethereum",
"library",

View File

@@ -81,7 +81,7 @@ export declare abstract class Provider implements OnceBlockable {
abstract getCode(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
abstract getTransaction(transactionHash: string): Promise<TransactionResponse>;

View File

@@ -44,7 +44,7 @@ export declare class BaseProvider extends Provider {
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse;
call(transaction: TransactionRequest): Promise<string>;
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;
getTransaction(transactionHash: string): Promise<TransactionResponse>;

View File

@@ -375,12 +375,12 @@ function getEventTag(eventName) {
return 'address:' + address_1.getAddress(eventName);
}
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
}
else if (bytes_1.hexDataLength(eventName) === 32) {
if (bytes_1.hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
}
else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -441,8 +441,7 @@ var BaseProvider = /** @class */ (function (_super) {
if (_this._lastBlockNumber === -2) {
_this._lastBlockNumber = blockNumber - 1;
}
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
var _loop_1 = function (i) {
if (_this._emitted.block < i) {
_this._emitted.block = i;
// Evict any transaction hashes or block hashes over 12 blocks
@@ -457,6 +456,10 @@ var BaseProvider = /** @class */ (function (_super) {
});
}
_this.emit('block', i);
};
// Notify all listener for each block that has passed
for (var i = _this._lastBlockNumber + 1; i <= blockNumber; i++) {
_loop_1(i);
}
// Sweep balances and remove addresses we no longer have events for
var newBalances = {};
@@ -493,14 +496,16 @@ var BaseProvider = /** @class */ (function (_super) {
break;
}
case 'filter': {
var address = comps[1];
var topics = deserializeTopics(comps[2]);
var filter_1 = {
address: address,
address: comps[1],
fromBlock: _this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
};
if (!filter_1.address) {
delete filter_1.address;
}
_this.getLogs(filter_1).then(function (logs) {
if (logs.length === 0) {
return;
@@ -752,13 +757,14 @@ var BaseProvider = /** @class */ (function (_super) {
};
return result;
};
BaseProvider.prototype.call = function (transaction) {
BaseProvider.prototype.call = function (transaction, blockTag) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
return this.ready.then(function () {
return properties_1.resolveProperties(tx).then(function (tx) {
return properties_1.resolveProperties({ blockTag: blockTag, tx: tx }).then(function (_a) {
var blockTag = _a.blockTag, tx = _a.tx;
return _this._resolveNames(tx, ['to', 'from']).then(function (tx) {
var params = { transaction: checkTransactionRequest(tx) };
var params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return _this.perform('call', params).then(function (result) {
return bytes_1.hexlify(result);
});
@@ -792,12 +798,12 @@ var BaseProvider = /** @class */ (function (_super) {
return properties_1.resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(function (_a) {
var blockHashOrBlockTag = _a.blockHashOrBlockTag;
try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) {
var blockHash_1 = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash_1) === 32) {
return web_1.poll(function () {
return _this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then(function (block) {
return _this.perform('getBlock', { blockHash: blockHash_1, includeTransactions: !!includeTransactions }).then(function (block) {
if (block == null) {
if (_this._emitted['b:' + blockHash] == null) {
if (_this._emitted['b:' + blockHash_1] == null) {
return null;
}
return undefined;
@@ -1094,6 +1100,9 @@ var BaseProvider = /** @class */ (function (_super) {
result = true;
return !(event.once);
});
if (this.listenerCount() === 0) {
this.polling = false;
}
return result;
};
BaseProvider.prototype.listenerCount = function (eventName) {

View File

@@ -112,41 +112,52 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
apiKey += '&apikey=' + this.apiKey;
}
var get = function (url, procFunc) {
return web_1.fetchJson(url, null, procFunc || getJsonResult).then(function (result) {
_this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: _this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getResult);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
return get(url).catch(function (error) {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -173,33 +184,39 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&boolean=false';
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
return get(url);
case 'call': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'estimateGas':
return get(url);
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
}
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
@@ -229,7 +246,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += apiKey;
var self = this;
return web_1.fetchJson(url, null, getResult).then(function (logs) {
return get(url, getResult).then(function (logs) {
var txs = {};
var seq = Promise.resolve();
logs.forEach(function (log) {
@@ -258,7 +275,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
}
url += '/api?module=stats&action=ethprice';
url += apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
return get(url, getResult).then(function (result) {
return parseFloat(result.ethusd);
});
default:
@@ -268,6 +285,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
};
// @TODO: Allow startBlock and endBlock to be Promises
EtherscanProvider.prototype.getHistory = function (addressOrName, startBlock, endBlock) {
var _this = this;
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -285,6 +303,12 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '&endblock=' + endBlock;
url += '&sort=asc' + apiKey;
return web_1.fetchJson(url, null, getResult).then(function (result) {
_this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: _this
});
var output = [];
result.forEach(function (tx) {
['contractAddress', 'to'].forEach(function (key) {

View File

@@ -27,5 +27,9 @@ export declare class JsonRpcProvider extends BaseProvider {
perform(method: string, params: any): Promise<any>;
protected _startPending(): void;
protected _stopPending(): void;
static hexlifyTransaction(transaction: TransactionRequest): any;
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: {
[key: string]: boolean;
}): {
[key: string]: string;
};
}

View File

@@ -20,13 +20,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC
var base_provider_1 = require("./base-provider");
var abstract_signer_1 = require("../abstract-signer");
var errors = __importStar(require("../errors"));
var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes");
var networks_1 = require("../utils/networks");
var properties_1 = require("../utils/properties");
var transaction_1 = require("../utils/transaction");
var utf8_1 = require("../utils/utf8");
var web_1 = require("../utils/web");
var errors = __importStar(require("../errors"));
function timer(timeout) {
return new Promise(function (resolve) {
setTimeout(function () {
@@ -77,14 +78,6 @@ var JsonRpcSigner = /** @class */ (function (_super) {
}
return _this;
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
JsonRpcSigner.prototype.getAddress = function () {
var _this = this;
if (this._address) {
@@ -106,20 +99,18 @@ var JsonRpcSigner = /** @class */ (function (_super) {
};
JsonRpcSigner.prototype.sendTransaction = function (transaction) {
var _this = this;
var tx = properties_1.shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then(function (address) {
if (!address) {
return null;
}
return address.toLowerCase();
});
}
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
}
return properties_1.resolveProperties(tx).then(function (tx) {
return _this.provider.send('eth_sendTransaction', [JsonRpcProvider.hexlifyTransaction(tx)]).then(function (hash) {
// Once populateTransaction resolves, the from address will be populated from getAddress
var from = null;
var getAddress = this.getAddress().then(function (address) {
if (address) {
from = address.toLowerCase();
}
return from;
});
return transaction_1.populateTransaction(transaction, this.provider, getAddress).then(function (tx) {
var hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = from;
return _this.provider.send('eth_sendTransaction', [hexTx]).then(function (hash) {
return web_1.poll(function () {
return _this.provider.getTransaction(hash).then(function (tx) {
if (tx === null) {
@@ -171,6 +162,9 @@ var JsonRpcSigner = /** @class */ (function (_super) {
return JsonRpcSigner;
}(abstract_signer_1.Signer));
exports.JsonRpcSigner = JsonRpcSigner;
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
var JsonRpcProvider = /** @class */ (function (_super) {
__extends(JsonRpcProvider, _super);
function JsonRpcProvider(url, network) {
@@ -223,13 +217,22 @@ var JsonRpcProvider = /** @class */ (function (_super) {
});
};
JsonRpcProvider.prototype.send = function (method, params) {
var _this = this;
var request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult);
return web_1.fetchJson(this.connection, JSON.stringify(request), getResult).then(function (result) {
_this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: _this
});
return result;
});
};
JsonRpcProvider.prototype.perform = function (method, params) {
switch (method) {
@@ -276,9 +279,9 @@ var JsonRpcProvider = /** @class */ (function (_super) {
case 'getTransactionReceipt':
return this.send('eth_getTransactionReceipt', [params.transactionHash]);
case 'call':
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest']);
return this.send('eth_call', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]);
case 'estimateGas':
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction)]);
return this.send('eth_estimateGas', [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true })]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
params.filter.address = getLowerCase(params.filter.address);
@@ -336,10 +339,21 @@ var JsonRpcProvider = /** @class */ (function (_super) {
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
JsonRpcProvider.hexlifyTransaction = function (transaction) {
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
JsonRpcProvider.hexlifyTransaction = function (transaction, allowExtra) {
// Check only allowed properties are given
var allowed = properties_1.shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (var key in allowExtra) {
if (allowExtra[key]) {
allowed[key] = true;
}
}
}
properties_1.checkProperties(transaction, allowed);
var result = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function (key) {
if (transaction[key] == null) {
return;

View File

@@ -1 +1 @@
export const version = "4.0.3";
export const version = "4.0.8";

View File

@@ -16,7 +16,7 @@ import { UnsignedTransaction } from './utils/transaction';
///////////////////////////////
// Imported Abstracts
import { Provider } from './providers/abstract-provider';
import { BlockTag, Provider } from './providers/abstract-provider';
import { Signer } from './abstract-signer';
///////////////////////////////
@@ -148,14 +148,22 @@ type RunFunction = (...params: Array<any>) => Promise<any>;
function runMethod(contract: Contract, functionName: string, estimateOnly: boolean): RunFunction {
let method = contract.interface.functions[functionName];
return function(...params): Promise<any> {
var tx: any = {}
let tx: any = {}
let blockTag: BlockTag = null;
// If 1 extra parameter was passed in, it contains overrides
if (params.length === method.inputs.length + 1 && typeof(params[params.length - 1]) === 'object') {
tx = shallowCopy(params.pop());
if (tx.blockTag != null) {
blockTag = tx.blockTag;
}
delete tx.blockTag;
// Check for unexpected keys (e.g. using "gas" instead of "gasLimit")
for (var key in tx) {
for (let key in tx) {
if (!allowedTransactionKeys[key]) {
throw new Error('unknown transaction override ' + key);
}
@@ -202,7 +210,7 @@ function runMethod(contract: Contract, functionName: string, estimateOnly: boole
tx.from = contract.signer.getAddress()
}
return contract.provider.call(tx).then((value) => {
return contract.provider.call(tx, blockTag).then((value) => {
if ((hexDataLength(value) % 32) === 4 && hexDataSlice(value, 0, 4) === '0x08c379a0') {
let reason = defaultAbiCoder.decode([ 'string' ], hexDataSlice(value, 4));

View File

@@ -125,7 +125,7 @@ export abstract class Provider implements OnceBlockable {
abstract getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>;
abstract call(transaction: TransactionRequest): Promise<string>;
abstract call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>;
abstract estimateGas(transaction: TransactionRequest): Promise<BigNumber>;
abstract getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block>;

View File

@@ -38,10 +38,10 @@ import { Network, Networkish } from '../utils/networks';
// @TODO: not any?
function check(format: any, object: any): any {
var result: any = {};
for (var key in format) {
let result: any = {};
for (let key in format) {
try {
var value = format[key](object[key]);
let value = format[key](object[key]);
if (value !== undefined) { result[key] = value; }
} catch (error) {
error.checkKey = key;
@@ -72,7 +72,7 @@ function arrayOf(check: CheckFunc): CheckFunc {
return (function(array: any): Array<any> {
if (!Array.isArray(array)) { throw new Error('not an array'); }
var result: any = [];
let result: any = [];
array.forEach(function(value) {
result.push(check(value));
@@ -204,7 +204,7 @@ function checkTransactionResponse(transaction: any): TransactionResponse {
// Very loose providers (e.g. TestRPC) don't provide a signature or raw
if (transaction.v && transaction.r && transaction.s) {
var raw = [
let raw = [
stripZeros(hexlify(transaction.nonce)),
stripZeros(hexlify(transaction.gasPrice)),
stripZeros(hexlify(transaction.gasLimit)),
@@ -221,9 +221,9 @@ function checkTransactionResponse(transaction: any): TransactionResponse {
}
var result = check(formatTransaction, transaction);
let result = check(formatTransaction, transaction);
var networkId = transaction.networkId;
let networkId = transaction.networkId;
if (isHexString(networkId)) {
networkId = bigNumberify(networkId).toNumber();
@@ -324,7 +324,7 @@ function checkTransactionReceipt(transactionReceipt: any): TransactionReceipt {
//var status = transactionReceipt.status;
//var root = transactionReceipt.root;
var result: TransactionReceipt = check(formatTransactionReceipt, transactionReceipt);
let result: TransactionReceipt = check(formatTransactionReceipt, transactionReceipt);
result.logs.forEach(function(entry, index) {
if (entry.transactionLogIndex == null) {
entry.transactionLogIndex = index;
@@ -425,13 +425,14 @@ function getEventTag(eventName: EventType): string {
eventName = eventName.toLowerCase();
if (eventName === 'block' || eventName === 'pending' || eventName === 'error') {
return eventName;
} else if (hexDataLength(eventName) === 32) {
if (hexDataLength(eventName) === 32) {
return 'tx:' + eventName;
}
if (eventName.indexOf(':') === -1) {
return eventName;
}
} else if (Array.isArray(eventName)) {
return 'filter::' + serializeTopics(eventName);
@@ -550,7 +551,7 @@ export class BaseProvider extends Provider {
if (this._lastBlockNumber === -2) { this._lastBlockNumber = blockNumber - 1; }
// Notify all listener for each block that has passed
for (var i = this._lastBlockNumber + 1; i <= blockNumber; i++) {
for (let i = this._lastBlockNumber + 1; i <= blockNumber; i++) {
if (this._emitted.block < i) {
this._emitted.block = i;
@@ -568,7 +569,7 @@ export class BaseProvider extends Provider {
}
// Sweep balances and remove addresses we no longer have events for
var newBalances: any = {};
let newBalances: any = {};
// Find all transaction hashes we are waiting on
this._events.forEach((event) => {
@@ -591,7 +592,7 @@ export class BaseProvider extends Provider {
newBalances[address] = this._balances[address];
}
this.getBalance(address, 'latest').then(function(balance) {
var lastBalance = this._balances[address];
let lastBalance = this._balances[address];
if (lastBalance && balance.eq(lastBalance)) { return; }
this._balances[address] = balance;
this.emit(address, balance);
@@ -601,14 +602,14 @@ export class BaseProvider extends Provider {
}
case 'filter': {
let address = comps[1];
let topics = deserializeTopics(comps[2]);
let filter = {
address: address,
address: comps[1],
fromBlock: this._lastBlockNumber + 1,
toBlock: blockNumber,
topics: topics
}
if (!filter.address) { delete filter.address; }
this.getLogs(filter).then((logs) => {
if (logs.length === 0) { return; }
logs.forEach((log: Log) => {
@@ -751,7 +752,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = { address: address, blockTag: checkBlockTag(blockTag) };
let params = { address: address, blockTag: checkBlockTag(blockTag) };
return this.perform('getBalance', params).then((result) => {
return bigNumberify(result);
});
@@ -764,7 +765,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = { address: address, blockTag: checkBlockTag(blockTag) };
let params = { address: address, blockTag: checkBlockTag(blockTag) };
return this.perform('getTransactionCount', params).then((result) => {
return bigNumberify(result).toNumber();
});
@@ -777,7 +778,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(({ addressOrName, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = {address: address, blockTag: checkBlockTag(blockTag)};
let params = {address: address, blockTag: checkBlockTag(blockTag)};
return this.perform('getCode', params).then((result) => {
return hexlify(result);
});
@@ -790,7 +791,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ addressOrName: addressOrName, position: position, blockTag: blockTag }).then(({ addressOrName, position, blockTag }) => {
return this.resolveName(addressOrName).then((address) => {
var params = {
let params = {
address: address,
blockTag: checkBlockTag(blockTag),
position: hexStripZeros(hexlify(position)),
@@ -806,7 +807,7 @@ export class BaseProvider extends Provider {
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse> {
return this.ready.then(() => {
return resolveProperties({ signedTransaction: signedTransaction }).then(({ signedTransaction }) => {
var params = { signedTransaction: hexlify(signedTransaction) };
let params = { signedTransaction: hexlify(signedTransaction) };
return this.perform('sendTransaction', params).then((hash) => {
return this._wrapTransaction(parseTransaction(signedTransaction), hash);
}, function (error) {
@@ -850,12 +851,12 @@ export class BaseProvider extends Provider {
}
call(transaction: TransactionRequest): Promise<string> {
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string> {
let tx: TransactionRequest = shallowCopy(transaction);
return this.ready.then(() => {
return resolveProperties(tx).then((tx) => {
return resolveProperties({ blockTag: blockTag, tx: tx }).then(({ blockTag, tx }) => {
return this._resolveNames(tx, [ 'to', 'from' ]).then((tx) => {
var params = { transaction: checkTransactionRequest(tx) };
let params = { blockTag: checkBlockTag(blockTag), transaction: checkTransactionRequest(tx) };
return this.perform('call', params).then((result) => {
return hexlify(result);
});
@@ -876,7 +877,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties(tx).then((tx) => {
return this._resolveNames(tx, [ 'to', 'from' ]).then((tx) => {
var params = { transaction: checkTransactionRequest(tx) };
let params = { transaction: checkTransactionRequest(tx) };
return this.perform('estimateGas', params).then((result) => {
return bigNumberify(result);
});
@@ -889,7 +890,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(({ blockHashOrBlockTag }) => {
try {
var blockHash = hexlify(blockHashOrBlockTag);
let blockHash = hexlify(blockHashOrBlockTag);
if (hexDataLength(blockHash) === 32) {
return poll(() => {
return this.perform('getBlock', { blockHash: blockHash, includeTransactions: !!includeTransactions }).then((block) => {
@@ -1013,7 +1014,7 @@ export class BaseProvider extends Provider {
return this.ready.then(() => {
return resolveProperties(filter).then((filter) => {
return this._resolveNames(filter, ['address']).then((filter) => {
var params = { filter: checkFilter(filter) };
let params = { filter: checkFilter(filter) };
return this.perform('getLogs', params).then((result) => {
return arrayOf(checkLog)(result);
});
@@ -1033,9 +1034,9 @@ export class BaseProvider extends Provider {
// @TODO: Could probably use resolveProperties instead?
private _resolveNames(object: any, keys: Array<string>): Promise<{ [key: string]: string }> {
var promises: Array<Promise<void>> = [];
let promises: Array<Promise<void>> = [];
var result: { [key: string ]: string } = shallowCopy(object);
let result: { [key: string ]: string } = shallowCopy(object);
keys.forEach(function(key) {
if (result[key] == null) { return; }
@@ -1062,8 +1063,8 @@ export class BaseProvider extends Provider {
}
// keccak256('resolver(bytes32)')
var data = '0x0178b8bf' + namehash(name).substring(2);
var transaction = { to: network.ensAddress, data: data };
let data = '0x0178b8bf' + namehash(name).substring(2);
let transaction = { to: network.ensAddress, data: data };
return this.call(transaction).then((data) => {
@@ -1088,22 +1089,22 @@ export class BaseProvider extends Provider {
return Promise.resolve(getAddress(name));
} catch (error) { }
var self = this;
let self = this;
var nodeHash = namehash(name);
let nodeHash = namehash(name);
// Get the addr from the resovler
return this._getResolver(name).then(function(resolverAddress) {
// keccak256('addr(bytes32)')
var data = '0x3b3b57de' + nodeHash.substring(2);
var transaction = { to: resolverAddress, data: data };
let data = '0x3b3b57de' + nodeHash.substring(2);
let transaction = { to: resolverAddress, data: data };
return self.call(transaction);
// extract the address from the data
}).then(function(data) {
if (hexDataLength(data) !== 32) { return null; }
var address = getAddress(hexDataSlice(data, 12));
let address = getAddress(hexDataSlice(data, 12));
if (address === '0x0000000000000000000000000000000000000000') { return null; }
return address;
});
@@ -1118,17 +1119,17 @@ export class BaseProvider extends Provider {
address = getAddress(address);
var name = address.substring(2) + '.addr.reverse'
var nodehash = namehash(name);
let name = address.substring(2) + '.addr.reverse'
let nodehash = namehash(name);
var self = this;
let self = this;
return this._getResolver(name).then(function(resolverAddress) {
if (!resolverAddress) { return null; }
// keccak('name(bytes32)')
var data = '0x691f3431' + nodehash.substring(2);
var transaction = { to: resolverAddress, data: data };
let data = '0x691f3431' + nodehash.substring(2);
let transaction = { to: resolverAddress, data: data };
return self.call(transaction);
}).then(function(data) {
@@ -1140,12 +1141,12 @@ export class BaseProvider extends Provider {
data = data.substring(64);
if (data.length < 64) { return null; }
var length = bigNumberify('0x' + data.substring(0, 64)).toNumber();
let length = bigNumberify('0x' + data.substring(0, 64)).toNumber();
data = data.substring(64);
if (2 * length > data.length) { return null; }
var name = toUtf8String('0x' + data.substring(0, 2 * length));
let name = toUtf8String('0x' + data.substring(0, 2 * length));
// Make sure the reverse record matches the foward record
return self.resolveName(name).then(function(addr) {
@@ -1212,6 +1213,8 @@ export class BaseProvider extends Provider {
return !(event.once);
});
if (this.listenerCount() === 0) { this.polling = false; }
return result;
}
@@ -1261,6 +1264,7 @@ export class BaseProvider extends Provider {
return this;
}
}
defineReadOnly(Provider, 'inherits', inheritable(Provider));

View File

@@ -108,50 +108,60 @@ export class EtherscanProvider extends BaseProvider{
perform(method: string, params: any) {
//if (!params) { params = {}; }
var url = this.baseUrl;
let url = this.baseUrl;
let apiKey = '';
if (this.apiKey) { apiKey += '&apikey=' + this.apiKey; }
let get = (url: string, procFunc?: (value: any) => any) => {
return fetchJson(url, null, procFunc || getJsonResult).then((result) => {
this.emit('debug', {
action: 'perform',
request: url,
response: result,
provider: this
});
return result;
});
};
switch (method) {
case 'getBlockNumber':
url += '/api?module=proxy&action=eth_blockNumber' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getGasPrice':
url += '/api?module=proxy&action=eth_gasPrice' + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getBalance':
// Returns base-10 result
url += '/api?module=account&action=balance&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getResult);
return get(url, getResult);
case 'getTransactionCount':
url += '/api?module=proxy&action=eth_getTransactionCount&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getCode':
url += '/api?module=proxy&action=eth_getCode&address=' + params.address;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'getStorageAt':
url += '/api?module=proxy&action=eth_getStorageAt&address=' + params.address;
url += '&position=' + params.position;
url += '&tag=' + params.blockTag + apiKey;
return fetchJson(url, null, getJsonResult);
return get(url, getJsonResult);
case 'sendTransaction':
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return fetchJson(url, null, getJsonResult).catch((error) => {
return get(url).catch((error) => {
if (error.responseText) {
// "Insufficient funds. The account you tried to send transaction from does not have enough funds. Required 21464000000000 and got: 0"
if (error.responseText.toLowerCase().indexOf('insufficient funds') >= 0) {
@@ -178,34 +188,40 @@ export class EtherscanProvider extends BaseProvider{
url += '&boolean=false';
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
throw new Error('getBlock by blockHash not implmeneted');
case 'getTransaction':
url += '/api?module=proxy&action=eth_getTransactionByHash&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'getTransactionReceipt':
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
case 'call':
var transaction = getTransactionString(params.transaction);
case 'call': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_call' + transaction;
//url += '&tag=' + params.blockTag + apiKey;
if (params.blockTag !== 'latest') {
throw new Error('EtherscanProvider does not support blockTag for call');
}
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'estimateGas':
var transaction = getTransactionString(params.transaction);
case 'estimateGas': {
let transaction = getTransactionString(params.transaction);
if (transaction) { transaction = '&' + transaction; }
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return fetchJson(url, null, getJsonResult);
return get(url);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
@@ -227,7 +243,7 @@ export class EtherscanProvider extends BaseProvider{
if (params.filter.topics.length > 1) {
throw new Error('unsupported topic format');
}
var topic0 = params.filter.topics[0];
let topic0 = params.filter.topics[0];
if (typeof(topic0) !== 'string' || topic0.length !== 66) {
throw new Error('unsupported topic0 format');
}
@@ -240,7 +256,7 @@ export class EtherscanProvider extends BaseProvider{
url += apiKey;
var self = this;
return fetchJson(url, null, getResult).then(function(logs: Array<any>) {
return get(url, getResult).then(function(logs: Array<any>) {
var txs: { [hash: string]: string } = {};
var seq = Promise.resolve();
@@ -268,7 +284,7 @@ export class EtherscanProvider extends BaseProvider{
if (this.network.name !== 'homestead') { return Promise.resolve(0.0); }
url += '/api?module=stats&action=ethprice';
url += apiKey;
return fetchJson(url, null, getResult).then(function(result) {
return get(url, getResult).then(function(result) {
return parseFloat(result.ethusd);
});
@@ -297,6 +313,12 @@ export class EtherscanProvider extends BaseProvider{
url += '&sort=asc' + apiKey;
return fetchJson(url, null, getResult).then((result: Array<any>) => {
this.emit('debug', {
action: 'getHistory',
request: url,
response: result,
provider: this
});
var output: Array<TransactionResponse> = [];
result.forEach((tx) => {
['contractAddress', 'to'].forEach(function(key) {

View File

@@ -6,11 +6,14 @@ import { BaseProvider } from './base-provider';
import { Signer } from '../abstract-signer';
import * as errors from '../errors';
import { getAddress } from '../utils/address';
import { BigNumber } from '../utils/bignumber';
import { hexlify, hexStripZeros } from '../utils/bytes';
import { getNetwork } from '../utils/networks';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { checkProperties, defineReadOnly, shallowCopy } from '../utils/properties';
import { populateTransaction } from '../utils/transaction';
import { toUtf8Bytes } from '../utils/utf8';
import { fetchJson, poll } from '../utils/web';
@@ -18,9 +21,8 @@ import { fetchJson, poll } from '../utils/web';
import { Arrayish } from '../utils/bytes';
import { Network, Networkish } from '../utils/networks';
import { ConnectionInfo } from '../utils/web';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
import * as errors from '../errors';
import { BlockTag, TransactionRequest, TransactionResponse } from '../providers/abstract-provider';
function timer(timeout: number): Promise<any> {
return new Promise(function(resolve) {
@@ -78,15 +80,6 @@ export class JsonRpcSigner extends Signer {
}
}
/* May add back in the future; for now it is considered confusing. :)
get address(): string {
if (!this._address) {
errors.throwError('no sync sync address available; use getAddress', errors.UNSUPPORTED_OPERATION, { operation: 'address' });
}
return this._address
}
*/
getAddress(): Promise<string> {
if (this._address) {
return Promise.resolve(this._address);
@@ -110,21 +103,18 @@ export class JsonRpcSigner extends Signer {
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
let tx: TransactionRequest = shallowCopy(transaction);
if (tx.from == null) {
tx.from = this.getAddress().then((address) => {
if (!address) { return null; }
return address.toLowerCase();
});
}
// Once populateTransaction resolves, the from address will be populated from getAddress
let from: string = null;
let getAddress = this.getAddress().then((address) => {
if (address) { from = address.toLowerCase(); }
return from;
});
if (transaction.gasLimit == null) {
tx.gasLimit = this.provider.estimateGas(tx);
}
return resolveProperties(tx).then((tx) => {
return this.provider.send('eth_sendTransaction', [ JsonRpcProvider.hexlifyTransaction(tx) ]).then((hash) => {
return populateTransaction(transaction, this.provider, getAddress).then((tx) => {
let hexTx = JsonRpcProvider.hexlifyTransaction(tx);
hexTx.from = from;
return this.provider.send('eth_sendTransaction', [ hexTx ]).then((hash) => {
return poll(() => {
return this.provider.getTransaction(hash).then((tx: TransactionResponse) => {
if (tx === null) { return undefined; }
@@ -176,6 +166,10 @@ export class JsonRpcSigner extends Signer {
}
}
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export class JsonRpcProvider extends BaseProvider {
readonly connection: ConnectionInfo;
@@ -236,14 +230,22 @@ export class JsonRpcProvider extends BaseProvider {
}
send(method: string, params: any): Promise<any> {
var request = {
let request = {
method: method,
params: params,
id: 42,
jsonrpc: "2.0"
};
return fetchJson(this.connection, JSON.stringify(request), getResult);
return fetchJson(this.connection, JSON.stringify(request), getResult).then((result) => {
this.emit('debug', {
action: 'send',
request: request,
response: result,
provider: this
});
return result;
});
}
perform(method: string, params: any): Promise<any> {
@@ -300,10 +302,10 @@ export class JsonRpcProvider extends BaseProvider {
return this.send('eth_getTransactionReceipt', [ params.transactionHash ]);
case 'call':
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction), 'latest' ]);
return this.send('eth_call', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag ]);
case 'estimateGas':
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction) ]);
return this.send('eth_estimateGas', [ JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }) ]);
case 'getLogs':
if (params.filter && params.filter.address != null) {
@@ -369,11 +371,22 @@ export class JsonRpcProvider extends BaseProvider {
// - gasLimit => gas
// - All values hexlified
// - All numeric values zero-striped
// @TODO: Not any, a dictionary of string to strings
static hexlifyTransaction(transaction: TransactionRequest): any {
var result: any = {};
// NOTE: This allows a TransactionRequest, but all values should be resolved
// before this is called
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { [key: string]: boolean }): { [key: string]: string } {
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like extra zeros.
// Check only allowed properties are given
let allowed = shallowCopy(allowedTransactionKeys);
if (allowExtra) {
for (let key in allowExtra) {
if (allowExtra[key]) { allowed[key] = true; }
}
}
checkProperties(transaction, allowed);
let result: { [key: string]: string } = {};
// Some nodes (INFURA ropsten; INFURA mainnet is fine) don't like leading zeros.
['gasLimit', 'gasPrice', 'nonce', 'value'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
let value = hexStripZeros(hexlify((<any>transaction)[key]));
@@ -384,7 +397,7 @@ export class JsonRpcProvider extends BaseProvider {
['from', 'to', 'data'].forEach(function(key) {
if ((<any>transaction)[key] == null) { return; }
result[key] = hexlify((<any>transaction)[key]);
});
});
return result;
}

View File

@@ -14,10 +14,11 @@ import { sha256 } from './sha2';
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
import { randomBytes } from './random-bytes';
import { getNetwork } from './networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from './secp256k1';
import { SigningKey } from './signing-key';
import { populateTransaction } from './transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from './utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from './units';
@@ -60,6 +61,7 @@ export {
fetchJson,
getNetwork,
checkProperties,
deepCopy,
defineReadOnly,
resolveProperties,
@@ -122,6 +124,7 @@ export {
joinSignature,
parseTransaction,
populateTransaction,
serializeTransaction,
getJsonWalletAddress,

View File

@@ -182,19 +182,21 @@ class _EventDescription extends Description implements EventDescription {
let topics: Array<string> = [];
if (!this.anonymous) { topics.push(this.topic); }
params.forEach((arg, index) => {
if (arg === null) {
topics.push(null);
return;
}
let param = this.inputs[index];
if (!param.indexed) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
if (arg != null) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
}
return;
}
if (param.type === 'string') {
if (arg == null) {
topics.push(null);
} else if (param.type === 'string') {
topics.push(id(arg));
} else if (param.type === 'bytes') {
topics.push(keccak256(arg));

View File

@@ -1,5 +1,7 @@
'use strict';
import * as errors from '../errors';
export function defineReadOnly(object: any, name: string, value: any): void {
Object.defineProperty(object, name, {
enumerable: true,
@@ -42,6 +44,25 @@ export function resolveProperties(object: any): Promise<any> {
});
}
export function checkProperties(object: any, properties: { [ name: string ]: boolean }): void {
if (!object || typeof(object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach((key) => {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
export function shallowCopy(object: any): any {
let result: any = {};
for (var key in object) { result[key] = object[key]; }

View File

@@ -9,6 +9,7 @@ import { getAddress } from './address';
import { BigNumber, bigNumberify } from './bignumber';
import { arrayify, hexlify, hexZeroPad, splitSignature, stripZeros, } from './bytes';
import { keccak256 } from './keccak256';
import { checkProperties, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
@@ -19,6 +20,8 @@ import * as RLP from './rlp';
import { Arrayish, Signature } from './bytes';
import { BigNumberish } from './bignumber';
import { Provider } from '../providers/abstract-provider';
///////////////////////////////
// Exported Types
@@ -65,7 +68,7 @@ function handleNumber(value: string): BigNumber {
return bigNumberify(value);
}
var transactionFields = [
const transactionFields = [
{ name: 'nonce', maxLength: 32 },
{ name: 'gasPrice', maxLength: 32 },
{ name: 'gasLimit', maxLength: 32 },
@@ -74,8 +77,14 @@ var transactionFields = [
{ name: 'data' },
];
const allowedTransactionKeys: { [ key: string ]: boolean } = {
chainId: true, data: true, gasLimit: true, gasPrice:true, nonce: true, to: true, value: true
}
export function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string {
var raw: Array<string | Uint8Array> = [];
checkProperties(transaction, allowedTransactionKeys);
let raw: Array<string | Uint8Array> = [];
transactionFields.forEach(function(fieldInfo) {
let value = (<any>transaction)[fieldInfo.name] || ([]);
@@ -115,7 +124,7 @@ export function serialize(transaction: UnsignedTransaction, signature?: Arrayish
let sig = splitSignature(signature);
// We pushed a chainId and null r, s on for hashing only; remove those
var v = 27 + sig.recoveryParam
let v = 27 + sig.recoveryParam
if (raw.length === 9) {
raw.pop();
raw.pop();
@@ -171,7 +180,7 @@ export function parse(rawTransaction: Arrayish): Transaction {
tx.chainId = Math.floor((tx.v - 35) / 2);
if (tx.chainId < 0) { tx.chainId = 0; }
var recoveryParam = tx.v - 27;
let recoveryParam = tx.v - 27;
let raw = transaction.slice(0, 6);
@@ -182,7 +191,7 @@ export function parse(rawTransaction: Arrayish): Transaction {
recoveryParam -= tx.chainId * 2 + 8;
}
var digest = keccak256(RLP.encode(raw));
let digest = keccak256(RLP.encode(raw));
try {
tx.from = recoverAddress(digest, { r: hexlify(tx.r), s: hexlify(tx.s), recoveryParam: recoveryParam });
} catch (error) {
@@ -194,3 +203,41 @@ export function parse(rawTransaction: Arrayish): Transaction {
return tx;
}
export function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction> {
if (!Provider.isProvider(provider)) {
errors.throwError('missing provider', errors.INVALID_ARGUMENT, {
argument: 'provider',
value: provider
});
}
checkProperties(transaction, allowedTransactionKeys);
let tx = shallowCopy(transaction);
if (tx.to != null) {
tx.to = provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = provider.getTransactionCount(from);
}
if (tx.gasLimit == null) {
let estimate = shallowCopy(tx);
estimate.from = from;
tx.gasLimit = provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = provider.getNetwork().then((network) => network.chainId);
}
return resolveProperties(tx);
}

View File

@@ -3,6 +3,7 @@
import { XMLHttpRequest } from 'xmlhttprequest';
import { encode as base64Encode } from './base64';
import { shallowCopy } from './properties';
import { toUtf8Bytes } from './utf8';
import * as errors from '../errors';
@@ -180,6 +181,7 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any> {
if (!options) { options = {}; }
options = shallowCopy(options);
if (options.floor == null) { options.floor = 0; }
if (options.ceiling == null) { options.ceiling = 10000; }
if (options.interval == null) { options.interval = 250; }

View File

@@ -10,7 +10,7 @@ import { defineReadOnly, resolveProperties, shallowCopy } from './utils/properti
import { randomBytes } from './utils/random-bytes';
import * as secretStorage from './utils/secret-storage';
import { SigningKey } from './utils/signing-key';
import { serialize as serializeTransaction } from './utils/transaction';
import { populateTransaction, serialize as serializeTransaction } from './utils/transaction';
import { Wordlist } from './utils/wordlist';
// Imported Abstracts
@@ -24,7 +24,6 @@ import { BlockTag, TransactionRequest, TransactionResponse } from './providers/a
import * as errors from './errors';
export class Wallet extends AbstractSigner {
readonly provider: Provider;
@@ -72,7 +71,7 @@ export class Wallet extends AbstractSigner {
return resolveProperties(transaction).then((tx) => {
let rawTx = serializeTransaction(tx);
let signature = this.signingKey.signDigest(keccak256(rawTx));
return Promise.resolve(serializeTransaction(tx, signature));
return serializeTransaction(tx, signature);
});
}
@@ -92,36 +91,11 @@ export class Wallet extends AbstractSigner {
}
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
if (!this.provider) { throw new Error('missing provider'); }
if (!transaction || typeof(transaction) !== 'object') {
throw new Error('invalid transaction object');
}
var tx = shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasLimit == null) {
tx.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(tx);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then((network) => network.chainId);
}
return this.provider.sendTransaction(this.sign(tx));
return populateTransaction(transaction, this.provider, this.address).then((tx) => {
return this.sign(tx).then((signedTransaction) => {
return this.provider.sendTransaction(signedTransaction);
});
});
}
encrypt(password: Arrayish | string, options?: any, progressCallback?: ProgressCallback): Promise<string> {

55
tests/local-tests.js Normal file
View File

@@ -0,0 +1,55 @@
// These are test cases that cannot be run on Travis CI, but running them locally can
// help prevent certain bugs from getting committed.
var assert = require('assert');
var ethers = require('..');
var wallet = new ethers.Wallet("0x0123456789012345678901234567890123456789012345678901234567890123");
describe("Local JSON-RPC", function() {
// https://github.com/ethers-io/ethers.js/issues/306
it ("sends a transaction", function() {
this.timeout(10000);
var provider = new ethers.providers.JsonRpcProvider();
var signer = provider.getSigner(1);
return signer.sendTransaction({
to: wallet.address,
value: 1
}).then(function(tx) {
console.log(tx);
return tx.wait().then(() => {
console.log("Mined", provider);
});
}, function(error) {
console.log(error);
assert.ok(false, "throws an error");
});
});
it("sends a wallet transactin", function() {
this.timeout(10000);
var provider = new ethers.providers.JsonRpcProvider();
var signer = wallet.connect(provider);
return signer.sendTransaction({
to: provider.getSigner(1).getAddress(),
value: 2
}).then(function(tx) {
console.log(tx);
return tx.wait().then(() => {
console.log("Mined");
});
}, function(error) {
console.log(error);
assert.ok(false, "throws an error");
});
});
});

View File

@@ -441,3 +441,72 @@ describe('Test Fixed Bytes Coder', function() {
});
});
});
describe('Test Filters', function() {
// @TODO: Add a LOT more tests here
function doTest(test) {
it(test.name, function() {
var iface = new ethers.utils.Interface([ test.signature ]);
var eventDescription = iface.events[test.event];
var filter = eventDescription.encodeTopics(test.args);
assert.equal(filter.length, test.expected.length, 'filter length matches - ' + test.name);
filter.forEach(function(expected, index) {
assert.equal(expected, test.expected[index], 'signature topic matches - ' + index + ' - ' + test.name);
});
});
}
var Tests = [
// Skips null in non-indexed fields
// See: https://github.com/ethers-io/ethers.js/issues/305
{
name: "creates correct filters for null non-indexed fields",
args: [ null, 2, null, null ],
event: "LogSomething",
signature: "event LogSomething(int hup, int indexed two, bool three, address indexed four)",
expected: [
"0xf6b983969813047dce97b9ff8a48cfb0a13306eb2caae2ef186b280bc27491c8",
"0x0000000000000000000000000000000000000000000000000000000000000002"
]
},
// https://etherscan.io/tx/0x820cc57bc77be44d8f4f024a18e18f64a8b6e62a82a3d7897db5970dbe181ba1
{
name: "transfer filtering from",
args: [
"0x59DEa134510ebce4a0c7146595dc8A61Eb9D0D79"
],
event: "Transfer",
signature: "event Transfer(address indexed from, address indexed to, uint value)",
expected: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
"0x00000000000000000000000059dea134510ebce4a0c7146595dc8a61eb9d0d79"
]
},
{
name: "transfer filtering to",
args: [
null,
"0x851b9167B7cbf772D38eFaf89705b35022880A07"
],
event: "Transfer",
signature: "event Transfer(address indexed from, address indexed to, uint value)",
expected: [
"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef",
null,
"0x000000000000000000000000851b9167b7cbf772d38efaf89705b35022880a07"
]
}
];
Tests.forEach(function(test) {
doTest(test);
});
});

1
utils/hdnode.d.ts vendored
View File

@@ -5,6 +5,7 @@ export declare class HDNode {
private readonly keyPair;
readonly privateKey: string;
readonly publicKey: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
readonly chainCode: string;

View File

@@ -54,6 +54,7 @@ var HDNode = /** @class */ (function () {
properties_1.defineReadOnly(this, 'keyPair', new secp256k1_1.KeyPair(privateKey));
properties_1.defineReadOnly(this, 'privateKey', this.keyPair.privateKey);
properties_1.defineReadOnly(this, 'publicKey', this.keyPair.compressedPublicKey);
properties_1.defineReadOnly(this, 'address', secp256k1_1.computeAddress(this.publicKey));
properties_1.defineReadOnly(this, 'chainCode', bytes_1.hexlify(chainCode));
properties_1.defineReadOnly(this, 'index', index);
properties_1.defineReadOnly(this, 'depth', depth);

5
utils/index.d.ts vendored
View File

@@ -12,10 +12,11 @@ import { sha256 } from './sha2';
import { keccak256 as solidityKeccak256, pack as solidityPack, sha256 as soliditySha256 } from './solidity';
import { randomBytes } from './random-bytes';
import { getNetwork } from './networks';
import { deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import { checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy } from './properties';
import * as RLP from './rlp';
import { computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage } from './secp256k1';
import { SigningKey } from './signing-key';
import { populateTransaction } from './transaction';
import { parse as parseTransaction, serialize as serializeTransaction } from './transaction';
import { formatBytes32String, parseBytes32String, toUtf8Bytes, toUtf8String } from './utf8';
import { commify, formatEther, parseEther, formatUnits, parseUnits } from './units';
@@ -31,4 +32,4 @@ import { Transaction, UnsignedTransaction } from './transaction';
import { ConnectionInfo, OnceBlockable, PollOptions } from './web';
import { EncryptOptions, ProgressCallback } from './secret-storage';
import { Wordlist } from './wordlist';
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };
export { AbiCoder, defaultAbiCoder, formatSignature, formatParamType, parseSignature, parseParamType, RLP, fetchJson, getNetwork, checkProperties, deepCopy, defineReadOnly, resolveProperties, shallowCopy, arrayify, concat, padZeros, stripZeros, HDNode, SigningKey, Interface, base64, BigNumber, bigNumberify, hexlify, hexStripZeros, hexZeroPad, hexDataLength, hexDataSlice, toUtf8Bytes, toUtf8String, formatBytes32String, parseBytes32String, hashMessage, namehash, id, getAddress, getIcapAddress, getContractAddress, formatEther, parseEther, formatUnits, parseUnits, commify, keccak256, sha256, randomBytes, solidityPack, solidityKeccak256, soliditySha256, splitSignature, joinSignature, parseTransaction, populateTransaction, serializeTransaction, getJsonWalletAddress, computeAddress, computePublicKey, recoverAddress, recoverPublicKey, verifyMessage, SupportedAlgorithms, UnicodeNormalizationForm, CoerceFunc, EventFragment, FunctionFragment, ParamType, BigNumberish, Arrayish, Hexable, Signature, Indexed, DeployDescription, EventDescription, FunctionDescription, LogDescription, TransactionDescription, Network, Networkish, Transaction, UnsignedTransaction, ConnectionInfo, OnceBlockable, PollOptions, EncryptOptions, ProgressCallback, Wordlist, };

View File

@@ -58,6 +58,7 @@ exports.randomBytes = random_bytes_1.randomBytes;
var networks_1 = require("./networks");
exports.getNetwork = networks_1.getNetwork;
var properties_1 = require("./properties");
exports.checkProperties = properties_1.checkProperties;
exports.deepCopy = properties_1.deepCopy;
exports.defineReadOnly = properties_1.defineReadOnly;
exports.resolveProperties = properties_1.resolveProperties;
@@ -73,8 +74,10 @@ exports.verifyMessage = secp256k1_1.verifyMessage;
var signing_key_1 = require("./signing-key");
exports.SigningKey = signing_key_1.SigningKey;
var transaction_1 = require("./transaction");
exports.parseTransaction = transaction_1.parse;
exports.serializeTransaction = transaction_1.serialize;
exports.populateTransaction = transaction_1.populateTransaction;
var transaction_2 = require("./transaction");
exports.parseTransaction = transaction_2.parse;
exports.serializeTransaction = transaction_2.serialize;
var utf8_1 = require("./utf8");
exports.formatBytes32String = utf8_1.formatBytes32String;
exports.parseBytes32String = utf8_1.parseBytes32String;

View File

@@ -128,15 +128,17 @@ var _EventDescription = /** @class */ (function (_super) {
topics.push(this.topic);
}
params.forEach(function (arg, index) {
if (arg === null) {
topics.push(null);
return;
}
var param = _this.inputs[index];
if (!param.indexed) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
if (arg != null) {
errors.throwError('cannot filter non-indexed parameters; must be null', errors.INVALID_ARGUMENT, { argument: (param.name || index), value: arg });
}
return;
}
if (param.type === 'string') {
if (arg == null) {
topics.push(null);
}
else if (param.type === 'string') {
topics.push(hash_1.id(arg));
}
else if (param.type === 'bytes') {

View File

@@ -2,6 +2,9 @@ export declare function defineReadOnly(object: any, name: string, value: any): v
export declare function setType(object: any, type: string): void;
export declare function isType(object: any, type: string): boolean;
export declare function resolveProperties(object: any): Promise<any>;
export declare function checkProperties(object: any, properties: {
[name: string]: boolean;
}): void;
export declare function shallowCopy(object: any): any;
export declare function deepCopy(object: any, frozen?: boolean): any;
export declare function inheritable(parent: any): (child: any) => void;

View File

@@ -1,5 +1,13 @@
'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 });
var errors = __importStar(require("../errors"));
function defineReadOnly(object, name, value) {
Object.defineProperty(object, name, {
enumerable: true,
@@ -38,6 +46,24 @@ function resolveProperties(object) {
});
}
exports.resolveProperties = resolveProperties;
function checkProperties(object, properties) {
if (!object || typeof (object) !== 'object') {
errors.throwError('invalid object', errors.INVALID_ARGUMENT, {
argument: 'object',
value: object
});
}
Object.keys(object).forEach(function (key) {
if (!properties[key]) {
errors.throwError('invalid object key - ' + key, errors.INVALID_ARGUMENT, {
argument: 'transaction',
value: object,
key: key
});
}
});
}
exports.checkProperties = checkProperties;
function shallowCopy(object) {
var result = {};
for (var key in object) {

View File

@@ -1,6 +1,7 @@
import { BigNumber } from './bignumber';
import { Arrayish, Signature } from './bytes';
import { BigNumberish } from './bignumber';
import { Provider } from '../providers/abstract-provider';
export declare type UnsignedTransaction = {
to?: string;
nonce?: number;
@@ -26,3 +27,4 @@ export interface Transaction {
}
export declare function serialize(transaction: UnsignedTransaction, signature?: Arrayish | Signature): string;
export declare function parse(rawTransaction: Arrayish): Transaction;
export declare function populateTransaction(transaction: any, provider: Provider, from: string | Promise<string>): Promise<Transaction>;

View File

@@ -14,7 +14,9 @@ var address_1 = require("./address");
var bignumber_1 = require("./bignumber");
var bytes_1 = require("./bytes");
var keccak256_1 = require("./keccak256");
var properties_1 = require("./properties");
var RLP = __importStar(require("./rlp"));
var abstract_provider_1 = require("../providers/abstract-provider");
///////////////////////////////
function handleAddress(value) {
if (value === '0x') {
@@ -36,7 +38,11 @@ var transactionFields = [
{ name: 'value', maxLength: 32 },
{ name: 'data' },
];
var allowedTransactionKeys = {
chainId: true, data: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
};
function serialize(transaction, signature) {
properties_1.checkProperties(transaction, allowedTransactionKeys);
var raw = [];
transactionFields.forEach(function (fieldInfo) {
var value = transaction[fieldInfo.name] || ([]);
@@ -139,3 +145,32 @@ function parse(rawTransaction) {
return tx;
}
exports.parse = parse;
function populateTransaction(transaction, provider, from) {
if (!abstract_provider_1.Provider.isProvider(provider)) {
errors.throwError('missing provider', errors.INVALID_ARGUMENT, {
argument: 'provider',
value: provider
});
}
properties_1.checkProperties(transaction, allowedTransactionKeys);
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = provider.resolveName(tx.to);
}
if (tx.gasPrice == null) {
tx.gasPrice = provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = provider.getTransactionCount(from);
}
if (tx.gasLimit == null) {
var estimate = properties_1.shallowCopy(tx);
estimate.from = from;
tx.gasLimit = provider.estimateGas(estimate);
}
if (tx.chainId == null) {
tx.chainId = provider.getNetwork().then(function (network) { return network.chainId; });
}
return properties_1.resolveProperties(tx);
}
exports.populateTransaction = populateTransaction;

View File

@@ -9,6 +9,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true });
var xmlhttprequest_1 = require("xmlhttprequest");
var base64_1 = require("./base64");
var properties_1 = require("./properties");
var utf8_1 = require("./utf8");
var errors = __importStar(require("../errors"));
function fetchJson(connection, json, processFunc) {
@@ -144,6 +145,7 @@ function poll(func, options) {
if (!options) {
options = {};
}
options = properties_1.shallowCopy(options);
if (options.floor == null) {
options.floor = 0;
}

2
wallet.d.ts vendored
View File

@@ -30,6 +30,6 @@ export declare class Wallet extends AbstractSigner {
* Static methods to create Wallet instances.
*/
static createRandom(options?: any): Wallet;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet>;
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet>;
static fromMnemonic(mnemonic: string, path?: string, wordlist?: Wordlist): Wallet;
}

View File

@@ -83,7 +83,7 @@ var Wallet = /** @class */ (function (_super) {
return properties_1.resolveProperties(transaction).then(function (tx) {
var rawTx = transaction_1.serialize(tx);
var signature = _this.signingKey.signDigest(keccak256_1.keccak256(rawTx));
return Promise.resolve(transaction_1.serialize(tx, signature));
return transaction_1.serialize(tx, signature);
});
};
Wallet.prototype.signMessage = function (message) {
@@ -102,30 +102,12 @@ var Wallet = /** @class */ (function (_super) {
return this.provider.getTransactionCount(this.address, blockTag);
};
Wallet.prototype.sendTransaction = function (transaction) {
if (!this.provider) {
throw new Error('missing provider');
}
if (!transaction || typeof (transaction) !== 'object') {
throw new Error('invalid transaction object');
}
var tx = properties_1.shallowCopy(transaction);
if (tx.to != null) {
tx.to = this.provider.resolveName(tx.to);
}
if (tx.gasLimit == null) {
tx.from = this.getAddress();
tx.gasLimit = this.provider.estimateGas(tx);
}
if (tx.gasPrice == null) {
tx.gasPrice = this.provider.getGasPrice();
}
if (tx.nonce == null) {
tx.nonce = this.getTransactionCount();
}
if (tx.chainId == null) {
tx.chainId = this.provider.getNetwork().then(function (network) { return network.chainId; });
}
return this.provider.sendTransaction(this.sign(tx));
var _this = this;
return transaction_1.populateTransaction(transaction, this.provider, this.address).then(function (tx) {
return _this.sign(tx).then(function (signedTransaction) {
return _this.provider.sendTransaction(signedTransaction);
});
});
};
Wallet.prototype.encrypt = function (password, options, progressCallback) {
if (typeof (options) === 'function' && !progressCallback) {