admin: update dist files
This commit is contained in:
parent
180221574c
commit
f6d155c820
@ -3,6 +3,11 @@ Change Log
|
||||
|
||||
This change log is maintained by `src.ts/_admin/update-changelog.ts` but may also be manually updated.
|
||||
|
||||
ethers/v6.9.1 (2023-12-19 04:53)
|
||||
--------------------------------
|
||||
|
||||
- Fix uncatchable issue when sending transactions over JSON-RPC and provide some retry-recovery for missing v ([#4513](https://github.com/ethers-io/ethers.js/issues/4513); [1802215](https://github.com/ethers-io/ethers.js/commit/180221574c5d2af9ad85404af4fab8752d3d5029)).
|
||||
|
||||
ethers/v6.9.0 (2023-11-27 06:15)
|
||||
--------------------------------
|
||||
|
||||
|
80
dist/ethers.js
vendored
80
dist/ethers.js
vendored
@ -3,7 +3,7 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
const version = "6.9.0";
|
||||
const version = "6.9.1";
|
||||
|
||||
/**
|
||||
* Property helper functions.
|
||||
@ -18048,16 +18048,19 @@ class AbstractProvider {
|
||||
// No explicit network was set and this is our first time
|
||||
if (this.#networkPromise == null) {
|
||||
// Detect the current network (shared with all calls)
|
||||
const detectNetwork = this._detectNetwork().then((network) => {
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}, (error) => {
|
||||
// Reset the networkPromise on failure, so we will try again
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
const detectNetwork = (async () => {
|
||||
try {
|
||||
const network = await this._detectNetwork();
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
catch (error) {
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
this.#networkPromise = detectNetwork;
|
||||
return (await detectNetwork).clone();
|
||||
}
|
||||
@ -19444,12 +19447,45 @@ class JsonRpcSigner extends AbstractSigner {
|
||||
// for it; it should show up very quickly
|
||||
return await (new Promise((resolve, reject) => {
|
||||
const timeouts = [1000, 100];
|
||||
let invalids = 0;
|
||||
const checkTx = async () => {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
try {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// If we were cancelled: stop polling.
|
||||
// If the data is bad: the node returns bad transactions
|
||||
// If the network changed: calling again will also fail
|
||||
// If unsupported: likely destroyed
|
||||
if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") ||
|
||||
isError(error, "NETWORK_ERROR" )) {
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
// Stop-gap for misbehaving backends; see #4513
|
||||
if (isError(error, "INVALID_ARGUMENT")) {
|
||||
invalids++;
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
if (invalids > 10) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify anyone that cares; but we will try again, since
|
||||
// it is likely an intermittent service error
|
||||
this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
|
||||
}
|
||||
// Wait another 4 seconds
|
||||
this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
|
||||
@ -19527,7 +19563,7 @@ class JsonRpcApiProvider extends AbstractProvider {
|
||||
if (this.#drainTimer) {
|
||||
return;
|
||||
}
|
||||
// If we aren't using batching, no hard in sending it immeidately
|
||||
// If we aren't using batching, no harm in sending it immediately
|
||||
const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
|
||||
this.#drainTimer = setTimeout(() => {
|
||||
this.#drainTimer = null;
|
||||
@ -19690,9 +19726,15 @@ class JsonRpcApiProvider extends AbstractProvider {
|
||||
// If we are ready, use ``send``, which enabled requests to be batched
|
||||
if (this.ready) {
|
||||
this.#pendingDetectNetwork = (async () => {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
try {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
this.#pendingDetectNetwork = null;
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
return await this.#pendingDetectNetwork;
|
||||
}
|
||||
|
2
dist/ethers.js.map
vendored
2
dist/ethers.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/ethers.min.js
vendored
2
dist/ethers.min.js
vendored
File diff suppressed because one or more lines are too long
80
dist/ethers.umd.js
vendored
80
dist/ethers.umd.js
vendored
@ -9,7 +9,7 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
const version = "6.9.0";
|
||||
const version = "6.9.1";
|
||||
|
||||
/**
|
||||
* Property helper functions.
|
||||
@ -18054,16 +18054,19 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
// No explicit network was set and this is our first time
|
||||
if (this.#networkPromise == null) {
|
||||
// Detect the current network (shared with all calls)
|
||||
const detectNetwork = this._detectNetwork().then((network) => {
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}, (error) => {
|
||||
// Reset the networkPromise on failure, so we will try again
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
const detectNetwork = (async () => {
|
||||
try {
|
||||
const network = await this._detectNetwork();
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
catch (error) {
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
this.#networkPromise = detectNetwork;
|
||||
return (await detectNetwork).clone();
|
||||
}
|
||||
@ -19450,12 +19453,45 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
// for it; it should show up very quickly
|
||||
return await (new Promise((resolve, reject) => {
|
||||
const timeouts = [1000, 100];
|
||||
let invalids = 0;
|
||||
const checkTx = async () => {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
try {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// If we were cancelled: stop polling.
|
||||
// If the data is bad: the node returns bad transactions
|
||||
// If the network changed: calling again will also fail
|
||||
// If unsupported: likely destroyed
|
||||
if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") ||
|
||||
isError(error, "NETWORK_ERROR" )) {
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
// Stop-gap for misbehaving backends; see #4513
|
||||
if (isError(error, "INVALID_ARGUMENT")) {
|
||||
invalids++;
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
if (invalids > 10) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify anyone that cares; but we will try again, since
|
||||
// it is likely an intermittent service error
|
||||
this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
|
||||
}
|
||||
// Wait another 4 seconds
|
||||
this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
|
||||
@ -19533,7 +19569,7 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
if (this.#drainTimer) {
|
||||
return;
|
||||
}
|
||||
// If we aren't using batching, no hard in sending it immeidately
|
||||
// If we aren't using batching, no harm in sending it immediately
|
||||
const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
|
||||
this.#drainTimer = setTimeout(() => {
|
||||
this.#drainTimer = null;
|
||||
@ -19696,9 +19732,15 @@ const __$G = (typeof globalThis !== 'undefined' ? globalThis: typeof window !==
|
||||
// If we are ready, use ``send``, which enabled requests to be batched
|
||||
if (this.ready) {
|
||||
this.#pendingDetectNetwork = (async () => {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
try {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
this.#pendingDetectNetwork = null;
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
return await this.#pendingDetectNetwork;
|
||||
}
|
||||
|
2
dist/ethers.umd.js.map
vendored
2
dist/ethers.umd.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/ethers.umd.min.js
vendored
2
dist/ethers.umd.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/wordlists-extra.js
vendored
2
dist/wordlists-extra.js
vendored
@ -100,7 +100,7 @@ const rotlBL = (h, l, s) => (h << (s - 32)) | (l >>> (64 - s));
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
const version = "6.9.0";
|
||||
const version = "6.9.1";
|
||||
|
||||
/**
|
||||
* Property helper functions.
|
||||
|
2
dist/wordlists-extra.js.map
vendored
2
dist/wordlists-extra.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/wordlists-extra.min.js
vendored
2
dist/wordlists-extra.min.js
vendored
File diff suppressed because one or more lines are too long
2
lib.commonjs/_tests/test-provider-jsonrpc.d.ts
vendored
Normal file
2
lib.commonjs/_tests/test-provider-jsonrpc.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=test-provider-jsonrpc.d.ts.map
|
1
lib.commonjs/_tests/test-provider-jsonrpc.d.ts.map
Normal file
1
lib.commonjs/_tests/test-provider-jsonrpc.d.ts.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"test-provider-jsonrpc.d.ts","sourceRoot":"","sources":["../../src.ts/_tests/test-provider-jsonrpc.ts"],"names":[],"mappings":""}
|
160
lib.commonjs/_tests/test-provider-jsonrpc.js
Normal file
160
lib.commonjs/_tests/test-provider-jsonrpc.js
Normal file
@ -0,0 +1,160 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const tslib_1 = require("tslib");
|
||||
const assert_1 = tslib_1.__importDefault(require("assert"));
|
||||
const index_js_1 = require("../index.js");
|
||||
const StatusMessages = {
|
||||
200: "OK",
|
||||
400: "BAD REQUEST",
|
||||
500: "SERVER ERROR",
|
||||
};
|
||||
const wallet = new index_js_1.Wallet((0, index_js_1.id)("test"));
|
||||
function createProvider(testFunc) {
|
||||
let blockNumber = 1;
|
||||
const ticker = setInterval(() => { blockNumber++; }, 100);
|
||||
if (ticker.unref) {
|
||||
ticker.unref();
|
||||
}
|
||||
const processReq = (req) => {
|
||||
let result = testFunc(req.method, req.params, blockNumber);
|
||||
if (result === undefined) {
|
||||
switch (req.method) {
|
||||
case "eth_blockNumber":
|
||||
result = blockNumber;
|
||||
break;
|
||||
case "eth_chainId":
|
||||
result = "0x1337";
|
||||
break;
|
||||
case "eth_accounts":
|
||||
result = [wallet.address];
|
||||
break;
|
||||
default:
|
||||
console.log("****", req);
|
||||
return { id: index_js_1.id, error: "unsupported", jsonrpc: "2.0" };
|
||||
}
|
||||
}
|
||||
return { id: req.id, result, jsonrpc: "2.0" };
|
||||
};
|
||||
const req = new index_js_1.FetchRequest("http:/\/localhost:8082/");
|
||||
req.getUrlFunc = async (_req, signal) => {
|
||||
const req = JSON.parse(_req.hasBody() ? (0, index_js_1.toUtf8String)(_req.body) : "");
|
||||
let statusCode = 200;
|
||||
const headers = {};
|
||||
let resp;
|
||||
try {
|
||||
if (Array.isArray(req)) {
|
||||
resp = req.map((r) => processReq(r));
|
||||
}
|
||||
else {
|
||||
resp = processReq(req);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
statusCode = 500;
|
||||
resp = error.message;
|
||||
}
|
||||
const body = (0, index_js_1.toUtf8Bytes)(JSON.stringify(resp));
|
||||
return {
|
||||
statusCode,
|
||||
statusMessage: StatusMessages[statusCode],
|
||||
headers, body
|
||||
};
|
||||
};
|
||||
return new index_js_1.JsonRpcProvider(req, undefined, { cacheTimeout: -1 });
|
||||
}
|
||||
describe("Ensure Catchable Errors", function () {
|
||||
it("Can catch bad broadcast replies", async function () {
|
||||
this.timeout(15000);
|
||||
const txInfo = {
|
||||
chainId: 1337,
|
||||
gasLimit: 100000,
|
||||
maxFeePerGas: 2000000000,
|
||||
maxPriorityFeePerGas: 1000000000,
|
||||
to: wallet.address,
|
||||
value: 1,
|
||||
};
|
||||
const txSign = await wallet.signTransaction(txInfo);
|
||||
const txObj = index_js_1.Transaction.from(txSign);
|
||||
let count = 0;
|
||||
const provider = createProvider((method, params, blockNumber) => {
|
||||
switch (method) {
|
||||
case "eth_sendTransaction":
|
||||
return txObj.hash;
|
||||
case "eth_getTransactionByHash": {
|
||||
count++;
|
||||
// First time; fail!
|
||||
if (count === 1) {
|
||||
throw (0, index_js_1.makeError)("Faux Error", "SERVER_ERROR", {
|
||||
request: ({})
|
||||
});
|
||||
}
|
||||
// Second time; return null
|
||||
if (count === 2) {
|
||||
return null;
|
||||
}
|
||||
// Return a valid tx...
|
||||
const result = Object.assign({}, txObj.toJSON(), txObj.signature.toJSON(), { hash: txObj.hash, from: wallet.address });
|
||||
// ...eventually mined
|
||||
if (count > 4) {
|
||||
result.blockNumber = blockNumber;
|
||||
result.blockHash = (0, index_js_1.id)("test");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
const signer = await provider.getSigner();
|
||||
const tx = await signer.sendTransaction(txInfo);
|
||||
(0, assert_1.default)(tx);
|
||||
});
|
||||
it("Missing v is recovered", async function () {
|
||||
this.timeout(15000);
|
||||
const txInfo = {
|
||||
chainId: 1337,
|
||||
gasLimit: 100000,
|
||||
maxFeePerGas: 2000000000,
|
||||
maxPriorityFeePerGas: 1000000000,
|
||||
to: wallet.address,
|
||||
value: 1,
|
||||
};
|
||||
const txSign = await wallet.signTransaction(txInfo);
|
||||
const txObj = index_js_1.Transaction.from(txSign);
|
||||
let count = 0;
|
||||
// A provider which is mocked to return a "missing v"
|
||||
// in getTransaction
|
||||
const provider = createProvider((method, params, blockNumber) => {
|
||||
switch (method) {
|
||||
case "eth_sendTransaction":
|
||||
return txObj.hash;
|
||||
case "eth_getTransactionByHash": {
|
||||
count++;
|
||||
// The fully valid tx response
|
||||
const result = Object.assign({}, txObj.toJSON(), txObj.signature.toJSON(), { hash: txObj.hash, from: wallet.address, sig: null });
|
||||
// First time; fail with a missing v!
|
||||
if (count < 2) {
|
||||
delete result.v;
|
||||
}
|
||||
// Debug
|
||||
result._count = count;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
// Track any "missing v" error
|
||||
let missingV = null;
|
||||
provider.on("error", (e) => {
|
||||
if ((0, index_js_1.isError)(e, "UNKNOWN_ERROR") && (0, index_js_1.isError)(e.error, "INVALID_ARGUMENT")) {
|
||||
if (e.error.argument === "signature" && e.error.shortMessage === "missing v") {
|
||||
missingV = e.error;
|
||||
}
|
||||
}
|
||||
});
|
||||
const signer = await provider.getSigner();
|
||||
const tx = await signer.sendTransaction(txInfo);
|
||||
assert_1.default.ok(!!tx, "we got a transaction");
|
||||
assert_1.default.ok(!!missingV, "missing v error present");
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=test-provider-jsonrpc.js.map
|
1
lib.commonjs/_tests/test-provider-jsonrpc.js.map
Normal file
1
lib.commonjs/_tests/test-provider-jsonrpc.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -5,5 +5,5 @@ exports.version = void 0;
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
exports.version = "6.9.0";
|
||||
exports.version = "6.9.1";
|
||||
//# sourceMappingURL=_version.js.map
|
File diff suppressed because one or more lines are too long
@ -544,16 +544,19 @@ class AbstractProvider {
|
||||
// No explicit network was set and this is our first time
|
||||
if (this.#networkPromise == null) {
|
||||
// Detect the current network (shared with all calls)
|
||||
const detectNetwork = this._detectNetwork().then((network) => {
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}, (error) => {
|
||||
// Reset the networkPromise on failure, so we will try again
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
const detectNetwork = (async () => {
|
||||
try {
|
||||
const network = await this._detectNetwork();
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
catch (error) {
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
this.#networkPromise = detectNetwork;
|
||||
return (await detectNetwork).clone();
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -136,12 +136,45 @@ class JsonRpcSigner extends abstract_signer_js_1.AbstractSigner {
|
||||
// for it; it should show up very quickly
|
||||
return await (new Promise((resolve, reject) => {
|
||||
const timeouts = [1000, 100];
|
||||
let invalids = 0;
|
||||
const checkTx = async () => {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
try {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// If we were cancelled: stop polling.
|
||||
// If the data is bad: the node returns bad transactions
|
||||
// If the network changed: calling again will also fail
|
||||
// If unsupported: likely destroyed
|
||||
if ((0, index_js_5.isError)(error, "CANCELLED") || (0, index_js_5.isError)(error, "BAD_DATA") ||
|
||||
(0, index_js_5.isError)(error, "NETWORK_ERROR" || (0, index_js_5.isError)(error, "UNSUPPORTED_OPERATION"))) {
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
// Stop-gap for misbehaving backends; see #4513
|
||||
if ((0, index_js_5.isError)(error, "INVALID_ARGUMENT")) {
|
||||
invalids++;
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
if (invalids > 10) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify anyone that cares; but we will try again, since
|
||||
// it is likely an intermittent service error
|
||||
this.provider.emit("error", (0, index_js_5.makeError)("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
|
||||
}
|
||||
// Wait another 4 seconds
|
||||
this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
|
||||
@ -220,7 +253,7 @@ class JsonRpcApiProvider extends abstract_provider_js_1.AbstractProvider {
|
||||
if (this.#drainTimer) {
|
||||
return;
|
||||
}
|
||||
// If we aren't using batching, no hard in sending it immeidately
|
||||
// If we aren't using batching, no harm in sending it immediately
|
||||
const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
|
||||
this.#drainTimer = setTimeout(() => {
|
||||
this.#drainTimer = null;
|
||||
@ -383,9 +416,15 @@ class JsonRpcApiProvider extends abstract_provider_js_1.AbstractProvider {
|
||||
// If we are ready, use ``send``, which enabled requests to be batched
|
||||
if (this.ready) {
|
||||
this.#pendingDetectNetwork = (async () => {
|
||||
const result = network_js_1.Network.from((0, index_js_5.getBigInt)(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
try {
|
||||
const result = network_js_1.Network.from((0, index_js_5.getBigInt)(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
this.#pendingDetectNetwork = null;
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
return await this.#pendingDetectNetwork;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
2
lib.esm/_tests/test-provider-jsonrpc.d.ts
vendored
Normal file
2
lib.esm/_tests/test-provider-jsonrpc.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=test-provider-jsonrpc.d.ts.map
|
1
lib.esm/_tests/test-provider-jsonrpc.d.ts.map
Normal file
1
lib.esm/_tests/test-provider-jsonrpc.d.ts.map
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"test-provider-jsonrpc.d.ts","sourceRoot":"","sources":["../../src.ts/_tests/test-provider-jsonrpc.ts"],"names":[],"mappings":""}
|
157
lib.esm/_tests/test-provider-jsonrpc.js
Normal file
157
lib.esm/_tests/test-provider-jsonrpc.js
Normal file
@ -0,0 +1,157 @@
|
||||
import assert from "assert";
|
||||
import { id, isError, makeError, toUtf8Bytes, toUtf8String, FetchRequest, JsonRpcProvider, Transaction, Wallet } from "../index.js";
|
||||
const StatusMessages = {
|
||||
200: "OK",
|
||||
400: "BAD REQUEST",
|
||||
500: "SERVER ERROR",
|
||||
};
|
||||
const wallet = new Wallet(id("test"));
|
||||
function createProvider(testFunc) {
|
||||
let blockNumber = 1;
|
||||
const ticker = setInterval(() => { blockNumber++; }, 100);
|
||||
if (ticker.unref) {
|
||||
ticker.unref();
|
||||
}
|
||||
const processReq = (req) => {
|
||||
let result = testFunc(req.method, req.params, blockNumber);
|
||||
if (result === undefined) {
|
||||
switch (req.method) {
|
||||
case "eth_blockNumber":
|
||||
result = blockNumber;
|
||||
break;
|
||||
case "eth_chainId":
|
||||
result = "0x1337";
|
||||
break;
|
||||
case "eth_accounts":
|
||||
result = [wallet.address];
|
||||
break;
|
||||
default:
|
||||
console.log("****", req);
|
||||
return { id, error: "unsupported", jsonrpc: "2.0" };
|
||||
}
|
||||
}
|
||||
return { id: req.id, result, jsonrpc: "2.0" };
|
||||
};
|
||||
const req = new FetchRequest("http:/\/localhost:8082/");
|
||||
req.getUrlFunc = async (_req, signal) => {
|
||||
const req = JSON.parse(_req.hasBody() ? toUtf8String(_req.body) : "");
|
||||
let statusCode = 200;
|
||||
const headers = {};
|
||||
let resp;
|
||||
try {
|
||||
if (Array.isArray(req)) {
|
||||
resp = req.map((r) => processReq(r));
|
||||
}
|
||||
else {
|
||||
resp = processReq(req);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
statusCode = 500;
|
||||
resp = error.message;
|
||||
}
|
||||
const body = toUtf8Bytes(JSON.stringify(resp));
|
||||
return {
|
||||
statusCode,
|
||||
statusMessage: StatusMessages[statusCode],
|
||||
headers, body
|
||||
};
|
||||
};
|
||||
return new JsonRpcProvider(req, undefined, { cacheTimeout: -1 });
|
||||
}
|
||||
describe("Ensure Catchable Errors", function () {
|
||||
it("Can catch bad broadcast replies", async function () {
|
||||
this.timeout(15000);
|
||||
const txInfo = {
|
||||
chainId: 1337,
|
||||
gasLimit: 100000,
|
||||
maxFeePerGas: 2000000000,
|
||||
maxPriorityFeePerGas: 1000000000,
|
||||
to: wallet.address,
|
||||
value: 1,
|
||||
};
|
||||
const txSign = await wallet.signTransaction(txInfo);
|
||||
const txObj = Transaction.from(txSign);
|
||||
let count = 0;
|
||||
const provider = createProvider((method, params, blockNumber) => {
|
||||
switch (method) {
|
||||
case "eth_sendTransaction":
|
||||
return txObj.hash;
|
||||
case "eth_getTransactionByHash": {
|
||||
count++;
|
||||
// First time; fail!
|
||||
if (count === 1) {
|
||||
throw makeError("Faux Error", "SERVER_ERROR", {
|
||||
request: ({})
|
||||
});
|
||||
}
|
||||
// Second time; return null
|
||||
if (count === 2) {
|
||||
return null;
|
||||
}
|
||||
// Return a valid tx...
|
||||
const result = Object.assign({}, txObj.toJSON(), txObj.signature.toJSON(), { hash: txObj.hash, from: wallet.address });
|
||||
// ...eventually mined
|
||||
if (count > 4) {
|
||||
result.blockNumber = blockNumber;
|
||||
result.blockHash = id("test");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
const signer = await provider.getSigner();
|
||||
const tx = await signer.sendTransaction(txInfo);
|
||||
assert(tx);
|
||||
});
|
||||
it("Missing v is recovered", async function () {
|
||||
this.timeout(15000);
|
||||
const txInfo = {
|
||||
chainId: 1337,
|
||||
gasLimit: 100000,
|
||||
maxFeePerGas: 2000000000,
|
||||
maxPriorityFeePerGas: 1000000000,
|
||||
to: wallet.address,
|
||||
value: 1,
|
||||
};
|
||||
const txSign = await wallet.signTransaction(txInfo);
|
||||
const txObj = Transaction.from(txSign);
|
||||
let count = 0;
|
||||
// A provider which is mocked to return a "missing v"
|
||||
// in getTransaction
|
||||
const provider = createProvider((method, params, blockNumber) => {
|
||||
switch (method) {
|
||||
case "eth_sendTransaction":
|
||||
return txObj.hash;
|
||||
case "eth_getTransactionByHash": {
|
||||
count++;
|
||||
// The fully valid tx response
|
||||
const result = Object.assign({}, txObj.toJSON(), txObj.signature.toJSON(), { hash: txObj.hash, from: wallet.address, sig: null });
|
||||
// First time; fail with a missing v!
|
||||
if (count < 2) {
|
||||
delete result.v;
|
||||
}
|
||||
// Debug
|
||||
result._count = count;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
// Track any "missing v" error
|
||||
let missingV = null;
|
||||
provider.on("error", (e) => {
|
||||
if (isError(e, "UNKNOWN_ERROR") && isError(e.error, "INVALID_ARGUMENT")) {
|
||||
if (e.error.argument === "signature" && e.error.shortMessage === "missing v") {
|
||||
missingV = e.error;
|
||||
}
|
||||
}
|
||||
});
|
||||
const signer = await provider.getSigner();
|
||||
const tx = await signer.sendTransaction(txInfo);
|
||||
assert.ok(!!tx, "we got a transaction");
|
||||
assert.ok(!!missingV, "missing v error present");
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=test-provider-jsonrpc.js.map
|
1
lib.esm/_tests/test-provider-jsonrpc.js.map
Normal file
1
lib.esm/_tests/test-provider-jsonrpc.js.map
Normal file
File diff suppressed because one or more lines are too long
@ -2,5 +2,5 @@
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
export const version = "6.9.0";
|
||||
export const version = "6.9.1";
|
||||
//# sourceMappingURL=_version.js.map
|
File diff suppressed because one or more lines are too long
@ -540,16 +540,19 @@ export class AbstractProvider {
|
||||
// No explicit network was set and this is our first time
|
||||
if (this.#networkPromise == null) {
|
||||
// Detect the current network (shared with all calls)
|
||||
const detectNetwork = this._detectNetwork().then((network) => {
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}, (error) => {
|
||||
// Reset the networkPromise on failure, so we will try again
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
const detectNetwork = (async () => {
|
||||
try {
|
||||
const network = await this._detectNetwork();
|
||||
this.emit("network", network, null);
|
||||
return network;
|
||||
}
|
||||
throw error;
|
||||
});
|
||||
catch (error) {
|
||||
if (this.#networkPromise === detectNetwork) {
|
||||
this.#networkPromise = null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
this.#networkPromise = detectNetwork;
|
||||
return (await detectNetwork).clone();
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -16,7 +16,7 @@ import { AbiCoder } from "../abi/index.js";
|
||||
import { getAddress, resolveAddress } from "../address/index.js";
|
||||
import { TypedDataEncoder } from "../hash/index.js";
|
||||
import { accessListify } from "../transaction/index.js";
|
||||
import { defineProperties, getBigInt, hexlify, isHexString, toQuantity, toUtf8Bytes, makeError, assert, assertArgument, FetchRequest, resolveProperties } from "../utils/index.js";
|
||||
import { defineProperties, getBigInt, hexlify, isHexString, toQuantity, toUtf8Bytes, isError, makeError, assert, assertArgument, FetchRequest, resolveProperties } from "../utils/index.js";
|
||||
import { AbstractProvider, UnmanagedSubscriber } from "./abstract-provider.js";
|
||||
import { AbstractSigner } from "./abstract-signer.js";
|
||||
import { Network } from "./network.js";
|
||||
@ -133,12 +133,45 @@ export class JsonRpcSigner extends AbstractSigner {
|
||||
// for it; it should show up very quickly
|
||||
return await (new Promise((resolve, reject) => {
|
||||
const timeouts = [1000, 100];
|
||||
let invalids = 0;
|
||||
const checkTx = async () => {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
try {
|
||||
// Try getting the transaction
|
||||
const tx = await this.provider.getTransaction(hash);
|
||||
if (tx != null) {
|
||||
resolve(tx.replaceableTransaction(blockNumber));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
// If we were cancelled: stop polling.
|
||||
// If the data is bad: the node returns bad transactions
|
||||
// If the network changed: calling again will also fail
|
||||
// If unsupported: likely destroyed
|
||||
if (isError(error, "CANCELLED") || isError(error, "BAD_DATA") ||
|
||||
isError(error, "NETWORK_ERROR" || isError(error, "UNSUPPORTED_OPERATION"))) {
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
// Stop-gap for misbehaving backends; see #4513
|
||||
if (isError(error, "INVALID_ARGUMENT")) {
|
||||
invalids++;
|
||||
if (error.info == null) {
|
||||
error.info = {};
|
||||
}
|
||||
error.info.sendTransactionHash = hash;
|
||||
if (invalids > 10) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Notify anyone that cares; but we will try again, since
|
||||
// it is likely an intermittent service error
|
||||
this.provider.emit("error", makeError("failed to fetch transation after sending (will try again)", "UNKNOWN_ERROR", { error }));
|
||||
}
|
||||
// Wait another 4 seconds
|
||||
this.provider._setTimeout(() => { checkTx(); }, timeouts.pop() || 4000);
|
||||
@ -216,7 +249,7 @@ export class JsonRpcApiProvider extends AbstractProvider {
|
||||
if (this.#drainTimer) {
|
||||
return;
|
||||
}
|
||||
// If we aren't using batching, no hard in sending it immeidately
|
||||
// If we aren't using batching, no harm in sending it immediately
|
||||
const stallTime = (this._getOption("batchMaxCount") === 1) ? 0 : this._getOption("batchStallTime");
|
||||
this.#drainTimer = setTimeout(() => {
|
||||
this.#drainTimer = null;
|
||||
@ -379,9 +412,15 @@ export class JsonRpcApiProvider extends AbstractProvider {
|
||||
// If we are ready, use ``send``, which enabled requests to be batched
|
||||
if (this.ready) {
|
||||
this.#pendingDetectNetwork = (async () => {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
try {
|
||||
const result = Network.from(getBigInt(await this.send("eth_chainId", [])));
|
||||
this.#pendingDetectNetwork = null;
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
this.#pendingDetectNetwork = null;
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
return await this.#pendingDetectNetwork;
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -93,7 +93,7 @@
|
||||
"url": "https://www.buymeacoffee.com/ricmoo"
|
||||
}
|
||||
],
|
||||
"gitHead": "f8f11c754aa2c9b541db73d3bde66a8ffa5146f0",
|
||||
"gitHead": "180221574c5d2af9ad85404af4fab8752d3d5029",
|
||||
"homepage": "https://ethers.org",
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
@ -128,8 +128,8 @@
|
||||
"test-browser": "node lib.esm/_admin/test-browser",
|
||||
"test-commonjs": "mocha --reporter ./reporter.cjs ./lib.commonjs/_tests/test-*.js",
|
||||
"test-coverage": "c8 -o output -r lcov -r text mocha --no-color --reporter ./reporter.cjs ./lib.esm/_tests/test-*.js | tee output/summary.txt",
|
||||
"test-esm": "mocha --reporter ./reporter.cjs ./lib.esm/_tests/test-*.js"
|
||||
"test-esm": "mocha --trace-warnings --reporter ./reporter.cjs ./lib.esm/_tests/test-*.js"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "6.9.0"
|
||||
"version": "6.9.1"
|
||||
}
|
||||
|
@ -3,4 +3,4 @@
|
||||
/**
|
||||
* The current version of Ethers.
|
||||
*/
|
||||
export const version: string = "6.9.0";
|
||||
export const version: string = "6.9.1";
|
||||
|
Loading…
Reference in New Issue
Block a user