Skip to content

Commit

Permalink
Merge branch 'wip-6.13'
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo committed Jun 3, 2024
2 parents 2d2572a + c2d5346 commit e97ca3b
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 39 deletions.
2 changes: 1 addition & 1 deletion src.ts/_tests/test-providers-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ describe("Test Networks", function() {
"base", "base-sepolia",
"bnb", "bnbt",
"linea", "linea-sepolia",
"matic", "matic-mumbai", "matic-amoy",
"matic", "matic-amoy",
"optimism", "optimism-sepolia",
"xdai",
];
Expand Down
6 changes: 3 additions & 3 deletions src.ts/_tests/test-providers-wildcard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ describe("Test EIP-2544 ENS wildcards", function() {
});

describe("Test ENS-DNS gasless resolver", function() {
it("Resolved almonit.org", async function() {
it("Resolved firefly.app", async function() {
this.timeout(10000);

const provider = connect("mainnet");
const addr = await provider.resolveName("almonit.org");
assert.equal(addr, "0x0D59d0f7DcC0fBF0A3305cE0261863aAf7Ab685c", "addr");
const addr = await provider.resolveName("firefly.app");
assert.equal(addr, "0x643aA0A61eADCC9Cc202D1915D942d35D005400C", "addr");
});
});
74 changes: 56 additions & 18 deletions src.ts/abi/coders/abstract-coder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
defineProperties, concat, getBytesCopy, getNumber, hexlify,
toBeArray, toBigInt, toNumber,
assert, assertArgument
/*, isError*/
} from "../../utils/index.js";

import type { BigNumberish, BytesLike } from "../../utils/index.js";
Expand All @@ -19,12 +20,44 @@ const passProperties = [ "then" ];

const _guard = { };

const resultNames: WeakMap<Result, ReadonlyArray<null | string>> = new WeakMap();

function getNames(result: Result): ReadonlyArray<null | string> {
return resultNames.get(result)!;
}
function setNames(result: Result, names: ReadonlyArray<null | string>): void {
resultNames.set(result, names);
}

function throwError(name: string, error: Error): never {
const wrapped = new Error(`deferred error during ABI decoding triggered accessing ${ name }`);
(<any>wrapped).error = error;
throw wrapped;
}

function toObject(names: ReadonlyArray<null | string>, items: Result, deep?: boolean): Record<string, any> | Array<any> {
if (names.indexOf(null) >= 0) {
return items.map((item, index) => {
if (item instanceof Result) {
return toObject(getNames(item), item, deep);
}
return item;
});
}

return (<Array<string>>names).reduce((accum, name, index) => {
let item = items.getValue(name);
if (!(name in accum)) {
if (deep && item instanceof Result) {
item = toObject(getNames(item), item, deep);
}
accum[name] = item;
}
return accum;
}, <Record<string, any>>{ });
}


/**
* A [[Result]] is a sub-class of Array, which allows accessing any
* of its values either positionally by its index or, if keys are
Expand All @@ -33,6 +66,9 @@ function throwError(name: string, error: Error): never {
* @_docloc: api/abi
*/
export class Result extends Array<any> {
// No longer used; but cannot be removed as it will remove the
// #private field from the .d.ts which may break backwards
// compatibility
readonly #names: ReadonlyArray<null | string>;

[ K: string | number ]: any
Expand Down Expand Up @@ -73,21 +109,25 @@ export class Result extends Array<any> {
}, <Map<string, number>>(new Map()));

// Remove any key thats not unique
this.#names = Object.freeze(items.map((item, index) => {
setNames(this, Object.freeze(items.map((item, index) => {
const name = names[index];
if (name != null && nameCounts.get(name) === 1) {
return name;
}
return null;
}));
})));

