@ethersproject/providers
Advanced tools
Comparing version 5.0.0-beta.124 to 5.0.0-beta.125
@@ -6,16 +6,29 @@ import { Block, BlockTag, BlockWithTransactions, EventType, Filter, FilterByBlockHash, Listener, Log, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider"; | ||
import { Formatter } from "./formatter"; | ||
export declare const defaultFormatter: Formatter; | ||
/** | ||
* EventType | ||
* - "block" | ||
* - "pending" | ||
* - "error" | ||
* - filter | ||
* - topics array | ||
* - transaction hash | ||
*/ | ||
declare type _Event = { | ||
listener: Listener; | ||
once: boolean; | ||
tag: string; | ||
}; | ||
export declare class BaseProvider extends Provider { | ||
private _network; | ||
private _events; | ||
_network: Network; | ||
_events: Array<_Event>; | ||
formatter: Formatter; | ||
protected _emitted: { | ||
_emitted: { | ||
[eventName: string]: number | "pending"; | ||
}; | ||
private _pollingInterval; | ||
private _poller; | ||
private _lastBlockNumber; | ||
private _fastBlockNumber; | ||
private _fastBlockNumberPromise; | ||
private _fastQueryDate; | ||
_pollingInterval: number; | ||
_poller: any; | ||
_lastBlockNumber: number; | ||
_fastBlockNumber: number; | ||
_fastBlockNumberPromise: Promise<number>; | ||
_fastQueryDate: number; | ||
/** | ||
@@ -30,5 +43,6 @@ * ready | ||
*/ | ||
protected ready: Promise<Network>; | ||
ready: Promise<Network>; | ||
constructor(network: Networkish | Promise<Network>); | ||
private _doPoll; | ||
static getFormatter(): Formatter; | ||
_doPoll(): void; | ||
resetEventsBlock(blockNumber: number): void; | ||
@@ -43,2 +57,5 @@ readonly network: Network; | ||
waitForTransaction(transactionHash: string, confirmations?: number): Promise<TransactionReceipt>; | ||
_runPerform(method: string, params: { | ||
[key: string]: () => any; | ||
}): Promise<any>; | ||
getBlockNumber(): Promise<number>; | ||
@@ -50,6 +67,8 @@ getGasPrice(): Promise<BigNumber>; | ||
getStorageAt(addressOrName: string | Promise<string>, position: BigNumberish | Promise<BigNumberish>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>; | ||
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse; | ||
sendTransaction(signedTransaction: string | Promise<string>): Promise<TransactionResponse>; | ||
_wrapTransaction(tx: Transaction, hash?: string): TransactionResponse; | ||
call(transaction: TransactionRequest, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>; | ||
estimateGas(transaction: TransactionRequest): Promise<BigNumber>; | ||
_getTransactionRequest(transaction: TransactionRequest | Promise<TransactionRequest>): Promise<Transaction>; | ||
_getFilter(filter: Filter | FilterByBlockHash | Promise<Filter | FilterByBlockHash>): Promise<Filter | FilterByBlockHash>; | ||
call(transaction: TransactionRequest | Promise<TransactionRequest>, blockTag?: BlockTag | Promise<BlockTag>): Promise<string>; | ||
estimateGas(transaction: TransactionRequest | Promise<TransactionRequest>): Promise<BigNumber>; | ||
_getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>, includeTransactions?: boolean): Promise<Block | BlockWithTransactions>; | ||
@@ -60,6 +79,6 @@ getBlock(blockHashOrBlockTag: BlockTag | string | Promise<BlockTag | string>): Promise<Block>; | ||
getTransactionReceipt(transactionHash: string): Promise<TransactionReceipt>; | ||
getLogs(filter: Filter | FilterByBlockHash): Promise<Array<Log>>; | ||
getLogs(filter: Filter | FilterByBlockHash | Promise<Filter | FilterByBlockHash>): Promise<Array<Log>>; | ||
getEtherPrice(): Promise<number>; | ||
private _resolveNames; | ||
private _getResolver; | ||
_getBlockTag(blockTag: BlockTag | Promise<BlockTag>): Promise<BlockTag>; | ||
_getResolver(name: string): Promise<string>; | ||
resolveName(name: string | Promise<string>): Promise<string>; | ||
@@ -69,12 +88,14 @@ lookupAddress(address: string | Promise<string>): Promise<string>; | ||
perform(method: string, params: any): Promise<any>; | ||
protected _startPending(): void; | ||
protected _stopPending(): void; | ||
private _addEventListener; | ||
on(eventName: EventType, listener: Listener): Provider; | ||
once(eventName: EventType, listener: Listener): Provider; | ||
_startPending(): void; | ||
_stopPending(): void; | ||
_checkPolling(): void; | ||
_addEventListener(eventName: EventType, listener: Listener, once: boolean): this; | ||
on(eventName: EventType, listener: Listener): this; | ||
once(eventName: EventType, listener: Listener): this; | ||
emit(eventName: EventType, ...args: Array<any>): boolean; | ||
listenerCount(eventName?: EventType): number; | ||
listeners(eventName?: EventType): Array<Listener>; | ||
off(eventName: EventType, listener?: Listener): Provider; | ||
removeAllListeners(eventName?: EventType): Provider; | ||
off(eventName: EventType, listener?: Listener): this; | ||
removeAllListeners(eventName?: EventType): this; | ||
} | ||
export {}; |
@@ -80,2 +80,6 @@ "use strict"; | ||
} | ||
else if (properties_1.isNamedInstance(abstract_provider_1.ForkEvent, eventName)) { | ||
errors.warn("not implemented"); | ||
throw new Error("not implemented"); | ||
} | ||
else if (eventName && typeof (eventName) === "object") { | ||
@@ -91,9 +95,11 @@ return "filter:" + (eventName.address || "*") + ":" + serializeTopics(eventName.topics || []); | ||
} | ||
exports.defaultFormatter = new formatter_1.Formatter(); | ||
var defaultFormatter = null; | ||
var BaseProvider = /** @class */ (function (_super) { | ||
__extends(BaseProvider, _super); | ||
function BaseProvider(network) { | ||
var _this = _super.call(this) || this; | ||
errors.checkNew(_this, abstract_provider_1.Provider); | ||
_this.formatter = exports.defaultFormatter; | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, abstract_provider_1.Provider); | ||
_this = _super.call(this) || this; | ||
_this.formatter = _newTarget.getFormatter(); | ||
if (network instanceof Promise) { | ||
@@ -125,2 +131,8 @@ properties_1.defineReadOnly(_this, "ready", network.then(function (network) { | ||
} | ||
BaseProvider.getFormatter = function () { | ||
if (defaultFormatter == null) { | ||
defaultFormatter = new formatter_1.Formatter(); | ||
} | ||
return defaultFormatter; | ||
}; | ||
BaseProvider.prototype._doPoll = function () { | ||
@@ -325,35 +337,37 @@ var _this = this; | ||
}; | ||
BaseProvider.prototype.getBlockNumber = function () { | ||
BaseProvider.prototype._runPerform = function (method, params) { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return _this.perform("getBlockNumber", {}).then(function (result) { | ||
var value = parseInt(result); | ||
if (value != result) { | ||
throw new Error("invalid response - getBlockNumber"); | ||
} | ||
_this._setFastBlockNumber(value); | ||
return value; | ||
// Execute all the functions now that we are "ready" | ||
Object.keys(params).forEach(function (key) { | ||
params[key] = params[key](); | ||
}); | ||
return properties_1.resolveProperties(params).then(function (params) { | ||
return _this.perform(method, params); | ||
}); | ||
}); | ||
}; | ||
BaseProvider.prototype.getGasPrice = function () { | ||
BaseProvider.prototype.getBlockNumber = function () { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return _this.perform("getGasPrice", {}).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
return this._runPerform("getBlockNumber", {}).then(function (result) { | ||
var value = parseInt(result); | ||
if (value != result) { | ||
throw new Error("invalid response - getBlockNumber"); | ||
} | ||
_this._setFastBlockNumber(value); | ||
return value; | ||
}); | ||
}; | ||
BaseProvider.prototype.getGasPrice = function () { | ||
return this._runPerform("getGasPrice", {}).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
}; | ||
BaseProvider.prototype.getBalance = function (addressOrName, blockTag) { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(function (_a) { | ||
var addressOrName = _a.addressOrName, blockTag = _a.blockTag; | ||
return _this.resolveName(addressOrName).then(function (address) { | ||
var params = { address: address, blockTag: _this.formatter.blockTag(blockTag) }; | ||
return _this.perform("getBalance", params).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
}); | ||
}); | ||
return this._runPerform("getBalance", { | ||
address: function () { return _this.resolveName(addressOrName); }, | ||
blockTag: function () { return _this._getBlockTag(blockTag); } | ||
}).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
@@ -363,12 +377,7 @@ }; | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(function (_a) { | ||
var addressOrName = _a.addressOrName, blockTag = _a.blockTag; | ||
return _this.resolveName(addressOrName).then(function (address) { | ||
var params = { address: address, blockTag: _this.formatter.blockTag(blockTag) }; | ||
return _this.perform("getTransactionCount", params).then(function (result) { | ||
return bignumber_1.BigNumber.from(result).toNumber(); | ||
}); | ||
}); | ||
}); | ||
return this._runPerform("getTransactionCount", { | ||
address: function () { return _this.resolveName(addressOrName); }, | ||
blockTag: function () { return _this._getBlockTag(blockTag); } | ||
}).then(function (result) { | ||
return bignumber_1.BigNumber.from(result).toNumber(); | ||
}); | ||
@@ -378,12 +387,7 @@ }; | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ addressOrName: addressOrName, blockTag: blockTag }).then(function (_a) { | ||
var addressOrName = _a.addressOrName, blockTag = _a.blockTag; | ||
return _this.resolveName(addressOrName).then(function (address) { | ||
var params = { address: address, blockTag: _this.formatter.blockTag(blockTag) }; | ||
return _this.perform("getCode", params).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
}); | ||
}); | ||
return this._runPerform("getCode", { | ||
address: function () { return _this.resolveName(addressOrName); }, | ||
blockTag: function () { return _this._getBlockTag(blockTag); } | ||
}).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
@@ -393,36 +397,10 @@ }; | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ addressOrName: addressOrName, position: position, blockTag: blockTag }).then(function (_a) { | ||
var addressOrName = _a.addressOrName, position = _a.position, blockTag = _a.blockTag; | ||
return _this.resolveName(addressOrName).then(function (address) { | ||
var params = { | ||
address: address, | ||
blockTag: _this.formatter.blockTag(blockTag), | ||
position: bytes_1.hexValue(position), | ||
}; | ||
return _this.perform("getStorageAt", params).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
}); | ||
}); | ||
return this._runPerform("getStorageAt", { | ||
address: function () { return _this.resolveName(addressOrName); }, | ||
blockTag: function () { return _this._getBlockTag(blockTag); }, | ||
position: function () { return Promise.resolve(position).then(function (p) { return bytes_1.hexValue(p); }); } | ||
}).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
}; | ||
BaseProvider.prototype.sendTransaction = function (signedTransaction) { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ signedTransaction: signedTransaction }).then(function (_a) { | ||
var signedTransaction = _a.signedTransaction; | ||
var params = { signedTransaction: bytes_1.hexlify(signedTransaction) }; | ||
return _this.perform("sendTransaction", params).then(function (hash) { | ||
return _this._wrapTransaction(_this.formatter.transaction(signedTransaction), hash); | ||
}, function (error) { | ||
error.transaction = _this.formatter.transaction(signedTransaction); | ||
if (error.transaction.hash) { | ||
error.transactionHash = error.transaction.hash; | ||
} | ||
throw error; | ||
}); | ||
}); | ||
}); | ||
}; | ||
// This should be called by any subclass wrapping a TransactionResponse | ||
@@ -464,85 +442,125 @@ BaseProvider.prototype._wrapTransaction = function (tx, hash) { | ||
}; | ||
BaseProvider.prototype.call = function (transaction, blockTag) { | ||
BaseProvider.prototype.sendTransaction = function (signedTransaction) { | ||
var _this = this; | ||
var tx = properties_1.shallowCopy(transaction); | ||
return this.ready.then(function () { | ||
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 = { blockTag: _this.formatter.blockTag(blockTag), transaction: _this.formatter.transactionRequest(tx) }; | ||
return _this.perform("call", params).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
}); | ||
return this._runPerform("getStorageAt", { | ||
signedTransaction: function () { return Promise.resolve(signedTransaction).then(function (t) { return bytes_1.hexlify(t); }); } | ||
}).then(function (result) { | ||
return _this._wrapTransaction(_this.formatter.transaction(signedTransaction), result); | ||
}, function (error) { | ||
error.transaction = _this.formatter.transaction(signedTransaction); | ||
if (error.transaction.hash) { | ||
error.transactionHash = error.transaction.hash; | ||
} | ||
throw error; | ||
}); | ||
}; | ||
BaseProvider.prototype._getTransactionRequest = function (transaction) { | ||
var _this = this; | ||
return Promise.resolve(transaction).then(function (t) { | ||
var tx = {}; | ||
["from", "to"].forEach(function (key) { | ||
if (t[key] == null) { | ||
return; | ||
} | ||
tx[key] = Promise.resolve(t[key]).then(function (a) { return (a ? _this.resolveName(a) : null); }); | ||
}); | ||
["data", "gasPrice", "value"].forEach(function (key) { | ||
if (t[key] == null) { | ||
return; | ||
} | ||
tx[key] = t[key]; | ||
}); | ||
return properties_1.resolveProperties(tx).then(function (t) { return _this.formatter.transactionRequest(t); }); | ||
}); | ||
}; | ||
BaseProvider.prototype.estimateGas = function (transaction) { | ||
BaseProvider.prototype._getFilter = function (filter) { | ||
var _this = this; | ||
var tx = { | ||
to: transaction.to, | ||
from: transaction.from, | ||
data: transaction.data, | ||
gasPrice: transaction.gasPrice, | ||
value: transaction.value | ||
}; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties(tx).then(function (tx) { | ||
return _this._resolveNames(tx, ["to", "from"]).then(function (tx) { | ||
var params = { transaction: _this.formatter.transactionRequest(tx) }; | ||
return _this.perform("estimateGas", params).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
}); | ||
return Promise.resolve(filter).then(function (f) { | ||
var filter = {}; | ||
if (f.address != null) { | ||
filter.address = _this.resolveName(f.address); | ||
} | ||
if (f.blockHash != null) { | ||
filter.blockHash = f.blockHash; | ||
} | ||
["fromBlock", "toBlock"].forEach(function (key) { | ||
if (f[key] == null) { | ||
return; | ||
} | ||
filter[key] = _this._getBlockTag(f[key]); | ||
}); | ||
return properties_1.resolveProperties(filter).then(function (f) { return _this.formatter.filter(f); }); | ||
}); | ||
}; | ||
BaseProvider.prototype.call = function (transaction, blockTag) { | ||
var _this = this; | ||
return this._runPerform("call", { | ||
tx: function () { return _this._getTransactionRequest(transaction); }, | ||
blockTag: function () { return _this._getBlockTag(blockTag); } | ||
}).then(function (result) { | ||
return bytes_1.hexlify(result); | ||
}); | ||
}; | ||
BaseProvider.prototype.estimateGas = function (transaction) { | ||
var _this = this; | ||
return this._runPerform("estimateGas", { | ||
tx: function () { return _this._getTransactionRequest(transaction); } | ||
}).then(function (result) { | ||
return bignumber_1.BigNumber.from(result); | ||
}); | ||
}; | ||
BaseProvider.prototype._getBlock = function (blockHashOrBlockTag, includeTransactions) { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties({ blockHashOrBlockTag: blockHashOrBlockTag }).then(function (_a) { | ||
var blockHashOrBlockTag = _a.blockHashOrBlockTag; | ||
try { | ||
var blockHash_1 = bytes_1.hexlify(blockHashOrBlockTag); | ||
if (bytes_1.hexDataLength(blockHash_1) === 32) { | ||
return web_1.poll(function () { | ||
return _this.perform("getBlock", { blockHash: blockHash_1, includeTransactions: !!includeTransactions }).then(function (block) { | ||
if (block == null) { | ||
if (_this._emitted["b:" + blockHash_1] == null) { | ||
return null; | ||
} | ||
return undefined; | ||
} | ||
if (includeTransactions) { | ||
return _this.formatter.blockWithTransactions(block); | ||
} | ||
return _this.formatter.block(block); | ||
}); | ||
}, { onceBlock: _this }); | ||
return _this._getBlockTag(blockHashOrBlockTag).then(function (blockHashOrBlockTag) { | ||
var params = { | ||
includeTransactions: !!includeTransactions | ||
}; | ||
// Exactly one of blockHash or blockTag will be set | ||
var blockHash = null; | ||
var blockTag = null; | ||
// If blockTag is a number (not "latest", etc), this is the block number | ||
var blockNumber = -128; | ||
if (bytes_1.isHexString(blockHashOrBlockTag, 32)) { | ||
params.blockHash = blockHashOrBlockTag; | ||
} | ||
else { | ||
try { | ||
params.blockTag = _this.formatter.blockTag(blockHashOrBlockTag); | ||
if (bytes_1.isHexString(params.blockTag)) { | ||
blockNumber = parseInt(params.blockTag.substring(2), 16); | ||
} | ||
} | ||
catch (error) { | ||
errors.throwError("invalid block hash or block tag", "blockHashOrBlockTag", blockHashOrBlockTag); | ||
} | ||
} | ||
catch (error) { } | ||
try { | ||
var blockNumber_1 = -128; | ||
var blockTag_1 = _this.formatter.blockTag(blockHashOrBlockTag); | ||
if (bytes_1.isHexString(blockTag_1)) { | ||
blockNumber_1 = parseInt(blockTag_1.substring(2), 16); | ||
} | ||
return web_1.poll(function () { | ||
return _this.perform("getBlock", { blockTag: blockTag_1, includeTransactions: !!includeTransactions }).then(function (block) { | ||
if (block == null) { | ||
if (blockNumber_1 <= _this._emitted.block) { | ||
return undefined; | ||
return web_1.poll(function () { | ||
return _this.perform("getBlock", params).then(function (block) { | ||
// Block was not found | ||
if (block == null) { | ||
// For blockhashes, if we didn't say it existed, that blockhash may | ||
// not exist. If we did see it though, perhaps from a log, we know | ||
// it exists, and this node is just not caught up yet. | ||
if (blockHash) { | ||
if (_this._emitted["b:" + blockHash] == null) { | ||
return null; | ||
} | ||
return null; | ||
} | ||
if (includeTransactions) { | ||
return _this.formatter.blockWithTransactions(block); | ||
// For block tags, if we are asking for a future block, we return null | ||
if (blockTag) { | ||
if (blockNumber > _this._emitted.block) { | ||
return null; | ||
} | ||
} | ||
return _this.formatter.block(block); | ||
}); | ||
}, { onceBlock: _this }); | ||
} | ||
catch (error) { } | ||
throw new Error("invalid block hash or block tag"); | ||
// Retry on the next block | ||
return undefined; | ||
} | ||
// Add transactions | ||
if (includeTransactions) { | ||
return _this.formatter.blockWithTransactions(block); | ||
} | ||
return _this.formatter.block(block); | ||
}); | ||
}, { onceBlock: _this }); | ||
}); | ||
@@ -633,32 +651,23 @@ }); | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return properties_1.resolveProperties(filter).then(function (filter) { | ||
return _this._resolveNames(filter, ["address"]).then(function (filter) { | ||
var params = { filter: _this.formatter.filter(filter) }; | ||
return _this.perform("getLogs", params).then(function (result) { | ||
return formatter_1.Formatter.arrayOf(_this.formatter.filterLog.bind(_this.formatter))(result); | ||
}); | ||
}); | ||
}); | ||
return this._runPerform("getLogs", { | ||
filter: function () { return _this._getFilter(filter); } | ||
}).then(function (result) { | ||
return formatter_1.Formatter.arrayOf(_this.formatter.filterLog.bind(_this.formatter))(result); | ||
}); | ||
}; | ||
BaseProvider.prototype.getEtherPrice = function () { | ||
var _this = this; | ||
return this.ready.then(function () { | ||
return _this.perform("getEtherPrice", {}).then(function (result) { | ||
// @TODO: Check valid float | ||
return result; | ||
}); | ||
return this._runPerform("getEtherPrice", {}).then(function (result) { | ||
return result; | ||
}); | ||
}; | ||
// @TODO: Could probably use resolveProperties instead? | ||
BaseProvider.prototype._resolveNames = function (object, keys) { | ||
var _this = this; | ||
var promises = []; | ||
var result = properties_1.shallowCopy(object); | ||
keys.forEach(function (key) { | ||
if (result[key] == null) { | ||
return; | ||
} | ||
promises.push(_this.resolveName(result[key]).then(function (address) { | ||
/* | ||
private _resolveNames(object: any, keys: Array<string>): Promise<{ [key: string]: string }> { | ||
let promises: Array<Promise<void>> = []; | ||
let result: { [key: string ]: string } = shallowCopy(object); | ||
keys.forEach((key) => { | ||
if (result[key] == null) { return; } | ||
promises.push(this.resolveName(result[key]).then((address: string) => { | ||
result[key] = address; | ||
@@ -668,3 +677,24 @@ return; | ||
}); | ||
return Promise.all(promises).then(function () { return result; }); | ||
return Promise.all(promises).then(() => { return result; }); | ||
} | ||
*/ | ||
BaseProvider.prototype._getBlockTag = function (blockTag) { | ||
var _this = this; | ||
if (blockTag instanceof Promise) { | ||
return blockTag.then(function (b) { return _this._getBlockTag(b); }); | ||
} | ||
if (typeof (blockTag) === "number" && blockTag < 0) { | ||
if (blockTag % 1) { | ||
errors.throwArgumentError("invalid BlockTag", "blockTag", blockTag); | ||
} | ||
return this._getFastBlockNumber().then(function (bn) { | ||
bn += blockTag; | ||
if (bn < 0) { | ||
bn = 0; | ||
} | ||
return _this.formatter.blockTag(bn); | ||
}); | ||
} | ||
return Promise.resolve(this.formatter.blockTag(blockTag)); | ||
}; | ||
@@ -691,5 +721,3 @@ BaseProvider.prototype._getResolver = function (name) { | ||
if (name instanceof Promise) { | ||
return name.then(function (addressOrName) { | ||
return _this.resolveName(addressOrName); | ||
}); | ||
return name.then(function (addressOrName) { return _this.resolveName(addressOrName); }); | ||
} | ||
@@ -717,5 +745,3 @@ // If it is already an address, nothing to resolve | ||
if (address instanceof Promise) { | ||
return address.then(function (address) { | ||
return _this.lookupAddress(address); | ||
}); | ||
return address.then(function (address) { return _this.lookupAddress(address); }); | ||
} | ||
@@ -759,4 +785,3 @@ address = this.formatter.address(address); | ||
BaseProvider.prototype.perform = function (method, params) { | ||
errors.throwError(method + " not implemented", errors.NOT_IMPLEMENTED, { operation: method }); | ||
return null; | ||
return errors.throwError(method + " not implemented", errors.NOT_IMPLEMENTED, { operation: method }); | ||
}; | ||
@@ -768,2 +793,8 @@ BaseProvider.prototype._startPending = function () { | ||
}; | ||
BaseProvider.prototype._checkPolling = function () { | ||
var pollingEvents = this._events.filter(function (event) { | ||
return (event.tag.indexOf(":") >= 0 || event.tag === "block" || event.tag === "pending"); | ||
}); | ||
this.polling = (pollingEvents.length > 0); | ||
}; | ||
BaseProvider.prototype._addEventListener = function (eventName, listener, once) { | ||
@@ -778,11 +809,11 @@ this._events.push({ | ||
} | ||
this.polling = true; | ||
// Do we still now have any events that require polling? | ||
this._checkPolling(); | ||
return this; | ||
}; | ||
BaseProvider.prototype.on = function (eventName, listener) { | ||
this._addEventListener(eventName, listener, false); | ||
return this; | ||
return this._addEventListener(eventName, listener, false); | ||
}; | ||
BaseProvider.prototype.once = function (eventName, listener) { | ||
this._addEventListener(eventName, listener, true); | ||
return this; | ||
return this._addEventListener(eventName, listener, true); | ||
}; | ||
@@ -807,5 +838,4 @@ BaseProvider.prototype.emit = function (eventName) { | ||
}); | ||
if (this.listenerCount() === 0) { | ||
this.polling = false; | ||
} | ||
// Do we still have any events that require polling? ("once" events remove themselves) | ||
this._checkPolling(); | ||
return result; | ||
@@ -850,5 +880,4 @@ }; | ||
} | ||
if (this.listenerCount() === 0) { | ||
this.polling = false; | ||
} | ||
// Do we still have any events that require polling? | ||
this._checkPolling(); | ||
return this; | ||
@@ -870,5 +899,4 @@ }; | ||
} | ||
if (this._events.length === 0) { | ||
this.polling = false; | ||
} | ||
// Do we still have any events that require polling? | ||
this._checkPolling(); | ||
return this; | ||
@@ -879,2 +907,1 @@ }; | ||
exports.BaseProvider = BaseProvider; | ||
properties_1.defineReadOnly(abstract_provider_1.Provider, "inherits", properties_1.inheritable(abstract_provider_1.Provider)); |
@@ -89,4 +89,6 @@ "use strict"; | ||
function EtherscanProvider(network, apiKey) { | ||
var _this = _super.call(this, network) || this; | ||
errors.checkNew(_this, EtherscanProvider); | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, EtherscanProvider); | ||
_this = _super.call(this, network) || this; | ||
var name = "invalid"; | ||
@@ -93,0 +95,0 @@ if (_this.network) { |
import { BaseProvider } from "./base-provider"; | ||
export declare class FallbackProvider extends BaseProvider { | ||
private _providers; | ||
constructor(providers: Array<BaseProvider>); | ||
readonly providers: Array<BaseProvider>; | ||
readonly weights: Array<number>; | ||
readonly quorum: number; | ||
constructor(providers: Array<BaseProvider>, quorum?: number, weights?: Array<number>); | ||
perform(method: string, params: { | ||
@@ -7,0 +8,0 @@ [name: string]: any; |
@@ -24,3 +24,6 @@ "use strict"; | ||
var errors = __importStar(require("@ethersproject/errors")); | ||
var random_1 = require("@ethersproject/random"); | ||
var properties_1 = require("@ethersproject/properties"); | ||
var base_provider_1 = require("./base-provider"); | ||
function now() { return (new Date()).getTime(); } | ||
// Returns: | ||
@@ -55,9 +58,56 @@ // - true is all networks match | ||
} | ||
function serialize(result) { | ||
if (Array.isArray(result)) { | ||
return JSON.stringify(result.map(function (r) { return serialize(r); })); | ||
} | ||
else if (result === null) { | ||
return "null"; | ||
} | ||
else if (typeof (result) === "object") { | ||
var bare_1 = {}; | ||
var keys = Object.keys(result); | ||
keys.sort(); | ||
keys.forEach(function (key) { | ||
var value = result[key]; | ||
if (typeof (value) === "function") { | ||
return; | ||
} | ||
bare_1[key] = serialize(value); | ||
}); | ||
return JSON.stringify(bare_1); | ||
} | ||
return JSON.stringify(result); | ||
} | ||
var nextRid = 1; | ||
var FallbackProvider = /** @class */ (function (_super) { | ||
__extends(FallbackProvider, _super); | ||
function FallbackProvider(providers) { | ||
function FallbackProvider(providers, quorum, weights) { | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, FallbackProvider); | ||
if (providers.length === 0) { | ||
throw new Error("no providers"); | ||
errors.throwArgumentError("missing providers", "providers", providers); | ||
} | ||
if (weights != null && weights.length !== providers.length) { | ||
errors.throwArgumentError("too many weights", "weights", weights); | ||
} | ||
else if (!weights) { | ||
weights = providers.map(function (p) { return 1; }); | ||
} | ||
else { | ||
weights.forEach(function (w) { | ||
if (w % 1 || w > 512 || w < 1) { | ||
errors.throwArgumentError("invalid weight; must be integer in [1, 512]", "weights", weights); | ||
} | ||
}); | ||
} | ||
var total = weights.reduce(function (accum, w) { return (accum + w); }); | ||
if (quorum == null) { | ||
quorum = total / 2; | ||
} | ||
else { | ||
if (quorum > total) { | ||
errors.throwArgumentError("quorum will always fail; larger than total weight", "quorum", quorum); | ||
} | ||
} | ||
// All networks are ready, we can know the network for certain | ||
@@ -69,3 +119,3 @@ var ready = checkNetworks(providers.map(function (p) { return p.network; })); | ||
else { | ||
// The network won"t be known until all child providers know | ||
// The network won't be known until all child providers know | ||
var ready_1 = Promise.all(providers.map(function (p) { return p.getNetwork(); })).then(function (networks) { | ||
@@ -79,35 +129,101 @@ if (!checkNetworks(networks)) { | ||
} | ||
errors.checkNew(_this, FallbackProvider); | ||
// Preserve a copy, so we do not get mutated | ||
_this._providers = providers.slice(0); | ||
properties_1.defineReadOnly(_this, "providers", Object.freeze(providers.slice())); | ||
properties_1.defineReadOnly(_this, "quorum", quorum); | ||
properties_1.defineReadOnly(_this, "weights", Object.freeze(weights.slice())); | ||
return _this; | ||
} | ||
Object.defineProperty(FallbackProvider.prototype, "providers", { | ||
get: function () { | ||
// Return a copy, so we don"t get mutated | ||
return this._providers.slice(0); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
FallbackProvider.prototype.perform = function (method, params) { | ||
// Creates a copy of the providers array | ||
var providers = this.providers; | ||
var _this = this; | ||
var T0 = now(); | ||
var runners = (random_1.shuffled(this.providers)).map(function (provider, i) { | ||
var weight = _this.weights[i]; | ||
var rid = nextRid++; | ||
return { | ||
run: function () { | ||
var t0 = now(); | ||
var start = t0 - T0; | ||
_this.emit("debug", "perform", rid, { weight: weight, start: start, provider: provider, method: method, params: params }); | ||
return provider.perform(method, params).then(function (result) { | ||
var duration = now() - t0; | ||
_this.emit("debug", "result", rid, { duration: duration, result: result }); | ||
return { weight: weight, result: result }; | ||
}, function (error) { | ||
var duration = now() - t0; | ||
_this.emit("debug", "error", rid, { duration: duration, error: error }); | ||
return { weight: weight, error: error }; | ||
}); | ||
}, | ||
weight: weight | ||
}; | ||
}); | ||
// Broadcast transactions to all backends, any that succeed is good enough | ||
if (method === "sendTransaction") { | ||
return Promise.all(runners.map(function (r) { return r.run(); })).then(function (results) { | ||
for (var i = 0; i < results.length; i++) { | ||
var result = results[i]; | ||
if (result.result) { | ||
return result.result; | ||
} | ||
} | ||
return Promise.reject(results[0].error); | ||
}); | ||
} | ||
// Otherwise query backends (randomly) until we have a quorum agreement | ||
// on the correct result | ||
return new Promise(function (resolve, reject) { | ||
var firstError = null; | ||
function next() { | ||
if (!providers.length) { | ||
reject(firstError); | ||
// How much weight is inflight | ||
var inflightWeight = 0; | ||
// All results, indexed by the serialized response. | ||
var results = {}; | ||
var next = function () { | ||
if (runners.length === 0) { | ||
return; | ||
} | ||
var provider = providers.shift(); | ||
provider.perform(method, params).then(function (result) { | ||
return resolve(result); | ||
}).catch(function (error) { | ||
if (!firstError) { | ||
firstError = error; | ||
var runner = runners.shift(); | ||
inflightWeight += runner.weight; | ||
runner.run().then(function (result) { | ||
if (results === null) { | ||
return; | ||
} | ||
inflightWeight -= runner.weight; | ||
if (result.error) { | ||
if (firstError == null) { | ||
firstError = result.error; | ||
} | ||
} | ||
else { | ||
var unique = serialize(result.result); | ||
if (results[unique] == null) { | ||
results[unique] = []; | ||
} | ||
results[unique].push(result); | ||
// Do any results meet our quroum? | ||
for (var u in results) { | ||
var weight = results[u].reduce(function (accum, r) { return (accum + r.weight); }, 0); | ||
if (weight >= _this.quorum) { | ||
var result_1 = results[u][0].result; | ||
_this.emit("debug", "quorum", -1, { weight: weight, result: result_1 }); | ||
resolve(result_1); | ||
results = null; | ||
return; | ||
} | ||
} | ||
} | ||
// Out of options; give up | ||
if (runners.length === 0 && inflightWeight === 0) { | ||
reject(firstError); | ||
return; | ||
} | ||
// Queue up the next round | ||
setTimeout(next, 0); | ||
}); | ||
} | ||
// Fire off requests until we could possibly meet quorum | ||
if (inflightWeight < _this.quorum) { | ||
setTimeout(next, 0); | ||
return; | ||
} | ||
}; | ||
// bootstrap firing requests | ||
next(); | ||
@@ -114,0 +230,0 @@ }); |
@@ -19,3 +19,4 @@ "use strict"; | ||
function Formatter() { | ||
errors.checkNew(this, Formatter); | ||
var _newTarget = this.constructor; | ||
errors.checkNew(_newTarget, Formatter); | ||
this.formats = this.getDefaultFormats(); | ||
@@ -107,2 +108,3 @@ } | ||
toBlock: Formatter.allowNull(blockTag, undefined), | ||
blockHash: Formatter.allowNull(hash, undefined), | ||
address: Formatter.allowNull(address, undefined), | ||
@@ -109,0 +111,0 @@ topics: Formatter.allowNull(this.topics.bind(this), undefined), |
@@ -5,2 +5,3 @@ import { Block, BlockTag, EventType, Filter, Log, Listener, Provider, TransactionReceipt, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider"; | ||
import { BaseProvider } from "./base-provider"; | ||
import { AlchemyProvider } from "./alchemy-provider"; | ||
import { EtherscanProvider } from "./etherscan-provider"; | ||
@@ -11,4 +12,5 @@ import { FallbackProvider } from "./fallback-provider"; | ||
import { JsonRpcProvider, JsonRpcSigner } from "./json-rpc-provider"; | ||
import { NodesmithProvider } from "./nodesmith-provider"; | ||
import { Web3Provider } from "./web3-provider"; | ||
import { AsyncSendable } from "./web3-provider"; | ||
export { Provider, BaseProvider, FallbackProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, Web3Provider, IpcProvider, JsonRpcSigner, getNetwork, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable, Network, Networkish }; | ||
export { Provider, BaseProvider, FallbackProvider, AlchemyProvider, EtherscanProvider, InfuraProvider, JsonRpcProvider, NodesmithProvider, Web3Provider, IpcProvider, JsonRpcSigner, getNetwork, Block, BlockTag, EventType, Filter, Log, Listener, TransactionReceipt, TransactionRequest, TransactionResponse, AsyncSendable, Network, Networkish }; |
@@ -9,2 +9,4 @@ "use strict"; | ||
exports.BaseProvider = base_provider_1.BaseProvider; | ||
var alchemy_provider_1 = require("./alchemy-provider"); | ||
exports.AlchemyProvider = alchemy_provider_1.AlchemyProvider; | ||
var etherscan_provider_1 = require("./etherscan-provider"); | ||
@@ -21,3 +23,5 @@ exports.EtherscanProvider = etherscan_provider_1.EtherscanProvider; | ||
exports.JsonRpcSigner = json_rpc_provider_1.JsonRpcSigner; | ||
var nodesmith_provider_1 = require("./nodesmith-provider"); | ||
exports.NodesmithProvider = nodesmith_provider_1.NodesmithProvider; | ||
var web3_provider_1 = require("./web3-provider"); | ||
exports.Web3Provider = web3_provider_1.Web3Provider; |
@@ -1,9 +0,7 @@ | ||
import { Networkish } from "@ethersproject/networks"; | ||
import { JsonRpcProvider, JsonRpcSigner } from "./json-rpc-provider"; | ||
export declare class InfuraProvider extends JsonRpcProvider { | ||
import { Network } from "@ethersproject/networks"; | ||
import { UrlJsonRpcProvider } from "./url-json-rpc-provider"; | ||
export declare class InfuraProvider extends UrlJsonRpcProvider { | ||
readonly projectId: string; | ||
constructor(network?: Networkish, projectId?: string); | ||
protected _startPending(): void; | ||
getSigner(address?: string): JsonRpcSigner; | ||
listAccounts(): Promise<Array<string>>; | ||
static getApiKey(apiKey: string): string; | ||
static getUrl(network: Network, apiKey: string): string; | ||
} |
@@ -23,17 +23,28 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var bytes_1 = require("@ethersproject/bytes"); | ||
var errors = __importStar(require("@ethersproject/errors")); | ||
var networks_1 = require("@ethersproject/networks"); | ||
var properties_1 = require("@ethersproject/properties"); | ||
var json_rpc_provider_1 = require("./json-rpc-provider"); | ||
var defaultProjectId = "7d0d81d0919f4f05b9ab6634be01ee73"; | ||
var url_json_rpc_provider_1 = require("./url-json-rpc-provider"); | ||
var defaultProjectId = "84842078b09946638c03157f83405213"; | ||
var InfuraProvider = /** @class */ (function (_super) { | ||
__extends(InfuraProvider, _super); | ||
function InfuraProvider(network, projectId) { | ||
var _this = this; | ||
var standard = networks_1.getNetwork((network == null) ? "homestead" : network); | ||
if (projectId == null) { | ||
projectId = defaultProjectId; | ||
function InfuraProvider() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
} | ||
Object.defineProperty(InfuraProvider.prototype, "projectId", { | ||
get: function () { return this.apiKey; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
InfuraProvider.getApiKey = function (apiKey) { | ||
if (apiKey == null) { | ||
return defaultProjectId; | ||
} | ||
if (!bytes_1.isHexString(apiKey, 16)) { | ||
errors.throwArgumentError("invalid projectId", "projectId", apiKey); | ||
} | ||
return apiKey; | ||
}; | ||
InfuraProvider.getUrl = function (network, apiKey) { | ||
var host = null; | ||
switch (standard.name) { | ||
switch (network.name) { | ||
case "homestead": | ||
@@ -60,19 +71,6 @@ host = "mainnet.infura.io"; | ||
} | ||
_this = _super.call(this, "https://" + host + "/v3/" + projectId, standard) || this; | ||
errors.checkNew(_this, InfuraProvider); | ||
properties_1.defineReadOnly(_this, "projectId", projectId); | ||
return _this; | ||
} | ||
InfuraProvider.prototype._startPending = function () { | ||
console.log("WARNING: INFURA does not support pending filters"); | ||
return "https://" + host + "/v3/" + apiKey; | ||
}; | ||
InfuraProvider.prototype.getSigner = function (address) { | ||
errors.throwError("INFURA does not support signing", errors.UNSUPPORTED_OPERATION, { operation: "getSigner" }); | ||
return null; | ||
}; | ||
InfuraProvider.prototype.listAccounts = function () { | ||
return Promise.resolve([]); | ||
}; | ||
return InfuraProvider; | ||
}(json_rpc_provider_1.JsonRpcProvider)); | ||
}(url_json_rpc_provider_1.UrlJsonRpcProvider)); | ||
exports.InfuraProvider = InfuraProvider; |
@@ -33,3 +33,5 @@ "use strict"; | ||
function IpcProvider(path, network) { | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, IpcProvider); | ||
if (path == null) { | ||
@@ -39,3 +41,2 @@ errors.throwError("missing path", errors.MISSING_ARGUMENT, { arg: "path" }); | ||
_this = _super.call(this, "ipc://" + path, network) || this; | ||
errors.checkNew(_this, IpcProvider); | ||
properties_1.defineReadOnly(_this, "path", path); | ||
@@ -42,0 +43,0 @@ return _this; |
@@ -10,4 +10,4 @@ import { BlockTag, Provider, TransactionRequest, TransactionResponse } from "@ethersproject/abstract-provider"; | ||
readonly provider: JsonRpcProvider; | ||
private _index; | ||
private _address; | ||
_index: number; | ||
_address: string; | ||
constructor(constructorGuard: any, provider: JsonRpcProvider, addressOrIndex?: string | number); | ||
@@ -25,12 +25,16 @@ connect(provider: Provider): JsonRpcSigner; | ||
} | ||
declare class UncheckedJsonRpcSigner extends JsonRpcSigner { | ||
sendTransaction(transaction: TransactionRequest): Promise<TransactionResponse>; | ||
} | ||
export declare class JsonRpcProvider extends BaseProvider { | ||
readonly connection: ConnectionInfo; | ||
private _pendingFilter; | ||
_pendingFilter: Promise<number>; | ||
constructor(url?: ConnectionInfo | string, network?: Networkish); | ||
getSigner(addressOrIndex?: string | number): JsonRpcSigner; | ||
getUncheckedSigner(addressOrIndex?: string | number): UncheckedJsonRpcSigner; | ||
listAccounts(): Promise<Array<string>>; | ||
send(method: string, params: any): Promise<any>; | ||
perform(method: string, params: any): Promise<any>; | ||
protected _startPending(): void; | ||
protected _stopPending(): void; | ||
_startPending(): void; | ||
_stopPending(): void; | ||
static hexlifyTransaction(transaction: TransactionRequest, allowExtra?: { | ||
@@ -42,1 +46,2 @@ [key: string]: boolean; | ||
} | ||
export {}; |
@@ -24,2 +24,3 @@ "use strict"; | ||
var abstract_signer_1 = require("@ethersproject/abstract-signer"); | ||
var bignumber_1 = require("@ethersproject/bignumber"); | ||
var bytes_1 = require("@ethersproject/bytes"); | ||
@@ -59,4 +60,6 @@ var errors = __importStar(require("@ethersproject/errors")); | ||
function JsonRpcSigner(constructorGuard, provider, addressOrIndex) { | ||
var _this = _super.call(this) || this; | ||
errors.checkNew(_this, JsonRpcSigner); | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, JsonRpcSigner); | ||
_this = _super.call(this) || this; | ||
if (constructorGuard !== _constructorGuard) { | ||
@@ -231,3 +234,5 @@ throw new Error("do not call the JsonRpcSigner constructor directly; use provider.getSigner"); | ||
function JsonRpcProvider(url, network) { | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, JsonRpcProvider); | ||
// One parameter, but it is a network name, so swap it with the URL | ||
@@ -248,6 +253,10 @@ if (typeof (url) === "string") { | ||
setTimeout(function () { | ||
_this.send("net_version", []).then(function (result) { | ||
return resolve(networks_1.getNetwork(parseInt(result))); | ||
_this.send("eth_chainId", []).then(function (result) { | ||
resolve(networks_1.getNetwork(bignumber_1.BigNumber.from(result).toNumber())); | ||
}).catch(function (error) { | ||
reject(error); | ||
_this.send("net_version", []).then(function (result) { | ||
resolve(networks_1.getNetwork(bignumber_1.BigNumber.from(result).toNumber())); | ||
}).catch(function (error) { | ||
reject(errors.makeError("could not detect network", errors.NETWORK_ERROR, {})); | ||
}); | ||
}); | ||
@@ -258,3 +267,2 @@ }); | ||
} | ||
errors.checkNew(_this, JsonRpcProvider); | ||
// Default URL | ||
@@ -277,2 +285,5 @@ if (!url) { | ||
}; | ||
JsonRpcProvider.prototype.getUncheckedSigner = function (addressOrIndex) { | ||
return this.getSigner(addressOrIndex).connectUnchecked(); | ||
}; | ||
JsonRpcProvider.prototype.listAccounts = function () { | ||
@@ -345,6 +356,3 @@ var _this = this; | ||
case "getTransactionReceipt": | ||
return this.send("eth_getTransactionReceipt", [params.transactionHash]).then(function (v) { | ||
console.log(v); | ||
return v; | ||
}); | ||
return this.send("eth_getTransactionReceipt", [params.transactionHash]); | ||
case "call": | ||
@@ -351,0 +359,0 @@ return this.send("eth_call", [JsonRpcProvider.hexlifyTransaction(params.transaction, { from: true }), params.blockTag]); |
{ | ||
"name": "@ethersproject/providers", | ||
"version": "5.0.0-beta.124", | ||
"version": "5.0.0-beta.125", | ||
"description": "Error utility functions for ethers.", | ||
@@ -24,2 +24,3 @@ "main": "index.js", | ||
"@ethersproject/properties": "^5.0.0-beta.119", | ||
"@ethersproject/random": "^5.0.0-beta.119", | ||
"@ethersproject/rlp": "^5.0.0-beta.119", | ||
@@ -39,3 +40,3 @@ "@ethersproject/strings": "^5.0.0-beta.119", | ||
}, | ||
"tarballHash": "0x6a324d78294d3c81eb29f899c574b25ccc6b6e1d74e72a9e921817c7ddfa3552" | ||
"tarballHash": "0xad249680d25daaf9e3b55b7923c460ffb512a74678b9b53684e81144a1ea1b03" | ||
} |
@@ -36,6 +36,7 @@ "use strict"; | ||
function Web3Provider(web3Provider, network) { | ||
var _this = | ||
var _newTarget = this.constructor; | ||
var _this = this; | ||
errors.checkNew(_newTarget, Web3Provider); | ||
// HTTP has a host; IPC has a path. | ||
_super.call(this, web3Provider.host || web3Provider.path || "", network) || this; | ||
errors.checkNew(_this, Web3Provider); | ||
_this = _super.call(this, web3Provider.host || web3Provider.path || "", network) || this; | ||
if (web3Provider) { | ||
@@ -42,0 +43,0 @@ if (web3Provider.sendAsync) { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
132529
33
3036
15
+ Added@ethersproject/random@5.7.0(transitive)