Compare commits

...

5 Commits

8 changed files with 82 additions and 17 deletions

View File

@@ -768,6 +768,14 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return new BaseContract(this.target, this.interface, runner);
}
/**
* Return a new Contract instance with the same ABI and runner, but
* a different %%target%%.
*/
attach(target: string | Addressable): BaseContract {
return new BaseContract(target, this.interface, this.runner);
}
/**
* Return the resolved address of this Contract.
*/
@@ -1022,7 +1030,7 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
/**
* Create a new Class for the %%abi%%.
*/
static buildClass<T = ContractInterface>(abi: InterfaceAbi): new (target: string, runner?: null | ContractRunner) => BaseContract & Omit<T, keyof BaseContract> {
static buildClass<T = ContractInterface>(abi: Interface | InterfaceAbi): new (target: string, runner?: null | ContractRunner) => BaseContract & Omit<T, keyof BaseContract> {
class CustomContract extends BaseContract {
constructor(address: string, runner: null | ContractRunner = null) {
super(address, abi, runner);
@@ -1034,14 +1042,14 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
/**
* Create a new BaseContract with a specified Interface.
*/
static from<T = ContractInterface>(target: string, abi: InterfaceAbi, runner?: null | ContractRunner): BaseContract & Omit<T, keyof BaseContract> {
static from<T = ContractInterface>(target: string, abi: Interface | InterfaceAbi, runner?: null | ContractRunner): BaseContract & Omit<T, keyof BaseContract> {
if (runner == null) { runner = null; }
const contract = new this(target, abi, runner );
return contract as any;
}
}
function _ContractBase(): new (target: string, abi: InterfaceAbi, runner?: null | ContractRunner) => BaseContract & Omit<ContractInterface, keyof BaseContract> {
function _ContractBase(): new (target: string, abi: Interface | InterfaceAbi, runner?: null | ContractRunner) => BaseContract & Omit<ContractInterface, keyof BaseContract> {
return BaseContract as any;
}

View File

@@ -9,6 +9,7 @@ import {
import { BaseContract, copyOverrides, resolveArgs } from "./contract.js";
import type { InterfaceAbi } from "../abi/index.js";
import type { Addressable } from "../address/index.js";
import type { ContractRunner } from "../providers/index.js";
import type { BytesLike } from "../utils/index.js";
@@ -65,6 +66,10 @@ export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract
});
}
attach(target: string | Addressable): BaseContract & Omit<I, keyof BaseContract> {
return new (<any>BaseContract)(target, this.interface, this.runner);
}
/**
* Resolves to the transaction to deploy the contract, passing %%args%%
* into the constructor.

View File

@@ -78,6 +78,7 @@ export {
Network,
EnsPlugin, EtherscanPlugin, FeeDataNetworkPlugin, GasCostPlugin, NetworkPlugin,
MulticoinProviderPlugin,
SocketBlockSubscriber, SocketEventSubscriber, SocketPendingSubscriber,
SocketSubscriber, UnmanagedSubscriber,
@@ -161,6 +162,8 @@ export type { TypedDataDomain, TypedDataField } from "./hash/index.js";
export type {
Provider, Signer,
AbstractProviderOptions,
AbstractProviderPlugin, BlockParams, BlockTag, ContractRunner, DebugEventBrowserProvider,
Eip1193Provider, EventFilter, Filter, FilterByBlockHash, GasCostParameters,
JsonRpcApiProviderOptions, JsonRpcError, JsonRpcPayload, JsonRpcResult,

View File

@@ -398,6 +398,24 @@ type _PerformAccountRequest = {
method: "getStorage", position: bigint
}
/**
* Options for configuring some internal aspects of an [[AbstractProvider]].
*
* **``cacheTimeout``** - how long to cache a low-level ``_perform``
* for, based on input parameters. This reduces the number of calls
* to getChainId and getBlockNumber, but may break test chains which
* can perform operations (internally) synchronously. Use ``-1`` to
* disable, ``0`` will only buffer within the same event loop and
* any other value is in ms. (default: ``250``)
*/
export type AbstractProviderOptions = {
cacheTimeout?: number;
};
const defaultOptions = {
cacheTimeout: 250
};
type CcipArgs = {
sender: string;
urls: Array<string>;
@@ -436,12 +454,15 @@ export class AbstractProvider implements Provider {
#disableCcipRead: boolean;
#options: Required<AbstractProviderOptions>;
/**
* Create a new **AbstractProvider** connected to %%network%%, or
* use the various network detection capabilities to discover the
* [[Network]] if necessary.
*/
constructor(_network?: "any" | Networkish) {
constructor(_network?: "any" | Networkish, options?: AbstractProviderOptions) {
this.#options = Object.assign({ }, defaultOptions, options || { });
if (_network === "any") {
this.#anyNetwork = true;
@@ -512,19 +533,25 @@ export class AbstractProvider implements Provider {
// Shares multiple identical requests made during the same 250ms
async #perform<T = any>(req: PerformActionRequest): Promise<T> {
const timeout = this.#options.cacheTimeout;
// Caching disabled
if (timeout < 0) { return await this._perform(req); }
// Create a tag
const tag = getTag(req.method, req);
let perform = this.#performCache.get(tag);
if (!perform) {
perform = this._perform(req);
this.#performCache.set(tag, perform);
setTimeout(() => {
if (this.#performCache.get(tag) === perform) {
this.#performCache.delete(tag);
}
}, 250);
}, timeout);
}
return await perform;

View File

@@ -5,11 +5,12 @@
* @_section: api/providers/ens-resolver:ENS Resolver [about-ens-rsolver]
*/
import { getAddress } from "../address/index.js";
import { ZeroAddress } from "../constants/index.js";
import { Contract } from "../contract/index.js";
import { dnsEncode, namehash } from "../hash/index.js";
import {
hexlify, toBeHex,
hexlify, isHexString, toBeHex,
defineProperties, encodeBase58,
assert, assertArgument, isError,
FetchRequest
@@ -184,7 +185,7 @@ export class EnsResolver {
"function supportsInterface(bytes4) view returns (bool)",
"function resolve(bytes, bytes) view returns (bytes)",
"function addr(bytes32) view returns (address)",
"function addr(bytes32, uint) view returns (address)",
"function addr(bytes32, uint) view returns (bytes)",
"function text(bytes32, string) view returns (string)",
"function contenthash(bytes32) view returns (bytes)",
], provider);
@@ -276,6 +277,14 @@ export class EnsResolver {
}
}
// Try decoding its EVM canonical chain as an EVM chain address first
if (coinType >= 0 && coinType < 0x80000000) {
let ethCoinType = coinType + 0x80000000;
const data = await this.#fetch("addr(bytes32,uint)", [ ethCoinType ]);
if (isHexString(data, 20)) { return getAddress(data); }
}
let coinPlugin: null | MulticoinProviderPlugin = null;
for (const plugin of this.provider.plugins) {
if (!(plugin instanceof MulticoinProviderPlugin)) { continue; }
@@ -294,7 +303,7 @@ export class EnsResolver {
if (data == null || data === "0x") { return null; }
// Compute the address
const address = await coinPlugin.encodeAddress(coinType, data);
const address = await coinPlugin.decodeAddress(coinType, data);
if (address != null) { return address; }

View File

@@ -26,7 +26,10 @@ export {
export { getDefaultProvider } from "./default-provider.js";
export { EnsResolver } from "./ens-resolver.js";
export {
EnsResolver,
MulticoinProviderPlugin
} from "./ens-resolver.js";
export { Network } from "./network.js";
@@ -74,7 +77,7 @@ export {
} from "./provider-socket.js";
export type {
Subscription, Subscriber,
AbstractProviderOptions, Subscription, Subscriber,
AbstractProviderPlugin,
PerformActionFilter, PerformActionTransaction, PerformActionRequest,
} from "./abstract-provider.js"

View File

@@ -34,7 +34,7 @@ import { PollingEventSubscriber } from "./subscriber-polling.js";
import type { TypedDataDomain, TypedDataField } from "../hash/index.js";
import type { TransactionLike } from "../transaction/index.js";
import type { PerformActionRequest, Subscriber, Subscription } from "./abstract-provider.js";
import type { AbstractProviderOptions, PerformActionRequest, Subscriber, Subscription } from "./abstract-provider.js";
import type { Networkish } from "./network.js";
import type { Provider, TransactionRequest, TransactionResponse } from "./provider.js";
import type { Signer } from "./signer.js";
@@ -185,6 +185,8 @@ export type DebugEventJsonRpcApiProvider = {
*
* **``batchMaxCount``** - maximum number of requests to allow in a batch.
* If ``batchMaxCount = 1``, then batching is disabled. (default: ``100``)
*
* **``cacheTimeout``** - passed as [[AbstractProviderOptions]].
*/
export type JsonRpcApiProviderOptions = {
polling?: boolean;
@@ -192,6 +194,8 @@ export type JsonRpcApiProviderOptions = {
batchStallTime?: number;
batchMaxSize?: number;
batchMaxCount?: number;
cacheTimeout?: number;
};
const defaultOptions = {
@@ -200,7 +204,9 @@ const defaultOptions = {
batchStallTime: 10, // 10ms
batchMaxSize: (1 << 20), // 1Mb
batchMaxCount: 100 // 100 requests
batchMaxCount: 100, // 100 requests
cacheTimeout: 250
}
/**
@@ -537,7 +543,11 @@ export abstract class JsonRpcApiProvider extends AbstractProvider {
}
constructor(network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network);
const superOptions: AbstractProviderOptions = { };
if (options && options.cacheTimeout != null) {
superOptions.cacheTimeout = options.cacheTimeout;
}
super(network, superOptions);
this.#nextId = 1;
this.#options = Object.assign({ }, defaultOptions, options || { });

View File

@@ -11,11 +11,11 @@
import { getBytes } from "./data.js";
import { assert, assertArgument, assertPrivate } from "./errors.js";
import {
getBigInt, fromTwos, mask, toBigInt
getBigInt, getNumber, fromTwos, mask, toBigInt
} from "./maths.js";
import { defineProperties } from "./properties.js";
import type { BigNumberish, BytesLike } from "./index.js";
import type { BigNumberish, BytesLike, Numeric } from "./index.js";
const BN_N1 = BigInt(-1);
const BN_0 = BigInt(0);
@@ -565,8 +565,8 @@ export class FixedNumber {
* for %%decimals%%) cannot fit in %%format%%, either due to overflow
* or underflow (precision loss).
*/
static fromValue(_value: BigNumberish, decimals?: number, _format?: FixedFormat): FixedNumber {
if (decimals == null) { decimals = 0; }
static fromValue(_value: BigNumberish, _decimals?: Numeric, _format?: FixedFormat): FixedNumber {
const decimals = (_decimals == null) ? 0: getNumber(_decimals);
const format = getFormat(_format);
let value = getBigInt(_value, "value");