// Dummy operations to prevent TypeScript from complaining
this.#names = [ ];
if (this.#names == null) { void(this.#names); }

if (!wrap) { return; }

// A wrapped Result is immutable
Object.freeze(this);

// Proxy indices and names so we can trap deferred errors
return new Proxy(this, {
const proxy = new Proxy(this, {
get: (target, prop, receiver) => {
if (typeof(prop) === "string") {

Expand Down Expand Up @@ -127,6 +167,7 @@ export class Result extends Array<any> {
return Reflect.get(target, prop, receiver);
}
});
setNames(proxy, getNames(this));
}

/**
Expand Down Expand Up @@ -157,21 +198,14 @@ export class Result extends Array<any> {
* any outstanding deferred errors.
*/
toObject(deep?: boolean): Record<string, any> {
return this.#names.reduce((accum, name, index) => {
assert(name != null, "value at index ${ index } unnamed", "UNSUPPORTED_OPERATION", {
const names = getNames(this);
return names.reduce((accum, name, index) => {

assert(name != null, `value at index ${ index } unnamed`, "UNSUPPORTED_OPERATION", {
operation: "toObject()"
});

// Add values for names that don't conflict
if (!(name in accum)) {
let child = this.getValue(name);
if (deep && child instanceof Result) {
child = child.toObject(deep);
}
accum[name] = child;
}

return accum;
return toObject(names, this, deep);
}, <Record<string, any>>{});
}

Expand All @@ -192,10 +226,12 @@ export class Result extends Array<any> {
}
if (end > this.length) { end = this.length; }

const _names = getNames(this);

const result: Array<any> = [ ], names: Array<null | string> = [ ];
for (let i = start; i < end; i++) {
result.push(this[i]);
names.push(this.#names[i]);
names.push(_names[i]);
}

return new Result(_guard, result, names);
Expand All @@ -205,6 +241,8 @@ export class Result extends Array<any> {
* @_ignore
*/
filter(callback: (el: any, index: number, array: Result) => boolean, thisArg?: any): Result {
const _names = getNames(this);

const result: Array<any> = [ ], names: Array<null | string> = [ ];
for (let i = 0; i < this.length; i++) {
const item = this[i];
Expand All @@ -214,7 +252,7 @@ export class Result extends Array<any> {

if (callback.call(thisArg, item, i, this)) {
result.push(item);
names.push(this.#names[i]);
names.push(_names[i]);
}
}

Expand Down Expand Up @@ -248,7 +286,7 @@ export class Result extends Array<any> {
* accessible by name.
*/
getValue(name: string): any {
const index = this.#names.indexOf(name);
const index = getNames(this).indexOf(name);
if (index === -1) { return undefined; }

const value = this[index];
Expand Down
2 changes: 1 addition & 1 deletion src.ts/ethers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ export type { TypedDataDomain, TypedDataField } from "./hash/index.js";
export type {
Provider, Signer,

AbstractProviderOptions, FallbackProviderOptions,
AbstractProviderOptions, BrowserProviderOptions, FallbackProviderOptions,

AbstractProviderPlugin, BlockParams, BlockTag, ContractRunner, DebugEventBrowserProvider,
Eip1193Provider, EventFilter, Filter, FilterByBlockHash, GasCostParameters,
Expand Down
2 changes: 1 addition & 1 deletion src.ts/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export type {
} from "./provider.js";

export type {
DebugEventBrowserProvider, Eip1193Provider
BrowserProviderOptions, DebugEventBrowserProvider, Eip1193Provider
} from "./provider-browser.js";

export type { FallbackProviderOptions } from "./provider-fallback.js";
Expand Down
19 changes: 16 additions & 3 deletions src.ts/providers/provider-browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { assertArgument } from "../utils/index.js";
import { JsonRpcApiPollingProvider } from "./provider-jsonrpc.js";

import type {
JsonRpcApiProviderOptions,
JsonRpcError, JsonRpcPayload, JsonRpcResult,
JsonRpcSigner
} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
import type { Network, Networkish } from "./network.js";

/**
* The interface to an [[link-eip-1193]] provider, which is a standard
Expand Down Expand Up @@ -35,6 +36,13 @@ export type DebugEventBrowserProvider = {
error: Error
};

export type BrowserProviderOptions = {
polling?: boolean;
staticNetwork?: null | boolean | Network;

cacheTimeout?: number;
pollingInterval?: number;
};

/**
* A **BrowserProvider** is intended to wrap an injected provider which
Expand All @@ -48,10 +56,15 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
* Connnect to the %%ethereum%% provider, optionally forcing the
* %%network%%.
*/
constructor(ethereum: Eip1193Provider, network?: Networkish) {
constructor(ethereum: Eip1193Provider, network?: Networkish, _options?: BrowserProviderOptions) {
// Copy the options
const options: JsonRpcApiProviderOptions = Object.assign({ },
((_options != null) ? _options: { }),
{ batchMaxCount: 1 });

assertArgument(ethereum && ethereum.request, "invalid EIP-1193 provider", "ethereum", ethereum);

super(network, { batchMaxCount: 1 });
super(network, options);

this.#request = async (method: string, params: Array<any> | Record<string, any>) => {
const payload = { method, params };
Expand Down
36 changes: 27 additions & 9 deletions src.ts/utils/geturl-browser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { assert } from "./errors.js";
import { assert, makeError } from "./errors.js";

import type {
FetchGetUrlFunc, FetchRequest, FetchCancelSignal, GetUrlResponse
Expand Down Expand Up @@ -27,11 +27,11 @@ declare global {
function fetch(url: string, init: FetchInit): Promise<Response>;
}

// @TODO: timeout is completely ignored; start a Promise.any with a reject?

export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {

async function getUrl(req: FetchRequest, _signal?: FetchCancelSignal): Promise<GetUrlResponse> {
assert(_signal == null || !_signal.cancelled, "request cancelled before sending", "CANCELLED");

const protocol = req.url.split(":")[0].toLowerCase();

assert(protocol === "http" || protocol === "https", `unsupported protocol ${ protocol }`, "UNSUPPORTED_OPERATION", {
Expand All @@ -43,21 +43,39 @@ export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {
operation: "request"
});

let signal: undefined | AbortSignal = undefined;
let error: null | Error = null;

const controller = new AbortController();

const timer = setTimeout(() => {
error = makeError("request timeout", "TIMEOUT");
controller.abort();
}, req.timeout);

if (_signal) {
const controller = new AbortController();
signal = controller.signal;
_signal.addListener(() => { controller.abort(); });
_signal.addListener(() => {
error = makeError("request cancelled", "CANCELLED");
controller.abort();
});
}

const init = {
method: req.method,
headers: new Headers(Array.from(req)),
body: req.body || undefined,
signal
signal: controller.signal
};

const resp = await fetch(req.url, init);
let resp: Awaited<ReturnType<typeof fetch>>;
try {
resp = await fetch(req.url, init);
} catch (_error) {
clearTimeout(timer);
if (error) { throw error; }
throw _error;
}

clearTimeout(timer);

const headers: Record<string, string> = { };
resp.headers.forEach((value, key) => {
Expand Down
24 changes: 21 additions & 3 deletions src.ts/utils/geturl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import http from "http";
import https from "https";
import { gunzipSync } from "zlib";

import { assert } from "./errors.js";
import { assert, makeError } from "./errors.js";
import { getBytes } from "./data.js";

import type {
Expand All @@ -15,6 +15,8 @@ import type {
export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {

async function getUrl(req: FetchRequest, signal?: FetchCancelSignal): Promise<GetUrlResponse> {
// Make sure we weren't cancelled before sending
assert(signal == null || !signal.cancelled, "request cancelled before sending", "CANCELLED");

const protocol = req.url.split(":")[0].toLowerCase();

Expand All @@ -35,6 +37,13 @@ export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {
if (options.agent) { reqOptions.agent = options.agent; }
}

// Create a Node-specific AbortController, if available
let abort: null | AbortController = null;
try {
abort = new AbortController();
reqOptions.abort = abort.signal;
} catch (e) { console.log(e); }

const request = ((protocol === "http") ? http: https).request(req.url, reqOptions);

request.setTimeout(req.timeout);
Expand All @@ -45,8 +54,17 @@ export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {
request.end();

return new Promise((resolve, reject) => {
// @TODO: Node 15 added AbortSignal; once we drop support for
// Node14, we can add that in here too

if (signal) {
signal.addListener(() => {
if (abort) { abort.abort(); }
reject(makeError("request cancelled", "CANCELLED"));
});
}

request.on("timeout", () => {
reject(makeError("request timeout", "TIMEOUT"));
});

request.once("response", (resp: http.IncomingMessage) => {
const statusCode = resp.statusCode || 0;
Expand Down

0 comments on commit e97ca3b

Please sign in to comment.