Compare commits

..

18 Commits

Author SHA1 Message Date
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
Richard Moore
e39e2ed718 Added address to HDNode (#196). 2018-10-07 01:09:56 -04:00
Richard Moore
71f781d542 Added French and Spanish to test-hdnode. 2018-10-07 01:05:49 -04:00
Richard Moore
b2db10e216 Mark progressCallback as optional (#293). 2018-10-07 00:42:52 -04:00
Richard Moore
3736a15714 Updated dist files. 2018-10-04 20:25:07 -04:00
45 changed files with 1054 additions and 413 deletions

2
_version.d.ts vendored
View File

@@ -1 +1 @@
export declare const version = "4.0.2";
export declare const version = "4.0.7";

View File

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

6
contract.d.ts vendored
View File

@@ -21,6 +21,12 @@ export interface Event extends Log {
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
export declare class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider);

View File

@@ -106,14 +106,20 @@ function resolveAddresses(provider, value, paramType) {
function runMethod(contract, functionName, estimateOnly) {
var method = contract.interface.functions[functionName];
return function () {
var _this = this;
var params = [];
for (var _i = 0; _i < arguments.length; _i++) {
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]) {
@@ -153,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, {
@@ -206,7 +212,36 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from != null) {
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
}
return contract.signer.sendTransaction(tx);
return contract.signer.sendTransaction(tx).then(function (tx) {
var wait = tx.wait.bind(tx);
tx.wait = function (confirmations) {
return wait(confirmations).then(function (receipt) {
receipt.events = receipt.logs.map(function (log) {
var event = properties_1.deepCopy(log);
var parsed = _this.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
event.event = parsed.name;
event.eventSignature = parsed.signature;
}
event.removeListener = function () { return _this.provider; };
event.getBlock = function () {
return _this.provider.getBlock(receipt.blockHash);
};
event.getTransaction = function () {
return _this.provider.getTransaction(receipt.transactionHash);
};
event.getTransactionReceipt = function () {
return Promise.resolve(receipt);
};
return event;
});
return receipt;
});
};
return tx;
});
}
throw new Error('invalid type - ' + method.type);
return null;

422
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.2";
exports.version = "4.0.7";
},{}],2:[function(require,module,exports){
"use strict";
@@ -154,14 +154,20 @@ function resolveAddresses(provider, value, paramType) {
function runMethod(contract, functionName, estimateOnly) {
var method = contract.interface.functions[functionName];
return function () {
var _this = this;
var params = [];
for (var _i = 0; _i < arguments.length; _i++) {
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]) {
@@ -201,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, {
@@ -254,7 +260,36 @@ function runMethod(contract, functionName, estimateOnly) {
if (tx.from != null) {
errors.throwError('cannot override from in a transaction', errors.UNSUPPORTED_OPERATION, { operation: 'sendTransaction' });
}
return contract.signer.sendTransaction(tx);
return contract.signer.sendTransaction(tx).then(function (tx) {
var wait = tx.wait.bind(tx);
tx.wait = function (confirmations) {
return wait(confirmations).then(function (receipt) {
receipt.events = receipt.logs.map(function (log) {
var event = properties_1.deepCopy(log);
var parsed = _this.interface.parseLog(log);
if (parsed) {
event.args = parsed.values;
event.decode = parsed.decode;
event.event = parsed.name;
event.eventSignature = parsed.signature;
}
event.removeListener = function () { return _this.provider; };
event.getBlock = function () {
return _this.provider.getBlock(receipt.blockHash);
};
event.getTransaction = function () {
return _this.provider.getTransaction(receipt.transactionHash);
};
event.getTransactionReceipt = function () {
return Promise.resolve(receipt);
};
return event;
});
return receipt;
});
};
return tx;
});
}
throw new Error('invalid type - ' + method.type);
return null;
@@ -9958,6 +9993,7 @@ var formatTransaction = {
blockHash: allowNull(checkHash, null),
blockNumber: allowNull(checkNumber, null),
transactionIndex: allowNull(checkNumber, null),
confirmations: allowNull(checkNumber, null),
from: address_1.getAddress,
gasPrice: bignumber_1.bigNumberify,
gasLimit: bignumber_1.bigNumberify,
@@ -10086,6 +10122,7 @@ var formatTransactionReceipt = {
transactionHash: checkHash,
logs: arrayOf(checkTransactionReceiptLog),
blockNumber: checkNumber,
confirmations: allowNull(checkNumber, null),
cumulativeGasUsed: bignumber_1.bigNumberify,
status: allowNull(checkNumber)
};
@@ -10196,6 +10233,11 @@ function getEventTag(eventName) {
}
throw new Error('invalid event - ' + eventName);
}
//////////////////////////////
// Helper Object
function getTime() {
return (new Date()).getTime();
}
var BaseProvider = /** @class */ (function (_super) {
__extends(BaseProvider, _super);
function BaseProvider(network) {
@@ -10228,11 +10270,13 @@ var BaseProvider = /** @class */ (function (_super) {
// until we get a response. This provides devs with a consistent view. Similarly for
// transaction hashes.
_this._emitted = { block: _this._lastBlockNumber };
_this._fastQueryDate = 0;
return _this;
}
BaseProvider.prototype._doPoll = function () {
var _this = this;
this.getBlockNumber().then(function (blockNumber) {
_this._setFastBlockNumber(blockNumber);
// If the block hasn't changed, meh.
if (blockNumber === _this._lastBlockNumber) {
return;
@@ -10382,13 +10426,44 @@ var BaseProvider = /** @class */ (function (_super) {
enumerable: true,
configurable: true
});
BaseProvider.prototype._getFastBlockNumber = function () {
var _this = this;
var now = getTime();
// Stale block number, request a newer value
if ((now - this._fastQueryDate) > 2 * this._pollingInterval) {
this._fastQueryDate = now;
this._fastBlockNumberPromise = this.getBlockNumber().then(function (blockNumber) {
if (_this._fastBlockNumber == null || blockNumber > _this._fastBlockNumber) {
_this._fastBlockNumber = blockNumber;
}
return _this._fastBlockNumber;
});
}
return this._fastBlockNumberPromise;
};
BaseProvider.prototype._setFastBlockNumber = function (blockNumber) {
// Older block, maybe a stale request
if (this._fastBlockNumber != null && blockNumber < this._fastBlockNumber) {
return;
}
// Update the time we updated the blocknumber
this._fastQueryDate = getTime();
// Newer block number, use it
if (this._fastBlockNumber == null || blockNumber > this._fastBlockNumber) {
this._fastBlockNumber = blockNumber;
this._fastBlockNumberPromise = Promise.resolve(blockNumber);
}
};
// @TODO: Add .poller which must be an event emitter with a 'start', 'stop' and 'block' event;
// this will be used once we move to the WebSocket or other alternatives to polling
BaseProvider.prototype.waitForTransaction = function (transactionHash, timeout) {
BaseProvider.prototype.waitForTransaction = function (transactionHash, confirmations) {
var _this = this;
if (!confirmations) {
confirmations = 1;
}
return web_1.poll(function () {
return _this.getTransactionReceipt(transactionHash).then(function (receipt) {
if (receipt == null) {
if (receipt == null || receipt.confirmations < confirmations) {
return undefined;
}
return receipt;
@@ -10403,6 +10478,7 @@ var BaseProvider = /** @class */ (function (_super) {
if (value != result) {
throw new Error('invalid response - getBlockNumber');
}
_this._setFastBlockNumber(value);
return value;
});
});
@@ -10496,7 +10572,7 @@ var BaseProvider = /** @class */ (function (_super) {
// This should be called by any subclass wrapping a TransactionResponse
BaseProvider.prototype._wrapTransaction = function (tx, hash) {
var _this = this;
if (bytes_1.hexDataLength(hash) !== 32) {
if (hash != null && bytes_1.hexDataLength(hash) !== 32) {
throw new Error('invalid response - sendTransaction');
}
var result = tx;
@@ -10506,11 +10582,11 @@ var BaseProvider = /** @class */ (function (_super) {
}
this._emitted['t:' + tx.hash] = 'pending';
// @TODO: (confirmations? number, timeout? number)
result.wait = function () {
return _this.waitForTransaction(hash).then(function (receipt) {
result.wait = function (confirmations) {
return _this.waitForTransaction(tx.hash, confirmations).then(function (receipt) {
if (receipt.status === 0) {
errors.throwError('transaction failed', errors.CALL_EXCEPTION, {
transactionHash: hash,
transactionHash: tx.hash,
transaction: tx
});
}
@@ -10519,13 +10595,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);
});
@@ -10612,7 +10689,22 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return BaseProvider.checkTransactionResponse(result);
var tx = BaseProvider.checkTransactionResponse(result);
if (tx.blockNumber == null) {
tx.confirmations = 0;
}
else if (tx.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - tx.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
tx.confirmations = confirmations;
return _this._wrapTransaction(tx);
});
}
return _this._wrapTransaction(tx);
});
}, { onceBlock: _this });
});
@@ -10632,7 +10724,26 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return checkTransactionReceipt(result);
// "geth-etc" returns receipts before they are ready
if (result.blockHash == null) {
return undefined;
}
var receipt = checkTransactionReceipt(result);
if (receipt.blockNumber == null) {
receipt.confirmations = 0;
}
else if (receipt.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - receipt.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
receipt.confirmations = confirmations;
return receipt;
});
}
return receipt;
});
}, { onceBlock: _this });
});
@@ -10827,6 +10938,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) {
@@ -11000,7 +11114,6 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -11035,17 +11148,19 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
// "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) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
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) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -11070,15 +11185,20 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
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':
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
@@ -11086,6 +11206,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {
@@ -11420,13 +11541,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 () {
@@ -11477,14 +11599,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) {
@@ -11506,20 +11620,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) {
@@ -11532,21 +11644,23 @@ var JsonRpcSigner = /** @class */ (function (_super) {
throw error;
});
}, function (error) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
}
throw error;
});
@@ -11569,6 +11683,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) {
@@ -11645,17 +11762,19 @@ var JsonRpcProvider = /** @class */ (function (_super) {
return this.send('eth_getStorageAt', [getLowerCase(params.address), params.position, params.blockTag]);
case 'sendTransaction':
return this.send('eth_sendRawTransaction', [params.signedTransaction]).catch(function (error) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
if (error.responseText) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -11672,9 +11791,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);
@@ -11732,10 +11851,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;
@@ -11758,7 +11888,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 ||
@@ -13504,6 +13634,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);
@@ -13779,6 +13910,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;
@@ -13794,8 +13926,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;
@@ -13949,15 +14083,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') {
@@ -14416,7 +14552,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,
@@ -14455,6 +14599,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) {
@@ -14528,7 +14690,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 });
@@ -15391,7 +15553,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') {
@@ -15413,7 +15577,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] || ([]);
@@ -15516,8 +15684,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;
@@ -15891,6 +16088,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) {
@@ -15964,6 +16162,9 @@ function fetchJson(connection, json, processFunc) {
// @TODO: not any!
var error = new Error('invalid response - ' + request.status);
error.statusCode = request.status;
if (request.responseText) {
error.responseText = request.responseText;
}
reject(error);
return;
}
@@ -16023,6 +16224,7 @@ function poll(func, options) {
if (!options) {
options = {};
}
options = properties_1.shallowCopy(options);
if (options.floor == null) {
options.floor = 0;
}
@@ -16089,7 +16291,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 });
@@ -16232,7 +16434,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) {
@@ -16251,30 +16453,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

43
dist/ethers.types.txt vendored
View File

@@ -17,9 +17,9 @@ declare module 'ethers/ethers' {
import * as wordlists from 'ethers/wordlists';
import { platform } from 'ethers/utils/shims';
import { version } from 'ethers/_version';
import { ContractFunction, Event, EventFilter } from 'ethers/contract';
import { ContractFunction, ContractTransaction, Event, EventFilter } from 'ethers/contract';
function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider;
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, Event, EventFilter };
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };
}
declare module 'ethers/contract' {
@@ -46,6 +46,12 @@ declare module 'ethers/contract' {
getTransaction: () => Promise<TransactionResponse>;
getTransactionReceipt: () => Promise<TransactionReceipt>;
}
export interface ContractReceipt extends TransactionReceipt {
events?: Array<Event>;
}
export interface ContractTransaction extends TransactionResponse {
wait(confirmations?: number): Promise<ContractReceipt>;
}
export class VoidSigner extends Signer {
readonly address: string;
constructor(address: string, provider: Provider);
@@ -146,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;
}
}
@@ -213,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';
@@ -232,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' {
@@ -254,7 +261,7 @@ declare module 'ethers/utils/shims' {
}
declare module 'ethers/_version' {
export const version = "4.0.2";
export const version = "4.0.7";
}
declare module 'ethers/utils/bignumber' {
@@ -406,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;
@@ -431,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' {
@@ -482,6 +491,7 @@ declare module 'ethers/providers/abstract-provider' {
transactionHash?: string;
logs?: Array<Log>;
blockNumber?: number;
confirmations?: number;
cumulativeGasUsed?: BigNumber;
byzantium: boolean;
status?: number;
@@ -500,9 +510,10 @@ declare module 'ethers/providers/abstract-provider' {
blockNumber?: number;
blockHash?: string;
timestamp?: number;
confirmations: number;
from: string;
raw?: string;
wait: (timeout?: number) => Promise<TransactionReceipt>;
wait: (confirmations?: number) => Promise<TransactionReceipt>;
}
export type EventType = string | Array<string> | Filter;
export type Listener = (...args: Array<any>) => void;
@@ -515,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>;
@@ -614,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;
@@ -721,7 +733,9 @@ declare module 'ethers/providers/base-provider' {
readonly blockNumber: number;
polling: boolean;
pollingInterval: number;
waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
_getFastBlockNumber(): Promise<number>;
_setFastBlockNumber(blockNumber: number): void;
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
getBlockNumber(): Promise<number>;
getGasPrice(): Promise<BigNumber>;
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
@@ -730,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>;
@@ -828,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;
};
}
}
@@ -921,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;

4
ethers.d.ts vendored
View File

@@ -8,6 +8,6 @@ import * as utils from './utils';
import * as wordlists from './wordlists';
import { platform } from './utils/shims';
import { version } from './_version';
import { ContractFunction, Event, EventFilter } from './contract';
import { ContractFunction, ContractTransaction, Event, EventFilter } from './contract';
declare function getDefaultProvider(network?: utils.Network | string): providers.BaseProvider;
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, Event, EventFilter };
export { Signer, Wallet, VoidSigner, getDefaultProvider, providers, Contract, ContractFactory, constants, errors, utils, wordlists, platform, version, ContractFunction, ContractTransaction, Event, EventFilter };

2
package-lock.json generated
View File

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

View File

@@ -1,6 +1,6 @@
{
"name": "ethers",
"version": "4.0.2",
"version": "4.0.7",
"description": "Ethereum wallet library.",
"main": "./index.js",
"types": "./index.d.ts",

View File

@@ -46,6 +46,7 @@ export interface TransactionReceipt {
transactionHash?: string;
logs?: Array<Log>;
blockNumber?: number;
confirmations?: number;
cumulativeGasUsed?: BigNumber;
byzantium: boolean;
status?: number;
@@ -64,9 +65,10 @@ export interface TransactionResponse extends Transaction {
blockNumber?: number;
blockHash?: string;
timestamp?: number;
confirmations: number;
from: string;
raw?: string;
wait: (timeout?: number) => Promise<TransactionReceipt>;
wait: (confirmations?: number) => Promise<TransactionReceipt>;
}
export declare type EventType = string | Array<string> | Filter;
export declare type Listener = (...args: Array<any>) => void;
@@ -79,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

@@ -12,6 +12,9 @@ export declare class BaseProvider extends Provider {
private _poller;
private _lastBlockNumber;
private _balances;
private _fastBlockNumber;
private _fastBlockNumberPromise;
private _fastQueryDate;
/**
* ready
*
@@ -30,7 +33,9 @@ export declare class BaseProvider extends Provider {
readonly blockNumber: number;
polling: boolean;
pollingInterval: number;
waitForTransaction(transactionHash: string, timeout?: number): Promise<TransactionReceipt>;
_getFastBlockNumber(): Promise<number>;
_setFastBlockNumber(blockNumber: number): void;
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>;
getBlockNumber(): Promise<number>;
getGasPrice(): Promise<BigNumber>;
getBalance(addressOrName: string | Promise<string>, blockTag?: BlockTag | Promise<BlockTag>): Promise<BigNumber>;
@@ -39,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

@@ -150,6 +150,7 @@ var formatTransaction = {
blockHash: allowNull(checkHash, null),
blockNumber: allowNull(checkNumber, null),
transactionIndex: allowNull(checkNumber, null),
confirmations: allowNull(checkNumber, null),
from: address_1.getAddress,
gasPrice: bignumber_1.bigNumberify,
gasLimit: bignumber_1.bigNumberify,
@@ -278,6 +279,7 @@ var formatTransactionReceipt = {
transactionHash: checkHash,
logs: arrayOf(checkTransactionReceiptLog),
blockNumber: checkNumber,
confirmations: allowNull(checkNumber, null),
cumulativeGasUsed: bignumber_1.bigNumberify,
status: allowNull(checkNumber)
};
@@ -388,6 +390,11 @@ function getEventTag(eventName) {
}
throw new Error('invalid event - ' + eventName);
}
//////////////////////////////
// Helper Object
function getTime() {
return (new Date()).getTime();
}
var BaseProvider = /** @class */ (function (_super) {
__extends(BaseProvider, _super);
function BaseProvider(network) {
@@ -420,11 +427,13 @@ var BaseProvider = /** @class */ (function (_super) {
// until we get a response. This provides devs with a consistent view. Similarly for
// transaction hashes.
_this._emitted = { block: _this._lastBlockNumber };
_this._fastQueryDate = 0;
return _this;
}
BaseProvider.prototype._doPoll = function () {
var _this = this;
this.getBlockNumber().then(function (blockNumber) {
_this._setFastBlockNumber(blockNumber);
// If the block hasn't changed, meh.
if (blockNumber === _this._lastBlockNumber) {
return;
@@ -574,13 +583,44 @@ var BaseProvider = /** @class */ (function (_super) {
enumerable: true,
configurable: true
});
BaseProvider.prototype._getFastBlockNumber = function () {
var _this = this;
var now = getTime();
// Stale block number, request a newer value
if ((now - this._fastQueryDate) > 2 * this._pollingInterval) {
this._fastQueryDate = now;
this._fastBlockNumberPromise = this.getBlockNumber().then(function (blockNumber) {
if (_this._fastBlockNumber == null || blockNumber > _this._fastBlockNumber) {
_this._fastBlockNumber = blockNumber;
}
return _this._fastBlockNumber;
});
}
return this._fastBlockNumberPromise;
};
BaseProvider.prototype._setFastBlockNumber = function (blockNumber) {
// Older block, maybe a stale request
if (this._fastBlockNumber != null && blockNumber < this._fastBlockNumber) {
return;
}
// Update the time we updated the blocknumber
this._fastQueryDate = getTime();
// Newer block number, use it
if (this._fastBlockNumber == null || blockNumber > this._fastBlockNumber) {
this._fastBlockNumber = blockNumber;
this._fastBlockNumberPromise = Promise.resolve(blockNumber);
}
};
// @TODO: Add .poller which must be an event emitter with a 'start', 'stop' and 'block' event;
// this will be used once we move to the WebSocket or other alternatives to polling
BaseProvider.prototype.waitForTransaction = function (transactionHash, timeout) {
BaseProvider.prototype.waitForTransaction = function (transactionHash, confirmations) {
var _this = this;
if (!confirmations) {
confirmations = 1;
}
return web_1.poll(function () {
return _this.getTransactionReceipt(transactionHash).then(function (receipt) {
if (receipt == null) {
if (receipt == null || receipt.confirmations < confirmations) {
return undefined;
}
return receipt;
@@ -595,6 +635,7 @@ var BaseProvider = /** @class */ (function (_super) {
if (value != result) {
throw new Error('invalid response - getBlockNumber');
}
_this._setFastBlockNumber(value);
return value;
});
});
@@ -688,7 +729,7 @@ var BaseProvider = /** @class */ (function (_super) {
// This should be called by any subclass wrapping a TransactionResponse
BaseProvider.prototype._wrapTransaction = function (tx, hash) {
var _this = this;
if (bytes_1.hexDataLength(hash) !== 32) {
if (hash != null && bytes_1.hexDataLength(hash) !== 32) {
throw new Error('invalid response - sendTransaction');
}
var result = tx;
@@ -698,11 +739,11 @@ var BaseProvider = /** @class */ (function (_super) {
}
this._emitted['t:' + tx.hash] = 'pending';
// @TODO: (confirmations? number, timeout? number)
result.wait = function () {
return _this.waitForTransaction(hash).then(function (receipt) {
result.wait = function (confirmations) {
return _this.waitForTransaction(tx.hash, confirmations).then(function (receipt) {
if (receipt.status === 0) {
errors.throwError('transaction failed', errors.CALL_EXCEPTION, {
transactionHash: hash,
transactionHash: tx.hash,
transaction: tx
});
}
@@ -711,13 +752,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);
});
@@ -804,7 +846,22 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return BaseProvider.checkTransactionResponse(result);
var tx = BaseProvider.checkTransactionResponse(result);
if (tx.blockNumber == null) {
tx.confirmations = 0;
}
else if (tx.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - tx.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
tx.confirmations = confirmations;
return _this._wrapTransaction(tx);
});
}
return _this._wrapTransaction(tx);
});
}, { onceBlock: _this });
});
@@ -824,7 +881,26 @@ var BaseProvider = /** @class */ (function (_super) {
}
return undefined;
}
return checkTransactionReceipt(result);
// "geth-etc" returns receipts before they are ready
if (result.blockHash == null) {
return undefined;
}
var receipt = checkTransactionReceipt(result);
if (receipt.blockNumber == null) {
receipt.confirmations = 0;
}
else if (receipt.confirmations == null) {
return _this._getFastBlockNumber().then(function (blockNumber) {
// Add the confirmations using the fast block number (pessimistic)
var confirmations = (blockNumber - receipt.blockNumber) + 1;
if (confirmations <= 0) {
confirmations = 1;
}
receipt.confirmations = confirmations;
return receipt;
});
}
return receipt;
});
}, { onceBlock: _this });
});
@@ -1019,6 +1095,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,7 +112,6 @@ var EtherscanProvider = /** @class */ (function (_super) {
return _this;
}
EtherscanProvider.prototype.perform = function (method, params) {
//if (!params) { params = {}; }
var url = this.baseUrl;
var apiKey = '';
if (this.apiKey) {
@@ -147,17 +146,19 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_sendRawTransaction&hex=' + params.signedTransaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult).catch(function (error) {
// "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) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
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) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "Transaction with the same hash was already imported."
if (error.responseText.indexOf('same hash was already imported') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce."
if (error.responseText.indexOf('another transaction with same nonce') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -182,15 +183,20 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_getTransactionReceipt&txhash=' + params.transactionHash;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
case 'call':
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':
}
case 'estimateGas': {
var transaction = getTransactionString(params.transaction);
if (transaction) {
transaction = '&' + transaction;
@@ -198,6 +204,7 @@ var EtherscanProvider = /** @class */ (function (_super) {
url += '/api?module=proxy&action=eth_estimateGas&' + transaction;
url += apiKey;
return web_1.fetchJson(url, null, getJsonResult);
}
case 'getLogs':
url += '/api?module=logs&action=getLogs';
try {

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) {
@@ -132,21 +123,23 @@ var JsonRpcSigner = /** @class */ (function (_super) {
throw error;
});
}, function (error) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
if (error.responseText) {
// See: JsonRpcProvider.sendTransaction (@TODO: Expose a ._throwError??)
if (error.responseText.indexOf('insufficient funds') >= 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {
transaction: tx
});
}
if (error.responseText.indexOf('nonce too low') >= 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {
transaction: tx
});
}
if (error.responseText.indexOf('replacement transaction underpriced') >= 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {
transaction: tx
});
}
}
throw error;
});
@@ -169,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) {
@@ -245,17 +241,19 @@ var JsonRpcProvider = /** @class */ (function (_super) {
return this.send('eth_getStorageAt', [getLowerCase(params.address), params.position, params.blockTag]);
case 'sendTransaction':
return this.send('eth_sendRawTransaction', [params.signedTransaction]).catch(function (error) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
if (error.responseText) {
// "insufficient funds for gas * price + value"
if (error.responseText.indexOf('insufficient funds') > 0) {
errors.throwError('insufficient funds', errors.INSUFFICIENT_FUNDS, {});
}
// "nonce too low"
if (error.responseText.indexOf('nonce too low') > 0) {
errors.throwError('nonce has already been used', errors.NONCE_EXPIRED, {});
}
// "replacement transaction underpriced"
if (error.responseText.indexOf('replacement transaction underpriced') > 0) {
errors.throwError('replacement fee too low', errors.REPLACEMENT_UNDERPRICED, {});
}
}
throw error;
});
@@ -272,9 +270,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);
@@ -332,10 +330,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.2";
export const version = "4.0.7";

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

@@ -19,7 +19,7 @@ import { toUtf8Bytes, UnicodeNormalizationForm } from './utf8';
import { pbkdf2 } from './pbkdf2';
import { computeHmac, SupportedAlgorithms } from './hmac';
import { defineReadOnly, isType, setType } from './properties';
import { KeyPair } from './secp256k1';
import { computeAddress, KeyPair } from './secp256k1';
import { sha256 } from './sha2';
const N = bigNumberify("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
@@ -54,6 +54,8 @@ export class HDNode {
readonly privateKey: string;
readonly publicKey: string;
readonly address: string;
readonly mnemonic: string;
readonly path: string;
@@ -81,6 +83,8 @@ export class HDNode {
defineReadOnly(this, 'privateKey', this.keyPair.privateKey);
defineReadOnly(this, 'publicKey', this.keyPair.compressedPublicKey);
defineReadOnly(this, 'address', computeAddress(this.publicKey));
defineReadOnly(this, 'chainCode', hexlify(chainCode));
defineReadOnly(this, 'index', index);

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> {
@@ -165,7 +139,7 @@ export class Wallet extends AbstractSigner {
return Wallet.fromMnemonic(mnemonic, options.path, options.locale);
}
static fromEncryptedJson(json: string, password: Arrayish, progressCallback: ProgressCallback): Promise<Wallet> {
static fromEncryptedJson(json: string, password: Arrayish, progressCallback?: ProgressCallback): Promise<Wallet> {
if (isCrowdsaleWallet(json)) {
try {
if (progressCallback) { progressCallback(0); }

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);
});
});

View File

@@ -22,6 +22,8 @@ describe('Test HD Node Derivation', function(test) {
var wallet = new ethers.Wallet(node.privateKey);
assert.equal(wallet.address.toLowerCase(), nodeTest.address,
'Generates address - ' + nodeTest.privateKey);
assert.equal(node.address, (new ethers.Wallet(node)).address, 'HDNode address matches - ' + nodeTest.privateKey);
});
});
});
@@ -64,6 +66,8 @@ function testEasySeed(lang, locale) {
}
testEasySeed(ethers.wordlists.en, 'en');
testEasySeed(ethers.wordlists.es, 'es');
testEasySeed(ethers.wordlists.fr, 'fr');
testEasySeed(ethers.wordlists.ja, 'ja');
testEasySeed(ethers.wordlists.zh_cn, 'zh_cn');
testEasySeed(ethers.wordlists.zh_tw, 'zh_tw');

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) {
@@ -82,6 +83,9 @@ function fetchJson(connection, json, processFunc) {
// @TODO: not any!
var error = new Error('invalid response - ' + request.status);
error.statusCode = request.status;
if (request.responseText) {
error.responseText = request.responseText;
}
reject(error);
return;
}
@@ -141,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) {