Added deployed to contract and abstracted polling into web.

This commit is contained in:
Richard Moore 2018-06-30 23:05:22 -04:00
parent ebf42dc9e0
commit b1d026b800
No known key found for this signature in database
GPG Key ID: 525F70A6FCABC295
14 changed files with 1438 additions and 167 deletions

@ -15,6 +15,7 @@ var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes"); var bytes_1 = require("../utils/bytes");
var bignumber_1 = require("../utils/bignumber"); var bignumber_1 = require("../utils/bignumber");
var properties_1 = require("../utils/properties"); var properties_1 = require("../utils/properties");
var web_1 = require("../utils/web");
var errors = __importStar(require("../utils/errors")); var errors = __importStar(require("../utils/errors"));
var allowedTransactionKeys = { var allowedTransactionKeys = {
data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
@ -270,6 +271,25 @@ var Contract = /** @class */ (function () {
enumerable: true, enumerable: true,
configurable: true configurable: true
}); });
// @TODO: Allow timeout?
Contract.prototype.deployed = function () {
var _this = this;
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
return this.deployTransaction.wait().then(function () {
return _this;
});
}
// Otherwise, poll for our code to be deployed
return web_1.poll(function () {
return _this.provider.getCode(_this.address).then(function (code) {
if (code === '0x') {
return undefined;
}
return _this;
});
});
};
// @TODO: // @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber> // estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO: // @TODO:

8
dist/ethers.d.ts vendored

