ethers.js/lib.esm/_tests/test-providers-jsonrpc.js
2024-01-17 14:33:43 -05:00

157 lines
5.7 KiB
JavaScript

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-providers-jsonrpc.js.map