@ -226,6 +226,7 @@ declare module 'ethers/contracts/contract' {
readonly deployTransaction: TransactionResponse; readonly deployTransaction: TransactionResponse;
constructor(addressOrName: string, contractInterface: Contractish, signerOrProvider: Signer | Provider); constructor(addressOrName: string, contractInterface: Contractish, signerOrProvider: Signer | Provider);
onerror: ErrorCallback; onerror: ErrorCallback;
deployed(): Promise<any>;
fallback(overrides?: TransactionRequest): Promise<TransactionResponse>; fallback(overrides?: TransactionRequest): Promise<TransactionResponse>;
connect(signerOrProvider: Signer | Provider): Contract; connect(signerOrProvider: Signer | Provider): Contract;
attach(addressOrName: string): Contract; attach(addressOrName: string): Contract;
@ -712,6 +713,13 @@ declare module 'ethers/utils/web' {
}; };
export type ProcessFunc = (value: any) => any; export type ProcessFunc = (value: any) => any;
export function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: ProcessFunc): Promise<any>; export function fetchJson(connection: string | ConnectionInfo, json: string, processFunc: ProcessFunc): Promise<any>;
export type PollOptions = {
timeout?: number;
floor?: number;
ceiling?: number;
interval?: number;
};
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any>;
} }
declare module 'ethers/utils/transaction' { declare module 'ethers/utils/transaction' {

179
dist/ethers.js vendored

@ -8867,6 +8867,7 @@ var address_1 = require("../utils/address");
var bytes_1 = require("../utils/bytes"); var bytes_1 = require("../utils/bytes");
var bignumber_1 = require("../utils/bignumber"); var bignumber_1 = require("../utils/bignumber");
var properties_1 = require("../utils/properties"); var properties_1 = require("../utils/properties");
var web_1 = require("../utils/web");
var errors = __importStar(require("../utils/errors")); var errors = __importStar(require("../utils/errors"));
var allowedTransactionKeys = { var allowedTransactionKeys = {
data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true data: true, from: true, gasLimit: true, gasPrice: true, nonce: true, to: true, value: true
@ -9122,6 +9123,25 @@ var Contract = /** @class */ (function () {
enumerable: true, enumerable: true,
configurable: true configurable: true
}); });
// @TODO: Allow timeout?
Contract.prototype.deployed = function () {
var _this = this;
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
return this.deployTransaction.wait().then(function () {
return _this;
});
}
// Otherwise, poll for our code to be deployed
return web_1.poll(function () {
return _this.provider.getCode(_this.address).then(function (code) {
if (code === '0x') {
return undefined;
}
return _this;
});
});
};
// @TODO: // @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber> // estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>
// @TODO: // @TODO:
@ -9198,7 +9218,7 @@ var Contract = /** @class */ (function () {
}()); }());
exports.Contract = Contract; exports.Contract = Contract;
},{"../providers/provider":56,"../utils/abi-coder":58,"../utils/address":59,"../utils/bignumber":60,"../utils/bytes":61,"../utils/errors":62,"../utils/properties":66,"../wallet/wallet":79,"./interface":49}],48:[function(require,module,exports){ },{"../providers/provider":56,"../utils/abi-coder":58,"../utils/address":59,"../utils/bignumber":60,"../utils/bytes":61,"../utils/errors":62,"../utils/properties":66,"../utils/web":74,"../wallet/wallet":79,"./interface":49}],48:[function(require,module,exports){
'use strict'; 'use strict';
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
var contract_1 = require("./contract"); var contract_1 = require("./contract");
@ -10507,6 +10527,7 @@ var hash_1 = require("../utils/hash");
var networks_1 = require("./networks"); var networks_1 = require("./networks");
var properties_1 = require("../utils/properties"); var properties_1 = require("../utils/properties");
var transaction_1 = require("../utils/transaction"); var transaction_1 = require("../utils/transaction");
var web_1 = require("../utils/web");
var errors = __importStar(require("../utils/errors")); var errors = __importStar(require("../utils/errors"));
; ;
; ;
@ -10851,31 +10872,6 @@ var formatLog = {
function checkLog(log) { function checkLog(log) {
return check(formatLog, log); return check(formatLog, log);
} }
function stallPromise(allowNullFunc, executeFunc) {
return new Promise(function (resolve, reject) {
var attempt = 0;
function check() {
executeFunc().then(function (result) {
// If we have a result, or are allowed null then we're done
if (result || allowNullFunc()) {
resolve(result);
// Otherwise, exponential back-off (up to 10s) our next request
}
else {
attempt++;
var timeout = 500 + 250 * parseInt(String(Math.random() * (1 << attempt)));
if (timeout > 10000) {
timeout = 10000;
}
setTimeout(check, timeout);
}
}, function (error) {
reject(error);
});
}
check();
});
}
////////////////////////////// //////////////////////////////
// Event Serializeing // Event Serializeing
function recurse(object, convertFunc) { function recurse(object, convertFunc) {
@ -11346,13 +11342,14 @@ var Provider = /** @class */ (function () {
try { try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag); var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) { if (bytes_1.hexDataLength(blockHash) === 32) {
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['b:' + blockHash.toLowerCase()] == null);
}, function () {
return _this.perform('getBlock', { blockHash: blockHash }).then(function (block) { return _this.perform('getBlock', { blockHash: blockHash }).then(function (block) {
if (block == null) { if (block == null) {
if (_this._emitted['b:' + blockHash.toLowerCase()] == null) {
return null; return null;
} }
return undefined;
}
return checkBlock(block); return checkBlock(block);
}); });
}); });
@ -11360,16 +11357,17 @@ var Provider = /** @class */ (function () {
} }
catch (error) { } catch (error) { }
try { try {
var blockTag = checkBlockTag(blockHashOrBlockTag); var blockNumber_1 = -128;
return stallPromise(function () { var blockTag_1 = checkBlockTag(blockHashOrBlockTag);
if (bytes_1.isHexString(blockTag)) { if (bytes_1.isHexString(blockTag_1)) {
var blockNumber = parseInt(blockTag.substring(2), 16); blockNumber_1 = parseInt(blockTag_1.substring(2), 16);
return blockNumber > _this._emitted.block;
} }
return true; return web_1.poll(function () {
}, function () { return _this.perform('getBlock', { blockTag: blockTag_1 }).then(function (block) {
return _this.perform('getBlock', { blockTag: blockTag }).then(function (block) {
if (block == null) { if (block == null) {
if (blockNumber_1 > _this._emitted.block) {
return undefined;
}
return null; return null;
} }
return checkBlock(block); return checkBlock(block);
@ -11387,14 +11385,15 @@ var Provider = /** @class */ (function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) { return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash; var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, function () {
return _this.perform('getTransaction', params).then(function (result) { return _this.perform('getTransaction', params).then(function (result) {
if (result != null) { if (result == null) {
result = checkTransactionResponse(result); if (_this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
} }
return result; return undefined;
}
return checkTransactionResponse(result);
}); });
}); });
}); });
@ -11406,14 +11405,15 @@ var Provider = /** @class */ (function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) { return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash; var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, function () {
return _this.perform('getTransactionReceipt', params).then(function (result) { return _this.perform('getTransactionReceipt', params).then(function (result) {
if (result != null) { if (result == null) {
result = checkTransactionReceipt(result); if (_this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
} }
return result; return undefined;
}
return checkTransactionReceipt(result);
}); });
}); });
}); });
@ -11679,7 +11679,7 @@ var Provider = /** @class */ (function () {
}()); }());
exports.Provider = Provider; exports.Provider = Provider;
},{"../utils/address":59,"../utils/bignumber":60,"../utils/bytes":61,"../utils/errors":62,"../utils/hash":63,"../utils/properties":66,"../utils/rlp":67,"../utils/transaction":71,"../utils/utf8":73,"../wallet/wallet":79,"./networks":55}],57:[function(require,module,exports){ },{"../utils/address":59,"../utils/bignumber":60,"../utils/bytes":61,"../utils/errors":62,"../utils/hash":63,"../utils/properties":66,"../utils/rlp":67,"../utils/transaction":71,"../utils/utf8":73,"../utils/web":74,"../wallet/wallet":79,"./networks":55}],57:[function(require,module,exports){
'use strict'; 'use strict';
var __extends = (this && this.__extends) || (function () { var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf || var extendStatics = Object.setPrototypeOf ||
@ -12041,6 +12041,18 @@ var Coder = /** @class */ (function () {
} }
return Coder; return Coder;
}()); }());
// Clones the functionality of an existing Coder, but without a localName
var CoderAnonymous = /** @class */ (function (_super) {
__extends(CoderAnonymous, _super);
function CoderAnonymous(coder) {
var _this = _super.call(this, coder.coerceFunc, coder.name, coder.type, undefined, coder.dynamic) || this;
properties_1.defineReadOnly(_this, 'coder', coder);
return _this;
}
CoderAnonymous.prototype.encode = function (value) { return this.coder.encode(value); };
CoderAnonymous.prototype.decode = function (data, offset) { return this.coder.decode(data, offset); };
return CoderAnonymous;
}(Coder));
var CoderNull = /** @class */ (function (_super) { var CoderNull = /** @class */ (function (_super) {
__extends(CoderNull, _super); __extends(CoderNull, _super);
function CoderNull(coerceFunc, localName) { function CoderNull(coerceFunc, localName) {
@ -12465,7 +12477,7 @@ var CoderArray = /** @class */ (function (_super) {
} }
var coders = []; var coders = [];
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
coders.push(this.coder); coders.push(new CoderAnonymous(this.coder));
} }
var result = unpack(coders, data, offset); var result = unpack(coders, data, offset);
result.consumed += consumed; result.consumed += consumed;
@ -14329,6 +14341,71 @@ function fetchJson(connection, json, processFunc) {
}); });
} }
exports.fetchJson = fetchJson; exports.fetchJson = fetchJson;
function poll(func, options) {
if (!options) {
options = {};
}
if (options.floor == null) {
options.floor = 0;
}
if (options.ceiling == null) {
options.ceiling = 10000;
}
if (options.interval == null) {
options.interval = 250;
}
return new Promise(function (resolve, reject) {
var timer = null;
var done = false;
// Returns true if cancel was successful. Unsuccessful cancel means we're already done.
var cancel = function () {
if (done) {
return false;
}
done = true;
if (timer) {
clearTimeout(timer);
}
return true;
};
if (options.timeout) {
timer = setTimeout(function () {
if (cancel()) {
reject(new Error('timeout'));
}
}, options.timeout);
}
var attempt = 0;
function check() {
func().then(function (result) {
// If we have a result, or are allowed null then we're done
if (result !== undefined) {
if (cancel()) {
resolve(result);
}
// Otherwise, exponential back-off (up to 10s) our next request
}
else if (!done) {
attempt++;
var timeout = options.interval * parseInt(String(Math.random() * Math.pow(2, attempt)));
if (timeout < options.floor) {
timeout = options.floor;
}
if (timeout > options.ceiling) {
timeout = options.ceiling;
}
setTimeout(check, timeout);
}
}, function (error) {
if (cancel()) {
reject(error);
}
});
}
check();
});
}
exports.poll = poll;
},{"./base64":40,"./errors":62,"./utf8":73,"xmlhttprequest":45}],75:[function(require,module,exports){ },{"./base64":40,"./errors":62,"./utf8":73,"xmlhttprequest":45}],75:[function(require,module,exports){
'use strict'; 'use strict';

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

1056
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -11,6 +11,7 @@
"dist-bip39": "gulp bip39-it bip39-ja bip39-ko bip39-zh", "dist-bip39": "gulp bip39-it bip39-ja bip39-ko bip39-zh",
"dist-types": "gulp temp-types && dts-bundle --name ethers --main .tmp/index.d.ts --out ../dist/ethers.d.ts", "dist-types": "gulp temp-types && dts-bundle --name ethers --main .tmp/index.d.ts --out ../dist/ethers.d.ts",
"dist-version": "node -e \"let v = require('./package.json').version; require('fs').writeFileSync('./src.ts/_version.ts', 'export const version = \\\"' + v +'\\\";\\n')\"", "dist-version": "node -e \"let v = require('./package.json').version; require('fs').writeFileSync('./src.ts/_version.ts', 'export const version = \\\"' + v +'\\\";\\n')\"",
"eslint": "eslint index.js contracts/*.js providers/*.js utils/*.js wallet/*.js wordlists/*.js",
"test": "if [ \"$RUN_PHANTOMJS\" = \"1\" ]; then npm run-script test-phantomjs; else npm run-script test-node; fi", "test": "if [ \"$RUN_PHANTOMJS\" = \"1\" ]; then npm run-script test-phantomjs; else npm run-script test-node; fi",
"test-node": "mocha tests/test-*.js", "test-node": "mocha tests/test-*.js",
"test-phantomjs": "gulp minified tests && phantomjs --web-security=false ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./tests/test.html", "test-phantomjs": "gulp minified tests && phantomjs --web-security=false ./node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js ./tests/test.html",
@ -32,6 +33,8 @@
"browserify": "^16.2.2", "browserify": "^16.2.2",
"browserify-zlib": "^0.2.0", "browserify-zlib": "^0.2.0",
"dts-bundle": "^0.7.3", "dts-bundle": "^0.7.3",
"eslint": "^5.0.1",
"eslint-plugin-promise": "^3.8.0",
"ethereumjs-tx": "^1.3.5", "ethereumjs-tx": "^1.3.5",
"ethereumjs-util": "^5.2.0", "ethereumjs-util": "^5.2.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",

@ -28,6 +28,7 @@ var hash_1 = require("../utils/hash");
var networks_1 = require("./networks"); var networks_1 = require("./networks");
var properties_1 = require("../utils/properties"); var properties_1 = require("../utils/properties");
var transaction_1 = require("../utils/transaction"); var transaction_1 = require("../utils/transaction");
var web_1 = require("../utils/web");
var errors = __importStar(require("../utils/errors")); var errors = __importStar(require("../utils/errors"));
; ;
; ;
@ -372,31 +373,6 @@ var formatLog = {
function checkLog(log) { function checkLog(log) {
return check(formatLog, log); return check(formatLog, log);
} }
function stallPromise(allowNullFunc, executeFunc) {
return new Promise(function (resolve, reject) {
var attempt = 0;
function check() {
executeFunc().then(function (result) {
// If we have a result, or are allowed null then we're done
if (result || allowNullFunc()) {
resolve(result);
// Otherwise, exponential back-off (up to 10s) our next request
}
else {
attempt++;
var timeout = 500 + 250 * parseInt(String(Math.random() * (1 << attempt)));
if (timeout > 10000) {
timeout = 10000;
}
setTimeout(check, timeout);
}
}, function (error) {
reject(error);
});
}
check();
});
}
////////////////////////////// //////////////////////////////
// Event Serializeing // Event Serializeing
function recurse(object, convertFunc) { function recurse(object, convertFunc) {
@ -867,13 +843,14 @@ var Provider = /** @class */ (function () {
try { try {
var blockHash = bytes_1.hexlify(blockHashOrBlockTag); var blockHash = bytes_1.hexlify(blockHashOrBlockTag);
if (bytes_1.hexDataLength(blockHash) === 32) { if (bytes_1.hexDataLength(blockHash) === 32) {
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['b:' + blockHash.toLowerCase()] == null);
}, function () {
return _this.perform('getBlock', { blockHash: blockHash }).then(function (block) { return _this.perform('getBlock', { blockHash: blockHash }).then(function (block) {
if (block == null) { if (block == null) {
if (_this._emitted['b:' + blockHash.toLowerCase()] == null) {
return null; return null;
} }
return undefined;
}
return checkBlock(block); return checkBlock(block);
}); });
}); });
@ -881,16 +858,17 @@ var Provider = /** @class */ (function () {
} }
catch (error) { } catch (error) { }
try { try {
var blockTag = checkBlockTag(blockHashOrBlockTag); var blockNumber_1 = -128;
return stallPromise(function () { var blockTag_1 = checkBlockTag(blockHashOrBlockTag);
if (bytes_1.isHexString(blockTag)) { if (bytes_1.isHexString(blockTag_1)) {
var blockNumber = parseInt(blockTag.substring(2), 16); blockNumber_1 = parseInt(blockTag_1.substring(2), 16);
return blockNumber > _this._emitted.block;
} }
return true; return web_1.poll(function () {
}, function () { return _this.perform('getBlock', { blockTag: blockTag_1 }).then(function (block) {
return _this.perform('getBlock', { blockTag: blockTag }).then(function (block) {
if (block == null) { if (block == null) {
if (blockNumber_1 > _this._emitted.block) {
return undefined;
}
return null; return null;
} }
return checkBlock(block); return checkBlock(block);
@ -908,14 +886,15 @@ var Provider = /** @class */ (function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) { return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash; var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, function () {
return _this.perform('getTransaction', params).then(function (result) { return _this.perform('getTransaction', params).then(function (result) {
if (result != null) { if (result == null) {
result = checkTransactionResponse(result); if (_this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
} }
return result; return undefined;
}
return checkTransactionResponse(result);
}); });
}); });
}); });
@ -927,14 +906,15 @@ var Provider = /** @class */ (function () {
return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) { return properties_1.resolveProperties({ transactionHash: transactionHash }).then(function (_a) {
var transactionHash = _a.transactionHash; var transactionHash = _a.transactionHash;
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(function () { return web_1.poll(function () {
return (_this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, function () {
return _this.perform('getTransactionReceipt', params).then(function (result) { return _this.perform('getTransactionReceipt', params).then(function (result) {
if (result != null) { if (result == null) {
result = checkTransactionReceipt(result); if (_this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
} }
return result; return undefined;
}
return checkTransactionReceipt(result);
}); });
}); });
}); });

@ -11,6 +11,7 @@ import { hexDataLength, hexDataSlice, isHexString } from '../utils/bytes';
import { ParamType } from '../utils/abi-coder'; import { ParamType } from '../utils/abi-coder';
import { BigNumber, ConstantZero } from '../utils/bignumber'; import { BigNumber, ConstantZero } from '../utils/bignumber';
import { defineReadOnly, shallowCopy } from '../utils/properties'; import { defineReadOnly, shallowCopy } from '../utils/properties';
import { poll } from '../utils/web';
import * as errors from '../utils/errors'; import * as errors from '../utils/errors';
@ -330,6 +331,24 @@ export class Contract {
this._onerror = callback; this._onerror = callback;
} }
// @TODO: Allow timeout?
deployed() {
// If we were just deployed, we know the transaction we should occur in
if (this.deployTransaction) {
return this.deployTransaction.wait().then(() => {
return this;
});
}
// Otherwise, poll for our code to be deployed
return poll(() => {
return this.provider.getCode(this.address).then((code) => {
if (code === '0x') { return undefined; }
return this;
});
});
}
// @TODO: // @TODO:
// estimateFallback(overrides?: TransactionRequest): Promise<BigNumber> // estimateFallback(overrides?: TransactionRequest): Promise<BigNumber>

@ -109,7 +109,7 @@ export class JsonRpcSigner extends Signer {
} }
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> { sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse> {
let tx = shallowCopy(transaction); let tx: TransactionRequest = shallowCopy(transaction);
if (tx.from == null) { if (tx.from == null) {
tx.from = this.getAddress().then((address) => { tx.from = this.getAddress().then((address) => {

@ -13,6 +13,7 @@ import { hashMessage, namehash } from '../utils/hash';
import { getNetwork, Network, Networkish } from './networks'; import { getNetwork, Network, Networkish } from './networks';
import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties'; import { defineReadOnly, resolveProperties, shallowCopy } from '../utils/properties';
import { parse as parseTransaction, serialize as serializeTransaction, SignDigestFunc, Transaction } from '../utils/transaction'; import { parse as parseTransaction, serialize as serializeTransaction, SignDigestFunc, Transaction } from '../utils/transaction';
import { poll } from '../utils/web';
import * as errors from '../utils/errors'; import * as errors from '../utils/errors';
@ -505,35 +506,6 @@ function checkLog(log: any): any {
return check(formatLog, log); return check(formatLog, log);
} }
//////////////////////////////
// Defer Promises
type AllowNullFunc = () => boolean;
type ExecuteFunc = () => Promise<any>
function stallPromise(allowNullFunc: AllowNullFunc, executeFunc: ExecuteFunc): Promise<any> {
return new Promise(function(resolve, reject) {
var attempt = 0;
function check() {
executeFunc().then(function(result) {
// If we have a result, or are allowed null then we're done
if (result || allowNullFunc()) {
resolve(result);
// Otherwise, exponential back-off (up to 10s) our next request
} else {
attempt++;
var timeout = 500 + 250 * parseInt(String(Math.random() * (1 << attempt)));
if (timeout > 10000) { timeout = 10000; }
setTimeout(check, timeout);
}
}, function(error) {
reject(error);
});
}
check();
});
}
////////////////////////////// //////////////////////////////
// Event Serializeing // Event Serializeing
@ -1037,28 +1009,37 @@ export class Provider {
try { try {
var blockHash = hexlify(blockHashOrBlockTag); var blockHash = hexlify(blockHashOrBlockTag);
if (hexDataLength(blockHash) === 32) { if (hexDataLength(blockHash) === 32) {
return stallPromise(() => { return poll(() => {
return (this._emitted['b:' + blockHash.toLowerCase()] == null); return this.perform('getBlock', { blockHash: blockHash }).then((block) => {
}, () => { if (block == null) {
return this.perform('getBlock', {blockHash: blockHash}).then((block) => { if (this._emitted['b:' + blockHash.toLowerCase()] == null) {
if (block == null) { return null; } return null;
}
return undefined;
}
return checkBlock(block); return checkBlock(block);
}); });
}); });
} }
} catch (error) { } } catch (error) { }
try { try {
var blockTag = checkBlockTag(blockHashOrBlockTag); let blockNumber = -128;
return stallPromise(() => {
let blockTag = checkBlockTag(blockHashOrBlockTag);
if (isHexString(blockTag)) { if (isHexString(blockTag)) {
var blockNumber = parseInt(blockTag.substring(2), 16); blockNumber = parseInt(blockTag.substring(2), 16);
return blockNumber > this._emitted.block;
} }
return true;
}, () => { return poll(() => {
return this.perform('getBlock', { blockTag: blockTag }).then((block) => { return this.perform('getBlock', { blockTag: blockTag }).then((block) => {
if (block == null) { return null; } if (block == null) {
if (blockNumber > this._emitted.block) {
return undefined;
}
return null;
}
return checkBlock(block); return checkBlock(block);
}); });
}); });
@ -1073,12 +1054,15 @@ export class Provider {
return this.ready.then(() => { return this.ready.then(() => {
return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => { return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => {
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(() => { return poll(() => {
return (this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, () => {
return this.perform('getTransaction', params).then((result) => { return this.perform('getTransaction', params).then((result) => {
if (result != null) { result = checkTransactionResponse(result); } if (result == null) {
return result; if (this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
}
return undefined;
}
return checkTransactionResponse(result);
}); });
}); });
}); });
@ -1089,12 +1073,15 @@ export class Provider {
return this.ready.then(() => { return this.ready.then(() => {
return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => { return resolveProperties({ transactionHash: transactionHash }).then(({ transactionHash }) => {
var params = { transactionHash: checkHash(transactionHash) }; var params = { transactionHash: checkHash(transactionHash) };
return stallPromise(() => { return poll(() => {
return (this._emitted['t:' + transactionHash.toLowerCase()] == null);
}, () => {
return this.perform('getTransactionReceipt', params).then((result) => { return this.perform('getTransactionReceipt', params).then((result) => {
if (result != null) { result = checkTransactionReceipt(result); } if (result == null) {
return result; if (this._emitted['t:' + transactionHash.toLowerCase()] == null) {
return null;
}
return undefined;
}
return checkTransactionReceipt(result);
}); });
}); });
}); });

@ -120,3 +120,61 @@ export function fetchJson(connection: string | ConnectionInfo, json: string, pro
}); });
} }
export type PollOptions = {
timeout?: number,
floor?: number,
ceiling?: number,
interval?: number
};
export function poll(func: () => Promise<any>, options?: PollOptions): Promise<any> {
if (!options) { options = {}; }
if (options.floor == null) { options.floor = 0; }
if (options.ceiling == null) { options.ceiling = 10000; }
if (options.interval == null) { options.interval = 250; }
return new Promise(function(resolve, reject) {
let timer: any = null;
let done: boolean = false;
// Returns true if cancel was successful. Unsuccessful cancel means we're already done.
let cancel = (): boolean => {
if (done) { return false; }
done = true;
if (timer) { clearTimeout(timer); }
return true;
};
if (options.timeout) {
timer = setTimeout(() => {
if (cancel()) { reject(new Error('timeout')); }
}, options.timeout)
}
let attempt = 0;
function check() {
func().then(function(result) {
// If we have a result, or are allowed null then we're done
if (result !== undefined) {
if (cancel()) { resolve(result); }
// Otherwise, exponential back-off (up to 10s) our next request
} else if (!done) {
attempt++;
let timeout = options.interval * parseInt(String(Math.random() * Math.pow(2, attempt)));
if (timeout < options.floor) { timeout = options.floor; }
if (timeout > options.ceiling) { timeout = options.ceiling; }
setTimeout(check, timeout);
}
}, function(error) {
if (cancel()) { reject(error); }
});
}
check();
});
}

@ -286,6 +286,18 @@ var Coder = /** @class */ (function () {
} }
return Coder; return Coder;
}()); }());
// Clones the functionality of an existing Coder, but without a localName
var CoderAnonymous = /** @class */ (function (_super) {
__extends(CoderAnonymous, _super);
function CoderAnonymous(coder) {
var _this = _super.call(this, coder.coerceFunc, coder.name, coder.type, undefined, coder.dynamic) || this;
properties_1.defineReadOnly(_this, 'coder', coder);
return _this;
}
CoderAnonymous.prototype.encode = function (value) { return this.coder.encode(value); };
CoderAnonymous.prototype.decode = function (data, offset) { return this.coder.decode(data, offset); };
return CoderAnonymous;
}(Coder));
var CoderNull = /** @class */ (function (_super) { var CoderNull = /** @class */ (function (_super) {
__extends(CoderNull, _super); __extends(CoderNull, _super);
function CoderNull(coerceFunc, localName) { function CoderNull(coerceFunc, localName) {
@ -710,7 +722,7 @@ var CoderArray = /** @class */ (function (_super) {
} }
var coders = []; var coders = [];
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
coders.push(this.coder); coders.push(new CoderAnonymous(this.coder));
} }
var result = unpack(coders, data, offset); var result = unpack(coders, data, offset);
result.consumed += consumed; result.consumed += consumed;

@ -102,3 +102,68 @@ function fetchJson(connection, json, processFunc) {
}); });
} }
exports.fetchJson = fetchJson; exports.fetchJson = fetchJson;
function poll(func, options) {
if (!options) {
options = {};
}
if (options.floor == null) {
options.floor = 0;
}
if (options.ceiling == null) {
options.ceiling = 10000;
}
if (options.interval == null) {
options.interval = 250;
}
return new Promise(function (resolve, reject) {
var timer = null;
var done = false;
// Returns true if cancel was successful. Unsuccessful cancel means we're already done.
var cancel = function () {
if (done) {
return false;
}
done = true;
if (timer) {
clearTimeout(timer);
}
return true;
};
if (options.timeout) {
timer = setTimeout(function () {
if (cancel()) {
reject(new Error('timeout'));
}
}, options.timeout);
}
var attempt = 0;
function check() {
func().then(function (result) {
// If we have a result, or are allowed null then we're done
if (result !== undefined) {
if (cancel()) {
resolve(result);
}
// Otherwise, exponential back-off (up to 10s) our next request
}
else if (!done) {
attempt++;
var timeout = options.interval * parseInt(String(Math.random() * Math.pow(2, attempt)));
if (timeout < options.floor) {
timeout = options.floor;
}
if (timeout > options.ceiling) {
timeout = options.ceiling;
}
setTimeout(check, timeout);
}
}, function (error) {
if (cancel()) {
reject(error);
}
});
}
check();
});
}
exports.poll = poll;