docs: added jsdocs and general documentation

This commit is contained in:
Richard Moore 2023-06-01 17:42:48 -04:00
parent c785c1e515
commit bbcfb5f6b8
45 changed files with 2923 additions and 208 deletions

@ -1,26 +1,97 @@
_section: ABI _section: Application Binary Interfaces @<docs-abi>
Explain about ABI here, what it does and does not do When interacting with any application, whether it is on Ethereum,
over the internet or within a compiled application on a computer
all information is stored and sent as binary data which is just a
sequence of bytes.
- encodes arbitrary (but defined) data structures to raw bytes So every application must agree on how to encode and decode their
information as a sequence of bytes.
_subsection: Call An **Application Binary Interface** (ABI) provides a way to describe
the encoding and decoding process, in a generic way so that a variety
of types and structures of types can be defined.
About encoding For example, a string is often encoded as a UTF-8 sequence of bytes,
which uses specific bits within sub-sequences to indicate emoji and
other special characters. Every implementation of UTF-8 must understand
and operate under the same rules so that strings work universally. In
this way, UTF-8 standard is itself an ABI.
_heading: selector or signature hash (sighash) When interacting with Ethereum, a contract received a sequence of bytes
as input (provided by sending a transaction or through a call) and
returns a result as a sequence of bytes. So, each Contract has its own
ABI that helps specify how to encode the input and how to decode the output.
_heading: Results It is up to the contract developer to make this ABI available. Many
Contracts implement a standard (such as ERC-20), in which case the
ABI for that standard can be used. Many developers choose to verify their
source code on Etherscan, in which case Etherscan computes the ABI and
provides it through their website (which can be fetched using the ``getContract``
method). Otherwise, beyond reverse engineering the Contract there is
not a meaningful way to extract the contract ABI.
_subsection: Events _subsection: Call Data Representation
When calling a Contract on Ethereum, the input data must be encoded
according to the ABI.
The first 4 bytes of the data are the **method selector**, which is
the keccak256 hash of the normalized method signature.
Then the method parameters are encoded and concatenated to the selector.
All encoded data is made up of components padded to 32 bytes, so the length
of input data will always be congruent to ``4 mod 32``.
The result of a successful call will be encoded values, whose components
are padded to 32 bytes each as well, so the length of a result will always
be congruent to ``0 mod 32``, on success.
The result of a reverted call will contain the **error selector** as the
first 4 bytes, which is the keccak256 of the normalized error signature,
followed by the encoded values, whose components are padded to 32 bytes
each, so the length of a revert will be congruent to ``4 mod 32``.
The one exception to all this is that ``revert(false)`` will return a
result or ``0x``.
_subsection: Event Data Representation
When an Event is emitted from a contract, there are two places data is
logged in a Log: the **topics** and the **data**.
An additonal fee is paid for each **topic**, but this affords a topic
to be indexed in a bloom filter within the block, which allows efficient
filtering.
The **topic hash** is always the first topic in a Log, which is the
keccak256 of the normalized event signature. This allows a specific
event to be efficiently filtered, finding the matching events in a block.
Each additional **indexed** parameter (i.e. parameters marked with
``indexed`` in the signautre) are placed in the topics as well, but may be
filtered to find matching values.
All non-indexed parameters are encoded and placed in the **data**. This
is cheaper and more compact, but does not allow filtering on these values.
For example, the event ``Transfer(address indexed from, address indexed to, uint value)``
would require 3 topics, which are the topic hash, the ``from`` address
and the ``to`` address and the data would contain 32 bytes, which is
the padded big-endian representation of ``value``. This allows for
efficient filtering by the event (i.e. ``Transfer``) as well as the ``from``
address and ``to`` address.
_subsection: Deployment _subsection: Deployment
About initcode When deploying a transaction, the data provided is treated as **initcode**,
which executes the data as normal EVM bytecode, which returns a sequence
of bytes, but instead of that sequence of bytes being treated as data that
result is instead the bytecode to install as the bytecode of the contract.
Another paragraph [[link-bip-39]]. The bytecode produced by Solidity is designed to have all constructor
parameters encoded normally and concatenated to the bytecode and provided
_heading: Foo? as the ``data`` to a transaction with no ``to`` address.
Bar?

@ -1,7 +1,8 @@
_section: Ethereum Basics @priority<99> _section: Ethereum Basics @<docs-basics> @priority<99>
Prelude here... This section aims to cover some of the basics for those interested
in a deeper understanding of the inner-workings of Ethereum.
_subsection: About stuff? _subsection: Topics
Some info - [Application Binary Interface](docs-abi)

@ -1,6 +1,7 @@
link-js-array [link-js-array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) link-js-array [link-js-array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
link-js-bigint [link-js-bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt) link-js-bigint [link-js-bigint](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)
link-js-date [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) link-js-date [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)
link-js-fetch [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
link-js-normalize [String.normalize](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize) link-js-normalize [String.normalize](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/normalize)
link-js-maxsafe [link-js-maxsafe](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#Description) link-js-maxsafe [link-js-maxsafe](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER#Description)
link-js-proxy [link-js-proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) link-js-proxy [link-js-proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)

@ -12,6 +12,7 @@ link-infura [INFURA](https://infura.io)
link-javascriptcore [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore?language=objc) link-javascriptcore [JavaScriptCore](https://developer.apple.com/documentation/javascriptcore?language=objc)
link-ledger [Ledger](https://www.ledger.com) link-ledger [Ledger](https://www.ledger.com)
link-metamask [MetaMask](https://metamask.io/) link-metamask [MetaMask](https://metamask.io/)
link-node [Node.js](https://nodejs.org/)
link-otto [Otto](https://github.com/robertkrimen/otto) link-otto [Otto](https://github.com/robertkrimen/otto)
link-parity [Parity](https://www.parity.io) link-parity [Parity](https://www.parity.io)
link-pocket [Pocket Network](https://pokt.network) link-pocket [Pocket Network](https://pokt.network)

@ -43,6 +43,7 @@ link-namehash [namehash](https://docs.ens.domains/contract-api-reference/name-pr
link-rlp [Recursive-Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/) link-rlp [Recursive-Length Prefix](https://ethereum.org/en/developers/docs/data-structures-and-encoding/rlp/)
link-pbkdf2 [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2) link-pbkdf2 [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2)
link-solc-abi [ABI Specification](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#formal-specification-of-the-encoding) link-solc-abi [ABI Specification](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#formal-specification-of-the-encoding)
link-solc-jsonabi [ABI JSON Specification](https://docs.soliditylang.org/en/v0.8.19/abi-spec.html#json)
link-solc-errors [Solidity Custom Errors](https://docs.soliditylang.org/en/v0.8.4/abi-spec.html#errors) link-solc-errors [Solidity Custom Errors](https://docs.soliditylang.org/en/v0.8.4/abi-spec.html#errors)
link-solc-events [Solidity Events](https://docs.soliditylang.org/en/v0.8.4/abi-spec.html#events) link-solc-events [Solidity Events](https://docs.soliditylang.org/en/v0.8.4/abi-spec.html#events)
link-solc-output [solc standard output](https://solidity.readthedocs.io/en/v0.6.0/using-the-compiler.html#output-description) link-solc-output [solc standard output](https://solidity.readthedocs.io/en/v0.6.0/using-the-compiler.html#output-description)

@ -121,8 +121,9 @@ function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null |
} }
/** /**
* About AbiCoder * The **AbiCoder** is a low-level class responsible for encoding JavaScript
*/ * values into binary data and decoding binary data into JavaScript values.
*/
export class AbiCoder { export class AbiCoder {
#getCoder(param: ParamType): Coder { #getCoder(param: ParamType): Coder {

@ -1,5 +1,11 @@
/** /**
* About frgaments... * A fragment is a single item from an ABI, which may represent any of:
*
* - [Functions](FunctionFragment)
* - [Events](EventFragment)
* - [Constructors](ConstructorFragment)
* - Custom [Errors](ErrorFragment)
* - [Fallback or Receive](FallbackFragment) functions
* *
* @_subsection api/abi/abi-coder:Fragments [about-fragments] * @_subsection api/abi/abi-coder:Fragments [about-fragments]
*/ */
@ -11,7 +17,7 @@ import {
import { id } from "../hash/index.js"; import { id } from "../hash/index.js";
/** /**
* A type description in a JSON API. * A Type description in a [JSON ABI format](link-solc-jsonabi).
*/ */
export interface JsonFragmentType { export interface JsonFragmentType {
/** /**
@ -41,7 +47,7 @@ export interface JsonFragmentType {
} }
/** /**
* A fragment for a method, event or error in a JSON API. * A fragment for a method, event or error in a [JSON ABI format](link-solc-jsonabi).
*/ */
export interface JsonFragment { export interface JsonFragment {
/** /**
@ -92,19 +98,21 @@ export interface JsonFragment {
/** /**
* The format to serialize the output as. * The format to serialize the output as.
*
* **``"sighash"``** - the bare formatting, used to compute the selector
* or topic hash; this format cannot be reversed (as it discards ``indexed``)
* so cannot by used to export an [[Interface]].
*
* **``"minimal"``** - Human-Readable ABI with minimal spacing and without
* names, so it is compact, but will result in Result objects that cannot
* be accessed by name.
*
* **``"full"``** - Full Human-Readable ABI, with readable spacing and names
* intact; this is generally the recommended format.
*
* **``"json"``** - The [JSON ABI format](link-solc-jsonabi).
*/ */
export type FormatType = export type FormatType = "sighash" | "minimal" | "full" | "json";
// Bare formatting, as is needed for computing a sighash of an event or function
"sighash" |
// Human-Readable with Minimal spacing and without names (compact human-readable)
"minimal" |
// Human-Readable with nice spacing, including all names
"full" |
// JSON-format a la Solidity
"json";
// [ "a", "b" ] => { "a": 1, "b": 1 } // [ "a", "b" ] => { "a": 1, "b": 1 }
function setify(items: Array<string>): ReadonlySet<string> { function setify(items: Array<string>): ReadonlySet<string> {
@ -513,7 +521,7 @@ const FunctionFragmentInternal = "_FunctionInternal";
const StructFragmentInternal = "_StructInternal"; const StructFragmentInternal = "_StructInternal";
/** /**
* Each input and output of a [[Fragment]] is an Array of **PAramType**. * Each input and output of a [[Fragment]] is an Array of **ParamType**.
*/ */
export class ParamType { export class ParamType {
@ -642,16 +650,6 @@ export class ParamType {
return result; return result;
} }
/*
* Returns true if %%value%% is an Array type.
*
* This provides a type gaurd ensuring that the
* [[arrayChildren]] and [[arrayLength]] are non-null.
*/
//static isArray(value: any): value is { arrayChildren: ParamType, arrayLength: number } {
// return value && (value.baseType === "array")
//}
/** /**
* Returns true if %%this%% is an Array type. * Returns true if %%this%% is an Array type.
* *
@ -913,7 +911,7 @@ export abstract class Fragment {
} }
/** /**
* Returns a string representation of this fragment. * Returns a string representation of this fragment as %%format%%.
*/ */
abstract format(format?: FormatType): string; abstract format(format?: FormatType): string;
@ -1050,6 +1048,9 @@ export class ErrorFragment extends NamedFragment {
return id(this.format("sighash")).substring(0, 10); return id(this.format("sighash")).substring(0, 10);
} }
/**
* Returns a string representation of this fragment as %%format%%.
*/
format(format?: FormatType): string { format(format?: FormatType): string {
if (format == null) { format = "sighash"; } if (format == null) { format = "sighash"; }
if (format === "json") { if (format === "json") {
@ -1066,6 +1067,9 @@ export class ErrorFragment extends NamedFragment {
return result.join(" "); return result.join(" ");
} }
/**
* Returns a new **ErrorFragment** for %%obj%%.
*/
static from(obj: any): ErrorFragment { static from(obj: any): ErrorFragment {
if (ErrorFragment.isFragment(obj)) { return obj; } if (ErrorFragment.isFragment(obj)) { return obj; }
@ -1084,6 +1088,10 @@ export class ErrorFragment extends NamedFragment {
obj.inputs ? obj.inputs.map(ParamType.from): [ ]); obj.inputs ? obj.inputs.map(ParamType.from): [ ]);
} }
/**
* Returns ``true`` and provides a type guard if %%value%% is an
* **ErrorFragment**.
*/
static isFragment(value: any): value is ErrorFragment { static isFragment(value: any): value is ErrorFragment {
return (value && value[internal] === ErrorFragmentInternal); return (value && value[internal] === ErrorFragmentInternal);
} }
@ -1093,6 +1101,9 @@ export class ErrorFragment extends NamedFragment {
* A Fragment which represents an Event. * A Fragment which represents an Event.
*/ */
export class EventFragment extends NamedFragment { export class EventFragment extends NamedFragment {
/**
* Whether this event is anonymous.
*/
readonly anonymous!: boolean; readonly anonymous!: boolean;
/** /**
@ -1111,6 +1122,9 @@ export class EventFragment extends NamedFragment {
return id(this.format("sighash")); return id(this.format("sighash"));
} }
/**
* Returns a string representation of this event as %%format%%.
*/
format(format?: FormatType): string { format(format?: FormatType): string {
if (format == null) { format = "sighash"; } if (format == null) { format = "sighash"; }
if (format === "json") { if (format === "json") {
@ -1129,12 +1143,18 @@ export class EventFragment extends NamedFragment {
return result.join(" "); return result.join(" ");
} }
/**
* Return the topic hash for an event with %%name%% and %%params%%.
*/
static getTopicHash(name: string, params?: Array<any>): string { static getTopicHash(name: string, params?: Array<any>): string {
params = (params || []).map((p) => ParamType.from(p)); params = (params || []).map((p) => ParamType.from(p));
const fragment = new EventFragment(_guard, name, params, false); const fragment = new EventFragment(_guard, name, params, false);
return fragment.topicHash; return fragment.topicHash;
} }
/**
* Returns a new **EventFragment** for %%obj%%.
*/
static from(obj: any): EventFragment { static from(obj: any): EventFragment {
if (EventFragment.isFragment(obj)) { return obj; } if (EventFragment.isFragment(obj)) { return obj; }
@ -1154,6 +1174,10 @@ export class EventFragment extends NamedFragment {
obj.inputs ? obj.inputs.map((p: any) => ParamType.from(p, true)): [ ], !!obj.anonymous); obj.inputs ? obj.inputs.map((p: any) => ParamType.from(p, true)): [ ], !!obj.anonymous);
} }
/**
* Returns ``true`` and provides a type guard if %%value%% is an
* **EventFragment**.
*/
static isFragment(value: any): value is EventFragment { static isFragment(value: any): value is EventFragment {
return (value && value[internal] === EventFragmentInternal); return (value && value[internal] === EventFragmentInternal);
} }
@ -1163,7 +1187,15 @@ export class EventFragment extends NamedFragment {
* A Fragment which represents a constructor. * A Fragment which represents a constructor.
*/ */
export class ConstructorFragment extends Fragment { export class ConstructorFragment extends Fragment {
/**
* Whether the constructor can receive an endowment.
*/
readonly payable!: boolean; readonly payable!: boolean;
/**
* The recommended gas limit for deployment or ``null``.
*/
readonly gas!: null | bigint; readonly gas!: null | bigint;
/** /**
@ -1175,6 +1207,9 @@ export class ConstructorFragment extends Fragment {
defineProperties<ConstructorFragment>(this, { payable, gas }); defineProperties<ConstructorFragment>(this, { payable, gas });
} }
/**
* Returns a string representation of this constructor as %%format%%.
*/
format(format?: FormatType): string { format(format?: FormatType): string {
assert(format != null && format !== "sighash", "cannot format a constructor for sighash", assert(format != null && format !== "sighash", "cannot format a constructor for sighash",
"UNSUPPORTED_OPERATION", { operation: "format(sighash)" }); "UNSUPPORTED_OPERATION", { operation: "format(sighash)" });
@ -1195,6 +1230,9 @@ export class ConstructorFragment extends Fragment {
return result.join(" "); return result.join(" ");
} }
/**
* Returns a new **ConstructorFragment** for %%obj%%.
*/
static from(obj: any): ConstructorFragment { static from(obj: any): ConstructorFragment {
if (ConstructorFragment.isFragment(obj)) { return obj; } if (ConstructorFragment.isFragment(obj)) { return obj; }
@ -1216,6 +1254,10 @@ export class ConstructorFragment extends Fragment {
!!obj.payable, (obj.gas != null) ? obj.gas: null); !!obj.payable, (obj.gas != null) ? obj.gas: null);
} }
/**
* Returns ``true`` and provides a type guard if %%value%% is a
* **ConstructorFragment**.
*/
static isFragment(value: any): value is ConstructorFragment { static isFragment(value: any): value is ConstructorFragment {
return (value && value[internal] === ConstructorFragmentInternal); return (value && value[internal] === ConstructorFragmentInternal);
} }
@ -1237,6 +1279,9 @@ export class FallbackFragment extends Fragment {
defineProperties<FallbackFragment>(this, { payable }); defineProperties<FallbackFragment>(this, { payable });
} }
/**
* Returns a string representation of this fallback as %%format%%.
*/
format(format?: FormatType): string { format(format?: FormatType): string {
const type = ((this.inputs.length === 0) ? "receive": "fallback"); const type = ((this.inputs.length === 0) ? "receive": "fallback");
@ -1248,6 +1293,9 @@ export class FallbackFragment extends Fragment {
return `${ type }()${ this.payable ? " payable": "" }`; return `${ type }()${ this.payable ? " payable": "" }`;
} }
/**
* Returns a new **FallbackFragment** for %%obj%%.
*/
static from(obj: any): FallbackFragment { static from(obj: any): FallbackFragment {
if (FallbackFragment.isFragment(obj)) { return obj; } if (FallbackFragment.isFragment(obj)) { return obj; }
@ -1310,6 +1358,10 @@ export class FallbackFragment extends Fragment {
assertArgument(false, "invalid fallback description", "obj", obj); assertArgument(false, "invalid fallback description", "obj", obj);
} }
/**
* Returns ``true`` and provides a type guard if %%value%% is a
* **FallbackFragment**.
*/
static isFragment(value: any): value is FallbackFragment { static isFragment(value: any): value is FallbackFragment {
return (value && value[internal] === FallbackFragmentInternal); return (value && value[internal] === FallbackFragmentInternal);
} }
@ -1342,7 +1394,7 @@ export class FunctionFragment extends NamedFragment {
readonly payable!: boolean; readonly payable!: boolean;
/** /**
* The amount of gas to send when calling this function * The recommended gas limit to send when calling this function.
*/ */
readonly gas!: null | bigint; readonly gas!: null | bigint;
@ -1365,6 +1417,9 @@ export class FunctionFragment extends NamedFragment {
return id(this.format("sighash")).substring(0, 10); return id(this.format("sighash")).substring(0, 10);
} }
/**
* Returns a string representation of this function as %%format%%.
*/
format(format?: FormatType): string { format(format?: FormatType): string {
if (format == null) { format = "sighash"; } if (format == null) { format = "sighash"; }
if (format === "json") { if (format === "json") {
@ -1401,12 +1456,18 @@ export class FunctionFragment extends NamedFragment {
return result.join(" "); return result.join(" ");
} }
/**
* Return the selector for a function with %%name%% and %%params%%.
*/
static getSelector(name: string, params?: Array<any>): string { static getSelector(name: string, params?: Array<any>): string {
params = (params || []).map((p) => ParamType.from(p)); params = (params || []).map((p) => ParamType.from(p));
const fragment = new FunctionFragment(_guard, name, "view", params, [ ], null); const fragment = new FunctionFragment(_guard, name, "view", params, [ ], null);
return fragment.selector; return fragment.selector;
} }
/**
* Returns a new **FunctionFragment** for %%obj%%.
*/
static from(obj: any): FunctionFragment { static from(obj: any): FunctionFragment {
if (FunctionFragment.isFragment(obj)) { return obj; } if (FunctionFragment.isFragment(obj)) { return obj; }
@ -1458,6 +1519,10 @@ export class FunctionFragment extends NamedFragment {
(obj.gas != null) ? obj.gas: null); (obj.gas != null) ? obj.gas: null);
} }
/**
* Returns ``true`` and provides a type guard if %%value%% is a
* **FunctionFragment**.
*/
static isFragment(value: any): value is FunctionFragment { static isFragment(value: any): value is FunctionFragment {
return (value && value[internal] === FunctionFragmentInternal); return (value && value[internal] === FunctionFragmentInternal);
} }
@ -1476,10 +1541,16 @@ export class StructFragment extends NamedFragment {
Object.defineProperty(this, internal, { value: StructFragmentInternal }); Object.defineProperty(this, internal, { value: StructFragmentInternal });
} }
/**
* Returns a string representation of this struct as %%format%%.
*/
format(): string { format(): string {
throw new Error("@TODO"); throw new Error("@TODO");
} }
/**
* Returns a new **StructFragment** for %%obj%%.
*/
static from(obj: any): StructFragment { static from(obj: any): StructFragment {
if (typeof(obj) === "string") { if (typeof(obj) === "string") {
return StructFragment.from(lex(obj)); return StructFragment.from(lex(obj));
@ -1494,6 +1565,11 @@ export class StructFragment extends NamedFragment {
return new StructFragment(_guard, obj.name, obj.inputs ? obj.inputs.map(ParamType.from): [ ]); return new StructFragment(_guard, obj.name, obj.inputs ? obj.inputs.map(ParamType.from): [ ]);
} }
// @TODO: fix this return type
/**
* Returns ``true`` and provides a type guard if %%value%% is a
* **StructFragment**.
*/
static isFragment(value: any): value is FunctionFragment { static isFragment(value: any): value is FunctionFragment {
return (value && value[internal] === StructFragmentInternal); return (value && value[internal] === StructFragmentInternal);
} }

@ -1,5 +1,9 @@
/** /**
* Explain about ABI here... * The Application Binary Interface (ABI) describes how method input
* parameters should be encoded, their results decoded, and how to
* decode events and errors.
*
* See [About ABIs](docs-abi) for more details how they are used.
* *
* @_section api/abi:Application Binary Interface [about-abi] * @_section api/abi:Application Binary Interface [about-abi]
* @_navTitle: ABI * @_navTitle: ABI

@ -1,5 +1,11 @@
/** /**
* About Interface * The Interface class is a low-level class that accepts an
* ABI and provides all the necessary functionality to encode
* and decode paramaters to and results from methods, events
* and errors.
*
* It also provides several convenience methods to automatically
* search and find matching transactions and events to parse them.
* *
* @_subsection api/abi:Interfaces [interfaces] * @_subsection api/abi:Interfaces [interfaces]
*/ */
@ -27,13 +33,39 @@ import type { JsonFragment } from "./fragments.js";
export { checkResultErrors, Result }; export { checkResultErrors, Result };
/**
* When using the [[Interface-parseLog]] to automatically match a Log to its event
* for parsing, a **LogDescription** is returned.
*/
export class LogDescription { export class LogDescription {
/**
* The matching fragment for the ``topic0``.
*/
readonly fragment!: EventFragment; readonly fragment!: EventFragment;
/**
* The name of the Event.
*/
readonly name!: string; readonly name!: string;
/**
* The full Event signature.
*/
readonly signature!: string; readonly signature!: string;
/**
* The topic hash for the Event.
*/
readonly topic!: string; readonly topic!: string;
/**
* The arguments passed into the Event with ``emit``.
*/
readonly args!: Result readonly args!: Result
/**
* @_ignore:
*/
constructor(fragment: EventFragment, topic: string, args: Result) { constructor(fragment: EventFragment, topic: string, args: Result) {
const name = fragment.name, signature = fragment.format(); const name = fragment.name, signature = fragment.format();
defineProperties<LogDescription>(this, { defineProperties<LogDescription>(this, {
@ -42,14 +74,45 @@ export class LogDescription {
} }
} }
/**
* When using the [[Interface-parseTransaction]] to automatically match
* a transaction data to its function for parsing,
* a **TransactionDescription** is returned.
*/
export class TransactionDescription { export class TransactionDescription {
/**
* The matching fragment from the transaction ``data``.
*/
readonly fragment!: FunctionFragment; readonly fragment!: FunctionFragment;
/**
* The name of the Function from the transaction ``data``.
*/
readonly name!: string; readonly name!: string;
/**
* The arguments passed to the Function from the transaction ``data``.
*/
readonly args!: Result; readonly args!: Result;
/**
* The full Function signature from the transaction ``data``.
*/
readonly signature!: string; readonly signature!: string;
/**
* The selector for the Function from the transaction ``data``.
*/
readonly selector!: string; readonly selector!: string;
/**
* The ``value`` (in wei) from the transaction.
*/
readonly value!: bigint; readonly value!: bigint;
/**
* @_ignore:
*/
constructor(fragment: FunctionFragment, selector: string, args: Result, value: bigint) { constructor(fragment: FunctionFragment, selector: string, args: Result, value: bigint) {
const name = fragment.name, signature = fragment.format(); const name = fragment.name, signature = fragment.format();
defineProperties<TransactionDescription>(this, { defineProperties<TransactionDescription>(this, {
@ -58,13 +121,39 @@ export class TransactionDescription {
} }
} }
/**
* When using the [[Interface-parseError]] to automatically match an
* error for a call result for parsing, an **ErrorDescription** is returned.
*/
export class ErrorDescription { export class ErrorDescription {
/**
* The matching fragment.
*/
readonly fragment!: ErrorFragment; readonly fragment!: ErrorFragment;
/**
* The name of the Error.
*/
readonly name!: string; readonly name!: string;
/**
* The arguments passed to the Error with ``revert``.
*/
readonly args!: Result; readonly args!: Result;
/**
* The full Error signature.
*/
readonly signature!: string; readonly signature!: string;
/**
* The selector for the Error.
*/
readonly selector!: string; readonly selector!: string;
/**
* @_ignore:
*/
constructor(fragment: ErrorFragment, selector: string, args: Result) { constructor(fragment: ErrorFragment, selector: string, args: Result) {
const name = fragment.name, signature = fragment.format(); const name = fragment.name, signature = fragment.format();
defineProperties<ErrorDescription>(this, { defineProperties<ErrorDescription>(this, {
@ -73,14 +162,35 @@ export class ErrorDescription {
} }
} }
/**
* An **Indexed** is used as a value when a value that does not
* fit within a topic (i.e. not a fixed-length, 32-byte type). It
* is the ``keccak256`` of the value, and used for types such as
* arrays, tuples, bytes and strings.
*/
export class Indexed { export class Indexed {
/**
* The ``keccak256`` of the value logged.
*/
readonly hash!: null | string; readonly hash!: null | string;
/**
* @_ignore:
*/
readonly _isIndexed!: boolean; readonly _isIndexed!: boolean;
/**
* Returns ``true`` if %%value%% is an **Indexed**.
*
* This provides a Type Guard for property access.
*/
static isIndexed(value: any): value is Indexed { static isIndexed(value: any): value is Indexed {
return !!(value && value._isIndexed); return !!(value && value._isIndexed);
} }
/**
* @_ignore:
*/
constructor(hash: null | string) { constructor(hash: null | string) {
defineProperties<Indexed>(this, { hash, _isIndexed: true }) defineProperties<Indexed>(this, { hash, _isIndexed: true })
} }
@ -152,7 +262,23 @@ function checkNames(fragment: Fragment, type: "input" | "output", params: Array<
*/ */
/** /**
* @TODO * An **InterfaceAbi** may be any supported ABI format.
*
* A string is expected to be a JSON string, which will be parsed
* using ``JSON.parse``. This means that the value **must** be a valid
* JSON string, with no stray commas, etc.
*
* An array may contain any combination of:
* - Human-Readable fragments
* - Parsed JSON fragment
* - [[Fragment]] instances
*
* A **Human-Readable Fragment** is a string which resembles a Solidity
* signature and is introduced in [this blog entry](link-ricmoo-humanreadableabi).
* For example, ``function balanceOf(address) view returns (uint)``.
*
* A **Parsed JSON Fragment** is a JavaScript Object desribed in the
* [Solidity documentation](link-solc-jsonabi).
*/ */
export type InterfaceAbi = string | ReadonlyArray<Fragment | JsonFragment | string>; export type InterfaceAbi = string | ReadonlyArray<Fragment | JsonFragment | string>;

@ -38,6 +38,10 @@ function b(value: BytesLike, size?: number): Typed {
return new Typed(_gaurd, `bytes${ (size) ? size: "" }`, value, { size }); return new Typed(_gaurd, `bytes${ (size) ? size: "" }`, value, { size });
} }
// @TODO: Remove this in v7, it was replaced by TypedBigInt
/**
* @_ignore:
*/
export interface TypedNumber extends Typed { export interface TypedNumber extends Typed {
value: number; value: number;
defaultValue(): number; defaultValue(): number;
@ -45,33 +49,88 @@ export interface TypedNumber extends Typed {
maxValue(): number; maxValue(): number;
} }
/**
* A **Typed** that represents a numeric value.
*/
export interface TypedBigInt extends Typed { export interface TypedBigInt extends Typed {
/**
* The value.
*/
value: bigint; value: bigint;
/**
* The default value for all numeric types is ``0``.
*/
defaultValue(): bigint; defaultValue(): bigint;
/**
* The minimum value for this type, accounting for bit-width and signed-ness.
*/
minValue(): bigint; minValue(): bigint;
/**
* The minimum value for this type, accounting for bit-width.
*/
maxValue(): bigint; maxValue(): bigint;
} }
/**
* A **Typed** that represents a binary sequence of data as bytes.
*/
export interface TypedData extends Typed { export interface TypedData extends Typed {
/**
* The value.
*/
value: string; value: string;
/**
* The default value for this type.
*/
defaultValue(): string; defaultValue(): string;
} }
/**
* A **Typed** that represents a UTF-8 sequence of bytes.
*/
export interface TypedString extends Typed { export interface TypedString extends Typed {
/**
* The value.
*/
value: string; value: string;
/**
* The default value for the string type is the empty string (i.e. ``""``).
*/
defaultValue(): string; defaultValue(): string;
} }
const _typedSymbol = Symbol.for("_ethers_typed"); const _typedSymbol = Symbol.for("_ethers_typed");
/**
* The **Typed** class to wrap values providing explicit type information.
*/
export class Typed { export class Typed {
/**
* The type, as a Solidity-compatible type.
*/
readonly type!: string; readonly type!: string;
/**
* The actual value.
*/
readonly value!: any; readonly value!: any;
readonly #options: any; readonly #options: any;
/**
* @_ignore:
*/
readonly _typedSymbol!: Symbol; readonly _typedSymbol!: Symbol;
/**
* @_ignore:
*/
constructor(gaurd: any, type: string, value: any, options?: any) { constructor(gaurd: any, type: string, value: any, options?: any) {
if (options == null) { options = null; } if (options == null) { options = null; }
assertPrivate(_gaurd, gaurd, "Typed"); assertPrivate(_gaurd, gaurd, "Typed");
@ -82,6 +141,9 @@ export class Typed {
this.format(); this.format();
} }
/**
* Format the type as a Human-Readable type.
*/
format(): string { format(): string {
if (this.type === "array") { if (this.type === "array") {
throw new Error(""); throw new Error("");
@ -94,30 +156,51 @@ export class Typed {
return this.type; return this.type;
} }
/**
* The default value returned by this type.
*/
defaultValue(): string | number | bigint | Result { defaultValue(): string | number | bigint | Result {
return 0; return 0;
} }
/**
* The minimum value for numeric types.
*/
minValue(): string | number | bigint { minValue(): string | number | bigint {
return 0; return 0;
} }
/**
* The maximum value for numeric types.
*/
maxValue(): string | number | bigint { maxValue(): string | number | bigint {
return 0; return 0;
} }
/**
* Returns ``true`` and provides a type guard is this is a [[TypedBigInt]].
*/
isBigInt(): this is TypedBigInt { isBigInt(): this is TypedBigInt {
return !!(this.type.match(/^u?int[0-9]+$/)); return !!(this.type.match(/^u?int[0-9]+$/));
} }
/**
* Returns ``true`` and provides a type guard is this is a [[TypedData]].
*/
isData(): this is TypedData { isData(): this is TypedData {
return this.type.startsWith("bytes"); return this.type.startsWith("bytes");
} }
/**
* Returns ``true`` and provides a type guard is this is a [[TypedString]].
*/
isString(): this is TypedString { isString(): this is TypedString {
return (this.type === "string"); return (this.type === "string");
} }
/**
* Returns the tuple name, if this is a tuple. Throws otherwise.
*/
get tupleName(): null | string { get tupleName(): null | string {
if (this.type !== "tuple") { throw TypeError("not a tuple"); } if (this.type !== "tuple") { throw TypeError("not a tuple"); }
return this.#options; return this.#options;
@ -127,6 +210,12 @@ export class Typed {
// - `null` indicates the length is unforced, it could be dynamic // - `null` indicates the length is unforced, it could be dynamic
// - `-1` indicates the length is dynamic // - `-1` indicates the length is dynamic
// - any other value indicates it is a static array and is its length // - any other value indicates it is a static array and is its length
/**
* Returns the length of the array type or ``-1`` if it is dynamic.
*
* Throws if the type is not an array.
*/
get arrayLength(): null | number { get arrayLength(): null | number {
if (this.type !== "array") { throw TypeError("not an array"); } if (this.type !== "array") { throw TypeError("not an array"); }
if (this.#options === true) { return -1; } if (this.#options === true) { return -1; }
@ -134,126 +223,546 @@ export class Typed {
return null; return null;
} }
/**
* Returns a new **Typed** of %%type%% with the %%value%%.
*/
static from(type: string, value: any): Typed { static from(type: string, value: any): Typed {
return new Typed(_gaurd, type, value); return new Typed(_gaurd, type, value);
} }
/**
* Return a new ``uint8`` type for %%v%%.
*/
static uint8(v: BigNumberish): Typed { return n(v, 8); } static uint8(v: BigNumberish): Typed { return n(v, 8); }
/**
* Return a new ``uint16`` type for %%v%%.
*/
static uint16(v: BigNumberish): Typed { return n(v, 16); } static uint16(v: BigNumberish): Typed { return n(v, 16); }
/**
* Return a new ``uint24`` type for %%v%%.
*/
static uint24(v: BigNumberish): Typed { return n(v, 24); } static uint24(v: BigNumberish): Typed { return n(v, 24); }
/**
* Return a new ``uint32`` type for %%v%%.
*/
static uint32(v: BigNumberish): Typed { return n(v, 32); } static uint32(v: BigNumberish): Typed { return n(v, 32); }
/**
* Return a new ``uint40`` type for %%v%%.
*/
static uint40(v: BigNumberish): Typed { return n(v, 40); } static uint40(v: BigNumberish): Typed { return n(v, 40); }
/**
* Return a new ``uint48`` type for %%v%%.
*/
static uint48(v: BigNumberish): Typed { return n(v, 48); } static uint48(v: BigNumberish): Typed { return n(v, 48); }
/**
* Return a new ``uint56`` type for %%v%%.
*/
static uint56(v: BigNumberish): Typed { return n(v, 56); } static uint56(v: BigNumberish): Typed { return n(v, 56); }
/**
* Return a new ``uint64`` type for %%v%%.
*/
static uint64(v: BigNumberish): Typed { return n(v, 64); } static uint64(v: BigNumberish): Typed { return n(v, 64); }
/**
* Return a new ``uint72`` type for %%v%%.
*/
static uint72(v: BigNumberish): Typed { return n(v, 72); } static uint72(v: BigNumberish): Typed { return n(v, 72); }
/**
* Return a new ``uint80`` type for %%v%%.
*/
static uint80(v: BigNumberish): Typed { return n(v, 80); } static uint80(v: BigNumberish): Typed { return n(v, 80); }
/**
* Return a new ``uint88`` type for %%v%%.
*/
static uint88(v: BigNumberish): Typed { return n(v, 88); } static uint88(v: BigNumberish): Typed { return n(v, 88); }
/**
* Return a new ``uint96`` type for %%v%%.
*/
static uint96(v: BigNumberish): Typed { return n(v, 96); } static uint96(v: BigNumberish): Typed { return n(v, 96); }
/**
* Return a new ``uint104`` type for %%v%%.
*/
static uint104(v: BigNumberish): Typed { return n(v, 104); } static uint104(v: BigNumberish): Typed { return n(v, 104); }
/**
* Return a new ``uint112`` type for %%v%%.
*/
static uint112(v: BigNumberish): Typed { return n(v, 112); } static uint112(v: BigNumberish): Typed { return n(v, 112); }
/**
* Return a new ``uint120`` type for %%v%%.
*/
static uint120(v: BigNumberish): Typed { return n(v, 120); } static uint120(v: BigNumberish): Typed { return n(v, 120); }
/**
* Return a new ``uint128`` type for %%v%%.
*/
static uint128(v: BigNumberish): Typed { return n(v, 128); } static uint128(v: BigNumberish): Typed { return n(v, 128); }
/**
* Return a new ``uint136`` type for %%v%%.
*/
static uint136(v: BigNumberish): Typed { return n(v, 136); } static uint136(v: BigNumberish): Typed { return n(v, 136); }
/**
* Return a new ``uint144`` type for %%v%%.
*/
static uint144(v: BigNumberish): Typed { return n(v, 144); } static uint144(v: BigNumberish): Typed { return n(v, 144); }
/**
* Return a new ``uint152`` type for %%v%%.
*/
static uint152(v: BigNumberish): Typed { return n(v, 152); } static uint152(v: BigNumberish): Typed { return n(v, 152); }
/**
* Return a new ``uint160`` type for %%v%%.
*/
static uint160(v: BigNumberish): Typed { return n(v, 160); } static uint160(v: BigNumberish): Typed { return n(v, 160); }
/**
* Return a new ``uint168`` type for %%v%%.
*/
static uint168(v: BigNumberish): Typed { return n(v, 168); } static uint168(v: BigNumberish): Typed { return n(v, 168); }
/**
* Return a new ``uint176`` type for %%v%%.
*/
static uint176(v: BigNumberish): Typed { return n(v, 176); } static uint176(v: BigNumberish): Typed { return n(v, 176); }
/**
* Return a new ``uint184`` type for %%v%%.
*/
static uint184(v: BigNumberish): Typed { return n(v, 184); } static uint184(v: BigNumberish): Typed { return n(v, 184); }
/**
* Return a new ``uint192`` type for %%v%%.
*/
static uint192(v: BigNumberish): Typed { return n(v, 192); } static uint192(v: BigNumberish): Typed { return n(v, 192); }
/**
* Return a new ``uint200`` type for %%v%%.
*/
static uint200(v: BigNumberish): Typed { return n(v, 200); } static uint200(v: BigNumberish): Typed { return n(v, 200); }
/**
* Return a new ``uint208`` type for %%v%%.
*/
static uint208(v: BigNumberish): Typed { return n(v, 208); } static uint208(v: BigNumberish): Typed { return n(v, 208); }
/**
* Return a new ``uint216`` type for %%v%%.
*/
static uint216(v: BigNumberish): Typed { return n(v, 216); } static uint216(v: BigNumberish): Typed { return n(v, 216); }
/**
* Return a new ``uint224`` type for %%v%%.
*/
static uint224(v: BigNumberish): Typed { return n(v, 224); } static uint224(v: BigNumberish): Typed { return n(v, 224); }
/**
* Return a new ``uint232`` type for %%v%%.
*/
static uint232(v: BigNumberish): Typed { return n(v, 232); } static uint232(v: BigNumberish): Typed { return n(v, 232); }
/**
* Return a new ``uint240`` type for %%v%%.
*/
static uint240(v: BigNumberish): Typed { return n(v, 240); } static uint240(v: BigNumberish): Typed { return n(v, 240); }
/**
* Return a new ``uint248`` type for %%v%%.
*/
static uint248(v: BigNumberish): Typed { return n(v, 248); } static uint248(v: BigNumberish): Typed { return n(v, 248); }
/**
* Return a new ``uint256`` type for %%v%%.
*/
static uint256(v: BigNumberish): Typed { return n(v, 256); } static uint256(v: BigNumberish): Typed { return n(v, 256); }
/**
* Return a new ``uint256`` type for %%v%%.
*/
static uint(v: BigNumberish): Typed { return n(v, 256); } static uint(v: BigNumberish): Typed { return n(v, 256); }
/**
* Return a new ``int8`` type for %%v%%.
*/
static int8(v: BigNumberish): Typed { return n(v, -8); } static int8(v: BigNumberish): Typed { return n(v, -8); }
/**
* Return a new ``int16`` type for %%v%%.
*/
static int16(v: BigNumberish): Typed { return n(v, -16); } static int16(v: BigNumberish): Typed { return n(v, -16); }
/**
* Return a new ``int24`` type for %%v%%.
*/
static int24(v: BigNumberish): Typed { return n(v, -24); } static int24(v: BigNumberish): Typed { return n(v, -24); }
/**
* Return a new ``int32`` type for %%v%%.
*/
static int32(v: BigNumberish): Typed { return n(v, -32); } static int32(v: BigNumberish): Typed { return n(v, -32); }
/**
* Return a new ``int40`` type for %%v%%.
*/
static int40(v: BigNumberish): Typed { return n(v, -40); } static int40(v: BigNumberish): Typed { return n(v, -40); }
/**
* Return a new ``int48`` type for %%v%%.
*/
static int48(v: BigNumberish): Typed { return n(v, -48); } static int48(v: BigNumberish): Typed { return n(v, -48); }
/**
* Return a new ``int56`` type for %%v%%.
*/
static int56(v: BigNumberish): Typed { return n(v, -56); } static int56(v: BigNumberish): Typed { return n(v, -56); }
/**
* Return a new ``int64`` type for %%v%%.
*/
static int64(v: BigNumberish): Typed { return n(v, -64); } static int64(v: BigNumberish): Typed { return n(v, -64); }
/**
* Return a new ``int72`` type for %%v%%.
*/
static int72(v: BigNumberish): Typed { return n(v, -72); } static int72(v: BigNumberish): Typed { return n(v, -72); }
/**
* Return a new ``int80`` type for %%v%%.
*/
static int80(v: BigNumberish): Typed { return n(v, -80); } static int80(v: BigNumberish): Typed { return n(v, -80); }
/**
* Return a new ``int88`` type for %%v%%.
*/
static int88(v: BigNumberish): Typed { return n(v, -88); } static int88(v: BigNumberish): Typed { return n(v, -88); }
/**
* Return a new ``int96`` type for %%v%%.
*/
static int96(v: BigNumberish): Typed { return n(v, -96); } static int96(v: BigNumberish): Typed { return n(v, -96); }
/**
* Return a new ``int104`` type for %%v%%.
*/
static int104(v: BigNumberish): Typed { return n(v, -104); } static int104(v: BigNumberish): Typed { return n(v, -104); }
/**
* Return a new ``int112`` type for %%v%%.
*/
static int112(v: BigNumberish): Typed { return n(v, -112); } static int112(v: BigNumberish): Typed { return n(v, -112); }
/**
* Return a new ``int120`` type for %%v%%.
*/
static int120(v: BigNumberish): Typed { return n(v, -120); } static int120(v: BigNumberish): Typed { return n(v, -120); }
/**
* Return a new ``int128`` type for %%v%%.
*/
static int128(v: BigNumberish): Typed { return n(v, -128); } static int128(v: BigNumberish): Typed { return n(v, -128); }
/**
* Return a new ``int136`` type for %%v%%.
*/
static int136(v: BigNumberish): Typed { return n(v, -136); } static int136(v: BigNumberish): Typed { return n(v, -136); }
/**
* Return a new ``int144`` type for %%v%%.
*/
static int144(v: BigNumberish): Typed { return n(v, -144); } static int144(v: BigNumberish): Typed { return n(v, -144); }
/**
* Return a new ``int52`` type for %%v%%.
*/
static int152(v: BigNumberish): Typed { return n(v, -152); } static int152(v: BigNumberish): Typed { return n(v, -152); }
/**
* Return a new ``int160`` type for %%v%%.
*/
static int160(v: BigNumberish): Typed { return n(v, -160); } static int160(v: BigNumberish): Typed { return n(v, -160); }
/**
* Return a new ``int168`` type for %%v%%.
*/
static int168(v: BigNumberish): Typed { return n(v, -168); } static int168(v: BigNumberish): Typed { return n(v, -168); }
/**
* Return a new ``int176`` type for %%v%%.
*/
static int176(v: BigNumberish): Typed { return n(v, -176); } static int176(v: BigNumberish): Typed { return n(v, -176); }
/**
* Return a new ``int184`` type for %%v%%.
*/
static int184(v: BigNumberish): Typed { return n(v, -184); } static int184(v: BigNumberish): Typed { return n(v, -184); }
/**
* Return a new ``int92`` type for %%v%%.
*/
static int192(v: BigNumberish): Typed { return n(v, -192); } static int192(v: BigNumberish): Typed { return n(v, -192); }
/**
* Return a new ``int200`` type for %%v%%.
*/
static int200(v: BigNumberish): Typed { return n(v, -200); } static int200(v: BigNumberish): Typed { return n(v, -200); }
/**
* Return a new ``int208`` type for %%v%%.
*/
static int208(v: BigNumberish): Typed { return n(v, -208); } static int208(v: BigNumberish): Typed { return n(v, -208); }
/**
* Return a new ``int216`` type for %%v%%.
*/
static int216(v: BigNumberish): Typed { return n(v, -216); } static int216(v: BigNumberish): Typed { return n(v, -216); }
/**
* Return a new ``int224`` type for %%v%%.
*/
static int224(v: BigNumberish): Typed { return n(v, -224); } static int224(v: BigNumberish): Typed { return n(v, -224); }
/**
* Return a new ``int232`` type for %%v%%.
*/
static int232(v: BigNumberish): Typed { return n(v, -232); } static int232(v: BigNumberish): Typed { return n(v, -232); }
/**
* Return a new ``int240`` type for %%v%%.
*/
static int240(v: BigNumberish): Typed { return n(v, -240); } static int240(v: BigNumberish): Typed { return n(v, -240); }
/**
* Return a new ``int248`` type for %%v%%.
*/
static int248(v: BigNumberish): Typed { return n(v, -248); } static int248(v: BigNumberish): Typed { return n(v, -248); }
/**
* Return a new ``int256`` type for %%v%%.
*/
static int256(v: BigNumberish): Typed { return n(v, -256); } static int256(v: BigNumberish): Typed { return n(v, -256); }
/**
* Return a new ``int256`` type for %%v%%.
*/
static int(v: BigNumberish): Typed { return n(v, -256); } static int(v: BigNumberish): Typed { return n(v, -256); }
/**
* Return a new ``bytes1`` type for %%v%%.
*/
static bytes1(v: BytesLike): Typed { return b(v, 1); } static bytes1(v: BytesLike): Typed { return b(v, 1); }
/**
* Return a new ``bytes2`` type for %%v%%.
*/
static bytes2(v: BytesLike): Typed { return b(v, 2); } static bytes2(v: BytesLike): Typed { return b(v, 2); }
/**
* Return a new ``bytes3`` type for %%v%%.
*/
static bytes3(v: BytesLike): Typed { return b(v, 3); } static bytes3(v: BytesLike): Typed { return b(v, 3); }
/**
* Return a new ``bytes4`` type for %%v%%.
*/
static bytes4(v: BytesLike): Typed { return b(v, 4); } static bytes4(v: BytesLike): Typed { return b(v, 4); }
/**
* Return a new ``bytes5`` type for %%v%%.
*/
static bytes5(v: BytesLike): Typed { return b(v, 5); } static bytes5(v: BytesLike): Typed { return b(v, 5); }
/**
* Return a new ``bytes6`` type for %%v%%.
*/
static bytes6(v: BytesLike): Typed { return b(v, 6); } static bytes6(v: BytesLike): Typed { return b(v, 6); }
/**
* Return a new ``bytes7`` type for %%v%%.
*/
static bytes7(v: BytesLike): Typed { return b(v, 7); } static bytes7(v: BytesLike): Typed { return b(v, 7); }
/**
* Return a new ``bytes8`` type for %%v%%.
*/
static bytes8(v: BytesLike): Typed { return b(v, 8); } static bytes8(v: BytesLike): Typed { return b(v, 8); }
/**
* Return a new ``bytes9`` type for %%v%%.
*/
static bytes9(v: BytesLike): Typed { return b(v, 9); } static bytes9(v: BytesLike): Typed { return b(v, 9); }
/**
* Return a new ``bytes10`` type for %%v%%.
*/
static bytes10(v: BytesLike): Typed { return b(v, 10); } static bytes10(v: BytesLike): Typed { return b(v, 10); }
/**
* Return a new ``bytes11`` type for %%v%%.
*/
static bytes11(v: BytesLike): Typed { return b(v, 11); } static bytes11(v: BytesLike): Typed { return b(v, 11); }
/**
* Return a new ``bytes12`` type for %%v%%.
*/
static bytes12(v: BytesLike): Typed { return b(v, 12); } static bytes12(v: BytesLike): Typed { return b(v, 12); }
/**
* Return a new ``bytes13`` type for %%v%%.
*/
static bytes13(v: BytesLike): Typed { return b(v, 13); } static bytes13(v: BytesLike): Typed { return b(v, 13); }
/**
* Return a new ``bytes14`` type for %%v%%.
*/
static bytes14(v: BytesLike): Typed { return b(v, 14); } static bytes14(v: BytesLike): Typed { return b(v, 14); }
/**
* Return a new ``bytes15`` type for %%v%%.
*/
static bytes15(v: BytesLike): Typed { return b(v, 15); } static bytes15(v: BytesLike): Typed { return b(v, 15); }
/**
* Return a new ``bytes16`` type for %%v%%.
*/
static bytes16(v: BytesLike): Typed { return b(v, 16); } static bytes16(v: BytesLike): Typed { return b(v, 16); }
/**
* Return a new ``bytes17`` type for %%v%%.
*/
static bytes17(v: BytesLike): Typed { return b(v, 17); } static bytes17(v: BytesLike): Typed { return b(v, 17); }
/**
* Return a new ``bytes18`` type for %%v%%.
*/
static bytes18(v: BytesLike): Typed { return b(v, 18); } static bytes18(v: BytesLike): Typed { return b(v, 18); }
/**
* Return a new ``bytes19`` type for %%v%%.
*/
static bytes19(v: BytesLike): Typed { return b(v, 19); } static bytes19(v: BytesLike): Typed { return b(v, 19); }
/**
* Return a new ``bytes20`` type for %%v%%.
*/
static bytes20(v: BytesLike): Typed { return b(v, 20); } static bytes20(v: BytesLike): Typed { return b(v, 20); }
/**
* Return a new ``bytes21`` type for %%v%%.
*/
static bytes21(v: BytesLike): Typed { return b(v, 21); } static bytes21(v: BytesLike): Typed { return b(v, 21); }
/**
* Return a new ``bytes22`` type for %%v%%.
*/
static bytes22(v: BytesLike): Typed { return b(v, 22); } static bytes22(v: BytesLike): Typed { return b(v, 22); }
/**
* Return a new ``bytes23`` type for %%v%%.
*/
static bytes23(v: BytesLike): Typed { return b(v, 23); } static bytes23(v: BytesLike): Typed { return b(v, 23); }
/**
* Return a new ``bytes24`` type for %%v%%.
*/
static bytes24(v: BytesLike): Typed { return b(v, 24); } static bytes24(v: BytesLike): Typed { return b(v, 24); }
/**
* Return a new ``bytes25`` type for %%v%%.
*/
static bytes25(v: BytesLike): Typed { return b(v, 25); } static bytes25(v: BytesLike): Typed { return b(v, 25); }
/**
* Return a new ``bytes26`` type for %%v%%.
*/
static bytes26(v: BytesLike): Typed { return b(v, 26); } static bytes26(v: BytesLike): Typed { return b(v, 26); }
/**
* Return a new ``bytes27`` type for %%v%%.
*/
static bytes27(v: BytesLike): Typed { return b(v, 27); } static bytes27(v: BytesLike): Typed { return b(v, 27); }
/**
* Return a new ``bytes28`` type for %%v%%.
*/
static bytes28(v: BytesLike): Typed { return b(v, 28); } static bytes28(v: BytesLike): Typed { return b(v, 28); }
/**
* Return a new ``bytes29`` type for %%v%%.
*/
static bytes29(v: BytesLike): Typed { return b(v, 29); } static bytes29(v: BytesLike): Typed { return b(v, 29); }
/**
* Return a new ``bytes30`` type for %%v%%.
*/
static bytes30(v: BytesLike): Typed { return b(v, 30); } static bytes30(v: BytesLike): Typed { return b(v, 30); }
/**
* Return a new ``bytes31`` type for %%v%%.
*/
static bytes31(v: BytesLike): Typed { return b(v, 31); } static bytes31(v: BytesLike): Typed { return b(v, 31); }
/**
* Return a new ``bytes32`` type for %%v%%.
*/
static bytes32(v: BytesLike): Typed { return b(v, 32); } static bytes32(v: BytesLike): Typed { return b(v, 32); }
/**
* Return a new ``address`` type for %%v%%.
*/
static address(v: string | Addressable): Typed { return new Typed(_gaurd, "address", v); } static address(v: string | Addressable): Typed { return new Typed(_gaurd, "address", v); }
/**
* Return a new ``bool`` type for %%v%%.
*/
static bool(v: any): Typed { return new Typed(_gaurd, "bool", !!v); } static bool(v: any): Typed { return new Typed(_gaurd, "bool", !!v); }
/**
* Return a new ``bytes`` type for %%v%%.
*/
static bytes(v: BytesLike): Typed { return new Typed(_gaurd, "bytes", v); } static bytes(v: BytesLike): Typed { return new Typed(_gaurd, "bytes", v); }
/**
* Return a new ``string`` type for %%v%%.
*/
static string(v: string): Typed { return new Typed(_gaurd, "string", v); } static string(v: string): Typed { return new Typed(_gaurd, "string", v); }
/**
* Return a new ``array`` type for %%v%%, allowing %%dynamic%% length.
*/
static array(v: Array<any | Typed>, dynamic?: null | boolean): Typed { static array(v: Array<any | Typed>, dynamic?: null | boolean): Typed {
throw new Error("not implemented yet"); throw new Error("not implemented yet");
return new Typed(_gaurd, "array", v, dynamic); return new Typed(_gaurd, "array", v, dynamic);
} }
/**
* Return a new ``tuple`` type for %%v%%, with the optional %%name%%.
*/
static tuple(v: Array<any | Typed> | Record<string, any | Typed>, name?: string): Typed { static tuple(v: Array<any | Typed> | Record<string, any | Typed>, name?: string): Typed {
throw new Error("not implemented yet"); throw new Error("not implemented yet");
return new Typed(_gaurd, "tuple", v, name); return new Typed(_gaurd, "tuple", v, name);
} }
/**
* Return a new ``uint8`` type for %%v%%.
*/
static overrides(v: Record<string, any>): Typed { static overrides(v: Record<string, any>): Typed {
return new Typed(_gaurd, "overrides", Object.assign({ }, v)); return new Typed(_gaurd, "overrides", Object.assign({ }, v));
} }

@ -608,16 +608,49 @@ async function emit(contract: BaseContract, event: ContractEventName, args: Arra
const passProperties = [ "then" ]; const passProperties = [ "then" ];
export class BaseContract implements Addressable, EventEmitterable<ContractEventName> { export class BaseContract implements Addressable, EventEmitterable<ContractEventName> {
/**
* The target to connect to.
*
* This can be an address, ENS name or any [[Addressable]], such as
* another contract. To get the resovled address, use the ``getAddress``
* method.
*/
readonly target!: string | Addressable; readonly target!: string | Addressable;
/**
* The contract Interface.
*/
readonly interface!: Interface; readonly interface!: Interface;
/**
* The connected runner. This is generally a [[Provider]] or a
* [[Signer]], which dictates what operations are supported.
*
* For example, a **Contract** connected to a [[Provider]] may
* only execute read-only operations.
*/
readonly runner!: null | ContractRunner; readonly runner!: null | ContractRunner;
/**
* All the Events available on this contract.
*/
readonly filters!: Record<string, ContractEvent>; readonly filters!: Record<string, ContractEvent>;
/**
* @_ignore:
*/
readonly [internal]: any; readonly [internal]: any;
/**
* The fallback or receive function if any.
*/
readonly fallback!: null | WrappedFallback; readonly fallback!: null | WrappedFallback;
/**
* Creates a new contract connected to %%target%% with the %%abi%% and
* optionally connected to a %%runner%% to perform operations on behalf
* of.
*/
constructor(target: string | Addressable, abi: Interface | InterfaceAbi, runner?: null | ContractRunner, _deployTx?: null | TransactionResponse) { constructor(target: string | Addressable, abi: Interface | InterfaceAbi, runner?: null | ContractRunner, _deployTx?: null | TransactionResponse) {
assertArgument(typeof(target) === "string" || isAddressable(target), assertArgument(typeof(target) === "string" || isAddressable(target),
"invalid value for Contract target", "target", target); "invalid value for Contract target", "target", target);
@ -727,12 +760,22 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
} }
/**
* Return a new Contract instance with the same target and ABI, but
* a different %%runner%%.
*/
connect(runner: null | ContractRunner): BaseContract { connect(runner: null | ContractRunner): BaseContract {
return new BaseContract(this.target, this.interface, runner); return new BaseContract(this.target, this.interface, runner);
} }
/**
* Return the resolved address of this Contract.
*/
async getAddress(): Promise<string> { return await getInternal(this).addrPromise; } async getAddress(): Promise<string> { return await getInternal(this).addrPromise; }
/**
* Return the dedployed bytecode or null if no bytecode is found.
*/
async getDeployedCode(): Promise<null | string> { async getDeployedCode(): Promise<null | string> {
const provider = getProvider(this.runner); const provider = getProvider(this.runner);
assert(provider, "runner does not support .provider", assert(provider, "runner does not support .provider",
@ -743,6 +786,10 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return code; return code;
} }
/**
* Resolve to this Contract once the bytecode has been deployed, or
* resolve immediately if already deployed.
*/
async waitForDeployment(): Promise<this> { async waitForDeployment(): Promise<this> {
// We have the deployement transaction; just use that (throws if deployement fails) // We have the deployement transaction; just use that (throws if deployement fails)
const deployTx = this.deploymentTransaction(); const deployTx = this.deploymentTransaction();
@ -774,26 +821,50 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
}); });
} }
/**
* Return the transaction used to deploy this contract.
*
* This is only available if this instance was returned from a
* [[ContractFactory]].
*/
deploymentTransaction(): null | ContractTransactionResponse { deploymentTransaction(): null | ContractTransactionResponse {
return getInternal(this).deployTx; return getInternal(this).deployTx;
} }
/**
* Return the function for a given name. This is useful when a contract
* method name conflicts with a JavaScript name such as ``prototype`` or
* when using a Contract programatically.
*/
getFunction<T extends ContractMethod = ContractMethod>(key: string | FunctionFragment): T { getFunction<T extends ContractMethod = ContractMethod>(key: string | FunctionFragment): T {
if (typeof(key) !== "string") { key = key.format(); } if (typeof(key) !== "string") { key = key.format(); }
const func = buildWrappedMethod(this, key); const func = buildWrappedMethod(this, key);
return <T>func; return <T>func;
} }
/**
* Return the event for a given name. This is useful when a contract
* event name conflicts with a JavaScript name such as ``prototype`` or
* when using a Contract programatically.
*/
getEvent(key: string | EventFragment): ContractEvent { getEvent(key: string | EventFragment): ContractEvent {
if (typeof(key) !== "string") { key = key.format(); } if (typeof(key) !== "string") { key = key.format(); }
return buildWrappedEvent(this, key); return buildWrappedEvent(this, key);
} }
/**
* @_ignore:
*/
async queryTransaction(hash: string): Promise<Array<EventLog>> { async queryTransaction(hash: string): Promise<Array<EventLog>> {
// Is this useful? // Is this useful?
throw new Error("@TODO"); throw new Error("@TODO");
} }
/**
* Provide historic access to event data for %%event%% in the range
* %%fromBlock%% (default: ``0``) to %%toBlock%% (default: ``"latest"``)
* inclusive.
*/
async queryFilter(event: ContractEventName, fromBlock?: BlockTag, toBlock?: BlockTag): Promise<Array<EventLog | Log>> { async queryFilter(event: ContractEventName, fromBlock?: BlockTag, toBlock?: BlockTag): Promise<Array<EventLog | Log>> {
if (fromBlock == null) { fromBlock = 0; } if (fromBlock == null) { fromBlock = 0; }
if (toBlock == null) { toBlock = "latest"; } if (toBlock == null) { toBlock = "latest"; }
@ -822,6 +893,9 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
}); });
} }
/**
* Add an event %%listener%% for the %%event%%.
*/
async on(event: ContractEventName, listener: Listener): Promise<this> { async on(event: ContractEventName, listener: Listener): Promise<this> {
const sub = await getSub(this, "on", event); const sub = await getSub(this, "on", event);
sub.listeners.push({ listener, once: false }); sub.listeners.push({ listener, once: false });
@ -829,6 +903,10 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return this; return this;
} }
/**
* Add an event %%listener%% for the %%event%%, but remove the listener
* after it is fired once.
*/
async once(event: ContractEventName, listener: Listener): Promise<this> { async once(event: ContractEventName, listener: Listener): Promise<this> {
const sub = await getSub(this, "once", event); const sub = await getSub(this, "once", event);
sub.listeners.push({ listener, once: true }); sub.listeners.push({ listener, once: true });
@ -836,10 +914,19 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return this; return this;
} }
/**
* Emit an %%event%% calling all listeners with %%args%%.
*
* Resolves to ``true`` if any listeners were called.
*/
async emit(event: ContractEventName, ...args: Array<any>): Promise<boolean> { async emit(event: ContractEventName, ...args: Array<any>): Promise<boolean> {
return await emit(this, event, args, null); return await emit(this, event, args, null);
} }
/**
* Resolves to the number of listeners of %%event%% or the total number
* of listeners if unspecified.
*/
async listenerCount(event?: ContractEventName): Promise<number> { async listenerCount(event?: ContractEventName): Promise<number> {
if (event) { if (event) {
const sub = await hasSub(this, event); const sub = await hasSub(this, event);
@ -856,6 +943,10 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return total; return total;
} }
/**
* Resolves to the listeners subscribed to %%event%% or all listeners
* if unspecified.
*/
async listeners(event?: ContractEventName): Promise<Array<Listener>> { async listeners(event?: ContractEventName): Promise<Array<Listener>> {
if (event) { if (event) {
const sub = await hasSub(this, event); const sub = await hasSub(this, event);
@ -872,6 +963,10 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return result; return result;
} }
/**
* Remove the %%listener%% from the listeners for %%event%% or remove
* all listeners if unspecified.
*/
async off(event: ContractEventName, listener?: Listener): Promise<this> { async off(event: ContractEventName, listener?: Listener): Promise<this> {
const sub = await hasSub(this, event); const sub = await hasSub(this, event);
if (!sub) { return this; } if (!sub) { return this; }
@ -889,6 +984,10 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return this; return this;
} }
/**
* Remove all the listeners for %%event%% or remove all listeners if
* unspecified.
*/
async removeAllListeners(event?: ContractEventName): Promise<this> { async removeAllListeners(event?: ContractEventName): Promise<this> {
if (event) { if (event) {
const sub = await hasSub(this, event); const sub = await hasSub(this, event);
@ -906,16 +1005,23 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return this; return this;
} }
// Alias for "on" /**
* Alias for [on].
*/
async addListener(event: ContractEventName, listener: Listener): Promise<this> { async addListener(event: ContractEventName, listener: Listener): Promise<this> {
return await this.on(event, listener); return await this.on(event, listener);
} }
// Alias for "off" /**
* Alias for [off].
*/
async removeListener(event: ContractEventName, listener: Listener): Promise<this> { async removeListener(event: ContractEventName, listener: Listener): Promise<this> {
return await this.off(event, listener); return await this.off(event, listener);
} }
/**
* 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: InterfaceAbi): new (target: string, runner?: null | ContractRunner) => BaseContract & Omit<T, keyof BaseContract> {
class CustomContract extends BaseContract { class CustomContract extends BaseContract {
constructor(address: string, runner: null | ContractRunner = null) { constructor(address: string, runner: null | ContractRunner = null) {
@ -925,6 +1031,9 @@ export class BaseContract implements Addressable, EventEmitterable<ContractEvent
return CustomContract as any; return CustomContract as any;
}; };
/**
* 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: InterfaceAbi, runner?: null | ContractRunner): BaseContract & Omit<T, keyof BaseContract> {
if (runner == null) { runner = null; } if (runner == null) { runner = null; }
const contract = new this(target, abi, runner ); const contract = new this(target, abi, runner );
@ -936,4 +1045,7 @@ function _ContractBase(): new (target: string, abi: InterfaceAbi, runner?: null
return BaseContract as any; return BaseContract as any;
} }
/**
* A [[BaseContract]] with no type guards on its methods or events.
*/
export class Contract extends _ContractBase() { } export class Contract extends _ContractBase() { }

@ -20,11 +20,34 @@ import type { ContractTransactionResponse } from "./wrappers.js";
// A = Arguments to the constructor // A = Arguments to the constructor
// I = Interface of deployed contracts // I = Interface of deployed contracts
/**
* A **ContractFactory** is used to deploy a Contract to the blockchain.
*/
export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract> { export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract> {
/**
* The Contract Interface.
*/
readonly interface!: Interface; readonly interface!: Interface;
/**
* The Contract deployment bytecode. Often called the initcode.
*/
readonly bytecode!: string; readonly bytecode!: string;
/**
* The ContractRunner to deploy the Contract as.
*/
readonly runner!: null | ContractRunner; readonly runner!: null | ContractRunner;
/**
* Create a new **ContractFactory** with %%abi%% and %%bytecode%%,
* optionally connected to %%runner%%.
*
* The %%bytecode%% may be the ``bytecode`` property within the
* standard Solidity JSON output.
*/
constructor(abi: Interface | InterfaceAbi, bytecode: BytesLike | { object: string }, runner?: null | ContractRunner) { constructor(abi: Interface | InterfaceAbi, bytecode: BytesLike | { object: string }, runner?: null | ContractRunner) {
const iface = Interface.from(abi); const iface = Interface.from(abi);
@ -42,6 +65,10 @@ export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract
}); });
} }
/**
* Resolves to the transaction to deploy the contract, passing %%args%%
* into the constructor.
*/
async getDeployTransaction(...args: ContractMethodArgs<A>): Promise<ContractDeployTransaction> { async getDeployTransaction(...args: ContractMethodArgs<A>): Promise<ContractDeployTransaction> {
let overrides: Omit<ContractDeployTransaction, "data"> = { }; let overrides: Omit<ContractDeployTransaction, "data"> = { };
@ -61,6 +88,14 @@ export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract
return Object.assign({ }, overrides, { data }); return Object.assign({ }, overrides, { data });
} }
/**
* Resolves to the Contract deployed by passing %%args%% into the
* constructor.
*
* This will resovle to the Contract before it has been deployed to the
* network, so the [[BaseContract-waitForDeployment]] should be used before
* sending any transactions to it.
*/
async deploy(...args: ContractMethodArgs<A>): Promise<BaseContract & { deploymentTransaction(): ContractTransactionResponse } & Omit<I, keyof BaseContract>> { async deploy(...args: ContractMethodArgs<A>): Promise<BaseContract & { deploymentTransaction(): ContractTransactionResponse } & Omit<I, keyof BaseContract>> {
const tx = await this.getDeployTransaction(...args); const tx = await this.getDeployTransaction(...args);
@ -73,10 +108,17 @@ export class ContractFactory<A extends Array<any> = Array<any>, I = BaseContract
return new (<any>BaseContract)(address, this.interface, this.runner, sentTx); return new (<any>BaseContract)(address, this.interface, this.runner, sentTx);
} }
/**
* Return a new **ContractFactory** with the same ABI and bytecode,
* but connected to %%runner%%.
*/
connect(runner: null | ContractRunner): ContractFactory<A, I> { connect(runner: null | ContractRunner): ContractFactory<A, I> {
return new ContractFactory(this.interface, this.bytecode, runner); return new ContractFactory(this.interface, this.bytecode, runner);
} }
/**
* Create a new **ContractFactory** from the standard Solidity JSON output.
*/
static fromSolidity<A extends Array<any> = Array<any>, I = ContractInterface>(output: any, runner?: ContractRunner): ContractFactory<A, I> { static fromSolidity<A extends Array<any> = Array<any>, I = ContractInterface>(output: any, runner?: ContractRunner): ContractFactory<A, I> {
assertArgument(output != null, "bad compiler output", "output", output); assertArgument(output != null, "bad compiler output", "output", output);

@ -1,5 +1,8 @@
/** /**
* About contracts... * A **Contract** object is a meta-class (a class whose definition is
* defined at runtime), which communicates with a deployed smart contract
* on the blockchain and provides a simple JavaScript interface to call
* methods, send transaction, query historic logs and listen for its events.
* *
* @_section: api/contract:Contracts [about-contracts] * @_section: api/contract:Contracts [about-contracts]
*/ */

@ -8,36 +8,89 @@ import type {
import type { ContractTransactionResponse } from "./wrappers.js"; import type { ContractTransactionResponse } from "./wrappers.js";
// The types of events a Contract can listen for /**
* The name for an event used for subscribing to Contract events.
*
* **``string``** - An event by name. The event must be non-ambiguous.
* The parameters will be dereferenced when passed into the listener.
*
* [[ContractEvent]] - A filter from the ``contract.filters``, which will
* pass only the EventPayload as a single parameter, which includes a
* ``.signature`` property that can be used to further filter the event.
*
* [[TopicFilter]] - A filter defined using the standard Ethereum API
* which provides the specific topic hash or topic hashes to watch for along
* with any additional values to filter by. This will only pass a single
* parameter to the listener, the EventPayload which will include additional
* details to refine by, such as the event name and signature.
*
* [[DeferredTopicFilter]] - A filter created by calling a [[ContractEvent]]
* with parameters, which will create a filter for a specific event
* signautre and dereference each parameter when calling the listener.
*/
export type ContractEventName = string | ContractEvent | TopicFilter | DeferredTopicFilter; export type ContractEventName = string | ContractEvent | TopicFilter | DeferredTopicFilter;
/**
* A Contract with no method constraints.
*/
export interface ContractInterface { export interface ContractInterface {
[ name: string ]: BaseContractMethod; [ name: string ]: BaseContractMethod;
}; };
/**
* When creating a filter using the ``contract.filters``, this is returned.
*/
export interface DeferredTopicFilter { export interface DeferredTopicFilter {
getTopicFilter(): Promise<TopicFilter>; getTopicFilter(): Promise<TopicFilter>;
fragment: EventFragment; fragment: EventFragment;
} }
/**
* When populating a transaction this type is returned.
*/
export interface ContractTransaction extends PreparedTransactionRequest { export interface ContractTransaction extends PreparedTransactionRequest {
// These are populated by contract methods and cannot bu null /**
* The target address.
*/
to: string; to: string;
/**
* The transaction data.
*/
data: string; data: string;
// These are resolved /**
* The from address, if any.
*/
from?: string; from?: string;
} }
// Deployment Transactions have no `to` /**
* A deployment transaction for a contract.
*/
export interface ContractDeployTransaction extends Omit<ContractTransaction, "to"> { } export interface ContractDeployTransaction extends Omit<ContractTransaction, "to"> { }
// Overrides; cannot override `to` or `data` as Contract populates these /**
* The overrides for a contract transaction.
*/
export interface Overrides extends Omit<TransactionRequest, "to" | "data"> { }; export interface Overrides extends Omit<TransactionRequest, "to" | "data"> { };
// Arguments for methods; with an optional (n+1)th Override /**
* Arguments to a Contract method can always include an additional and
* optional overrides parameter.
*
* @_ignore:
*/
export type PostfixOverrides<A extends Array<any>> = A | [ ...A, Overrides ]; export type PostfixOverrides<A extends Array<any>> = A | [ ...A, Overrides ];
/**
* Arguments to a Contract method can always include an additional and
* optional overrides parameter, and each parameter can optionally be
* [[Typed]].
*
* @_ignore:
*/
export type ContractMethodArgs<A extends Array<any>> = PostfixOverrides<{ [ I in keyof A ]-?: A[I] | Typed }>; export type ContractMethodArgs<A extends Array<any>> = PostfixOverrides<{ [ I in keyof A ]-?: A[I] | Typed }>;
// A = Arguments passed in as a tuple // A = Arguments passed in as a tuple
@ -45,51 +98,139 @@ export type ContractMethodArgs<A extends Array<any>> = PostfixOverrides<{ [ I in
// the qualified type, otherwise Result) // the qualified type, otherwise Result)
// D = The type the default call will return (i.e. R for view/pure, // D = The type the default call will return (i.e. R for view/pure,
// TransactionResponse otherwise) // TransactionResponse otherwise)
/**
* A Contract method can be called directly, or used in various ways.
*/
export interface BaseContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = R | ContractTransactionResponse> { export interface BaseContractMethod<A extends Array<any> = Array<any>, R = any, D extends R | ContractTransactionResponse = R | ContractTransactionResponse> {
(...args: ContractMethodArgs<A>): Promise<D>; (...args: ContractMethodArgs<A>): Promise<D>;
/**
* The name of the Contract method.
*/
name: string; name: string;
/**
* The fragment of the Contract method. This will throw on ambiguous
* method names.
*/
fragment: FunctionFragment; fragment: FunctionFragment;
/**
* Returns the fragment constrained by %%args%%. This can be used to
* resolve ambiguous method names.
*/
getFragment(...args: ContractMethodArgs<A>): FunctionFragment; getFragment(...args: ContractMethodArgs<A>): FunctionFragment;
/**
* Returns a populated transaction that can be used to perform the
* contract method with %%args%%.
*/
populateTransaction(...args: ContractMethodArgs<A>): Promise<ContractTransaction>; populateTransaction(...args: ContractMethodArgs<A>): Promise<ContractTransaction>;
/**
* Call the contract method with %%args%% and return the value.
*
* If the return value is a single type, it will be dereferenced and
* returned directly, otherwise the full Result will be returned.
*/
staticCall(...args: ContractMethodArgs<A>): Promise<R>; staticCall(...args: ContractMethodArgs<A>): Promise<R>;
/**
* Send a transaction for the contract method with %%args%%.
*/
send(...args: ContractMethodArgs<A>): Promise<ContractTransactionResponse>; send(...args: ContractMethodArgs<A>): Promise<ContractTransactionResponse>;
/**
* Estimate the gas to send the contract method with %%args%%.
*/
estimateGas(...args: ContractMethodArgs<A>): Promise<bigint>; estimateGas(...args: ContractMethodArgs<A>): Promise<bigint>;
/**
* Call the contract method with %%args%% and return the Result
* without any dereferencing.
*/
staticCallResult(...args: ContractMethodArgs<A>): Promise<Result>; staticCallResult(...args: ContractMethodArgs<A>): Promise<Result>;
} }
/**
* A contract method on a Contract.
*/
export interface ContractMethod< export interface ContractMethod<
A extends Array<any> = Array<any>, A extends Array<any> = Array<any>,
R = any, R = any,
D extends R | ContractTransactionResponse = R | ContractTransactionResponse D extends R | ContractTransactionResponse = R | ContractTransactionResponse
> extends BaseContractMethod<A, R, D> { } > extends BaseContractMethod<A, R, D> { }
/**
* A pure of view method on a Contract.
*/
export interface ConstantContractMethod< export interface ConstantContractMethod<
A extends Array<any>, A extends Array<any>,
R = any R = any
> extends ContractMethod<A, R, R> { } > extends ContractMethod<A, R, R> { }
// Arguments for events; with each element optional and/or nullable /**
* Each argument of an event is nullable (to indicate matching //any//.
*
* @_ignore:
*/
export type ContractEventArgs<A extends Array<any>> = { [ I in keyof A ]?: A[I] | Typed | null }; export type ContractEventArgs<A extends Array<any>> = { [ I in keyof A ]?: A[I] | Typed | null };
export interface ContractEvent<A extends Array<any> = Array<any>> { export interface ContractEvent<A extends Array<any> = Array<any>> {
(...args: ContractEventArgs<A>): DeferredTopicFilter; (...args: ContractEventArgs<A>): DeferredTopicFilter;
/**
* The name of the Contract event.
*/
name: string; name: string;
/**
* The fragment of the Contract event. This will throw on ambiguous
* method names.
*/
fragment: EventFragment; fragment: EventFragment;
/**
* Returns the fragment constrained by %%args%%. This can be used to
* resolve ambiguous event names.
*/
getFragment(...args: ContractEventArgs<A>): EventFragment; getFragment(...args: ContractEventArgs<A>): EventFragment;
}; };
/**
* A Fallback or Receive function on a Contract.
*/
export interface WrappedFallback { export interface WrappedFallback {
(overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransactionResponse>; (overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransactionResponse>;
/**
* Returns a populated transaction that can be used to perform the
* fallback method.
*
* For non-receive fallback, ``data`` may be overridden.
*/
populateTransaction(overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransaction>; populateTransaction(overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransaction>;
/**
* Call the contract fallback and return the result.
*
* For non-receive fallback, ``data`` may be overridden.
*/
staticCall(overrides?: Omit<TransactionRequest, "to">): Promise<string>; staticCall(overrides?: Omit<TransactionRequest, "to">): Promise<string>;
/**
* Send a transaction to the contract fallback.
*
* For non-receive fallback, ``data`` may be overridden.
*/
send(overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransactionResponse>; send(overrides?: Omit<TransactionRequest, "to">): Promise<ContractTransactionResponse>;
/**
* Estimate the gas to send a transaction to the contract fallback.
*
* For non-receive fallback, ``data`` may be overridden.
*/
estimateGas(overrides?: Omit<TransactionRequest, "to">): Promise<bigint>; estimateGas(overrides?: Omit<TransactionRequest, "to">): Promise<bigint>;
} }

@ -14,30 +14,64 @@ import type {
import type { BaseContract } from "./contract.js"; import type { BaseContract } from "./contract.js";
import type { ContractEventName } from "./types.js"; import type { ContractEventName } from "./types.js";
/**
* An **EventLog** contains additional properties parsed from the [[Log]].
*/
export class EventLog extends Log { export class EventLog extends Log {
/**
* The Contract Interface.
*/
readonly interface!: Interface; readonly interface!: Interface;
/**
* The matching event.
*/
readonly fragment!: EventFragment; readonly fragment!: EventFragment;
/**
* The parsed arguments passed to the event by ``emit``.
*/
readonly args!: Result; readonly args!: Result;
/**
* @_ignore:
*/
constructor(log: Log, iface: Interface, fragment: EventFragment) { constructor(log: Log, iface: Interface, fragment: EventFragment) {
super(log, log.provider); super(log, log.provider);
const args = iface.decodeEventLog(fragment, log.data, log.topics); const args = iface.decodeEventLog(fragment, log.data, log.topics);
defineProperties<EventLog>(this, { args, fragment, interface: iface }); defineProperties<EventLog>(this, { args, fragment, interface: iface });
} }
/**
* The name of the event.
*/
get eventName(): string { return this.fragment.name; } get eventName(): string { return this.fragment.name; }
/**
* The signature of the event.
*/
get eventSignature(): string { return this.fragment.format(); } get eventSignature(): string { return this.fragment.format(); }
} }
/**
* A **ContractTransactionReceipt** includes the parsed logs from a
* [[TransactionReceipt]].
*/
export class ContractTransactionReceipt extends TransactionReceipt { export class ContractTransactionReceipt extends TransactionReceipt {
readonly #iface: Interface; readonly #iface: Interface;
/**
* @_ignore:
*/
constructor(iface: Interface, provider: Provider, tx: TransactionReceipt) { constructor(iface: Interface, provider: Provider, tx: TransactionReceipt) {
super(tx, provider); super(tx, provider);
this.#iface = iface; this.#iface = iface;
} }
/**
* The parsed logs for any [[Log]] which has a matching event in the
* Contract ABI.
*/
get logs(): Array<EventLog | Log> { get logs(): Array<EventLog | Log> {
return super.logs.map((log) => { return super.logs.map((log) => {
const fragment = log.topics.length ? this.#iface.getEvent(log.topics[0]): null; const fragment = log.topics.length ? this.#iface.getEvent(log.topics[0]): null;
@ -51,14 +85,30 @@ export class ContractTransactionReceipt extends TransactionReceipt {
} }
/**
* A **ContractTransactionResponse** will return a
* [[ContractTransactionReceipt]] when waited on.
*/
export class ContractTransactionResponse extends TransactionResponse { export class ContractTransactionResponse extends TransactionResponse {
readonly #iface: Interface; readonly #iface: Interface;
/**
* @_ignore:
*/
constructor(iface: Interface, provider: Provider, tx: TransactionResponse) { constructor(iface: Interface, provider: Provider, tx: TransactionResponse) {
super(tx, provider); super(tx, provider);
this.#iface = iface; this.#iface = iface;
} }
/**
* Resolves once this transaction has been mined and has
* %%confirms%% blocks including it (default: ``1``) with an
* optional %%timeout%%.
*
* This can resolve to ``null`` only if %%confirms%% is ``0``
* and the transaction has not been mined, otherwise this will
* wait until enough confirmations have completed.
*/
async wait(confirms?: number): Promise<null | ContractTransactionReceipt> { async wait(confirms?: number): Promise<null | ContractTransactionReceipt> {
const receipt = await super.wait(); const receipt = await super.wait();
if (receipt == null) { return null; } if (receipt == null) { return null; }
@ -66,43 +116,86 @@ export class ContractTransactionResponse extends TransactionResponse {
} }
} }
/**
* A **ContractUnknownEventPayload** is included as the last parameter to
* Contract Events when the event does not match any events in the ABI.
*/
export class ContractUnknownEventPayload extends EventPayload<ContractEventName> { export class ContractUnknownEventPayload extends EventPayload<ContractEventName> {
/**
* The log with no matching events.
*/
readonly log!: Log; readonly log!: Log;
/**
* @_event:
*/
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, log: Log) { constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, log: Log) {
super(contract, listener, filter); super(contract, listener, filter);
defineProperties<ContractUnknownEventPayload>(this, { log }); defineProperties<ContractUnknownEventPayload>(this, { log });
} }
/**
* Resolves to the block the event occured in.
*/
async getBlock(): Promise<Block> { async getBlock(): Promise<Block> {
return await this.log.getBlock(); return await this.log.getBlock();
} }
/**
* Resolves to the transaction the event occured in.
*/
async getTransaction(): Promise<TransactionResponse> { async getTransaction(): Promise<TransactionResponse> {
return await this.log.getTransaction(); return await this.log.getTransaction();
} }
/**
* Resolves to the transaction receipt the event occured in.
*/
async getTransactionReceipt(): Promise<TransactionReceipt> { async getTransactionReceipt(): Promise<TransactionReceipt> {
return await this.log.getTransactionReceipt(); return await this.log.getTransactionReceipt();
} }
} }
/**
* A **ContractEventPayload** is included as the last parameter to
* Contract Events when the event is known.
*/
export class ContractEventPayload extends ContractUnknownEventPayload { export class ContractEventPayload extends ContractUnknownEventPayload {
/**
* The matching event.
*/
declare readonly fragment: EventFragment; declare readonly fragment: EventFragment;
/**
* The log, with parsed properties.
*/
declare readonly log: EventLog; declare readonly log: EventLog;
/**
* The parsed arguments passed to the event by ``emit``.
*/
declare readonly args: Result; declare readonly args: Result;
/**
* @_ignore:
*/
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, fragment: EventFragment, _log: Log) { constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, fragment: EventFragment, _log: Log) {
super(contract, listener, filter, new EventLog(_log, contract.interface, fragment)); super(contract, listener, filter, new EventLog(_log, contract.interface, fragment));
const args = contract.interface.decodeEventLog(fragment, this.log.data, this.log.topics); const args = contract.interface.decodeEventLog(fragment, this.log.data, this.log.topics);
defineProperties<ContractEventPayload>(this, { args, fragment }); defineProperties<ContractEventPayload>(this, { args, fragment });
} }
/**
* The event name.
*/
get eventName(): string { get eventName(): string {
return this.fragment.name; return this.fragment.name;
} }
/**
* The event signature.
*/
get eventSignature(): string { get eventSignature(): string {
return this.fragment.format(); return this.fragment.format();
} }

@ -32,6 +32,10 @@ export {
export { SigningKey } from "./signing-key.js"; export { SigningKey } from "./signing-key.js";
export { Signature } from "./signature.js"; export { Signature } from "./signature.js";
/**
* Once called, prevents any future change to the underlying cryptographic
* primitives using the ``.register`` feature for hooks.
*/
function lock(): void { function lock(): void {
computeHmac.lock(); computeHmac.lock();
keccak256.lock(); keccak256.lock();

@ -1,5 +1,6 @@
/** /**
* About hashing here... * Utilities for common tasks involving hashing. Also see
* [cryptographic hashing](about-crypto-hashing).
* *
* @_section: api/hashing:Hashing Utilities [about-hashing] * @_section: api/hashing:Hashing Utilities [about-hashing]
*/ */

@ -21,16 +21,50 @@ const BN_0 = BigInt(0);
const BN_1 = BigInt(1); const BN_1 = BigInt(1);
const BN_MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); const BN_MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
// @TODO: in v7, verifyingContract should be an AddressLike and use resolveAddress
/**
* The domain for an [[link-eip-712]] payload.
*/
export interface TypedDataDomain { export interface TypedDataDomain {
/**
* The human-readable name of the signing domain.
*/
name?: null | string; name?: null | string;
/**
* The major version of the signing domain.
*/
version?: null | string; version?: null | string;
/**
* The chain ID of the signing domain.
*/
chainId?: null | BigNumberish; chainId?: null | BigNumberish;
/**
* The the address of the contract that will verify the signature.
*/
verifyingContract?: null | string; verifyingContract?: null | string;
/**
* A salt used for purposes decided by the specific domain.
*/
salt?: null | BytesLike; salt?: null | BytesLike;
}; };
/**
* A specific field of a structured [[link-eip-712]] type.
*/
export interface TypedDataField { export interface TypedDataField {
/**
* The field name.
*/
name: string; name: string;
/**
* The type of the field.
*/
type: string; type: string;
}; };
@ -147,10 +181,30 @@ function encodeType(name: string, fields: Array<TypedDataField>): string {
return `${ name }(${ fields.map(({ name, type }) => (type + " " + name)).join(",") })`; return `${ name }(${ fields.map(({ name, type }) => (type + " " + name)).join(",") })`;
} }
/**
* A **TypedDataEncode** prepares and encodes [[link-eip-712]] payloads
* for signed typed data.
*
* This is useful for those that wish to compute various components of a
* typed data hash, primary types, or sub-components, but generally the
* higher level [[Signer-signTypedData]] is more useful.
*/
export class TypedDataEncoder { export class TypedDataEncoder {
/**
* The primary type for the structured [[types]].
*
* This is derived automatically from the [[types]], since no
* recursion is possible, once the DAG for the types is consturcted
* internally, the primary type must be the only remaining type with
* no parent nodes.
*/
readonly primaryType!: string; readonly primaryType!: string;
readonly #types: string; readonly #types: string;
/**
* The types.
*/
get types(): Record<string, Array<TypedDataField>> { get types(): Record<string, Array<TypedDataField>> {
return JSON.parse(this.#types); return JSON.parse(this.#types);
} }
@ -159,6 +213,13 @@ export class TypedDataEncoder {
readonly #encoderCache: Map<string, (value: any) => string>; readonly #encoderCache: Map<string, (value: any) => string>;
/**
* Create a new **TypedDataEncoder** for %%types%%.
*
* This performs all necessary checking that types are valid and
* do not violate the [[link-eip-712]] structural constraints as
* well as computes the [[primaryType]].
*/
constructor(types: Record<string, Array<TypedDataField>>) { constructor(types: Record<string, Array<TypedDataField>>) {
this.#types = JSON.stringify(types); this.#types = JSON.stringify(types);
this.#fullTypes = new Map(); this.#fullTypes = new Map();
@ -241,6 +302,9 @@ export class TypedDataEncoder {
} }
} }
/**
* Returnthe encoder for the specific %%type%%.
*/
getEncoder(type: string): (value: any) => string { getEncoder(type: string): (value: any) => string {
let encoder = this.#encoderCache.get(type); let encoder = this.#encoderCache.get(type);
if (!encoder) { if (!encoder) {
@ -293,28 +357,46 @@ export class TypedDataEncoder {
assertArgument(false, `unknown type: ${ type }`, "type", type); assertArgument(false, `unknown type: ${ type }`, "type", type);
} }
/**
* Return the full type for %%name%%.
*/
encodeType(name: string): string { encodeType(name: string): string {
const result = this.#fullTypes.get(name); const result = this.#fullTypes.get(name);
assertArgument(result, `unknown type: ${ JSON.stringify(name) }`, "name", name); assertArgument(result, `unknown type: ${ JSON.stringify(name) }`, "name", name);
return result; return result;
} }
/**
* Return the encoded %%value%% for the %%type%%.
*/
encodeData(type: string, value: any): string { encodeData(type: string, value: any): string {
return this.getEncoder(type)(value); return this.getEncoder(type)(value);
} }
/**
* Returns the hash of %%value%% for the type of %%name%%.
*/
hashStruct(name: string, value: Record<string, any>): string { hashStruct(name: string, value: Record<string, any>): string {
return keccak256(this.encodeData(name, value)); return keccak256(this.encodeData(name, value));
} }
/**
* Return the fulled encoded %%value%% for the [[types]].
*/
encode(value: Record<string, any>): string { encode(value: Record<string, any>): string {
return this.encodeData(this.primaryType, value); return this.encodeData(this.primaryType, value);
} }
/**
* Return the hash of the fully encoded %%value%% for the [[types]].
*/
hash(value: Record<string, any>): string { hash(value: Record<string, any>): string {
return this.hashStruct(this.primaryType, value); return this.hashStruct(this.primaryType, value);
} }
/**
* @_ignore:
*/
_visit(type: string, value: any, callback: (type: string, data: any) => any): any { _visit(type: string, value: any, callback: (type: string, data: any) => any): any {
// Basic encoder type (address, bool, uint256, etc) // Basic encoder type (address, bool, uint256, etc)
{ {
@ -341,22 +423,41 @@ export class TypedDataEncoder {
assertArgument(false, `unknown type: ${ type }`, "type", type); assertArgument(false, `unknown type: ${ type }`, "type", type);
} }
/**
* Call %%calback%% for each value in %%value%%, passing the type and
* component within %%value%%.
*
* This is useful for replacing addresses or other transformation that
* may be desired on each component, based on its type.
*/
visit(value: Record<string, any>, callback: (type: string, data: any) => any): any { visit(value: Record<string, any>, callback: (type: string, data: any) => any): any {
return this._visit(this.primaryType, value, callback); return this._visit(this.primaryType, value, callback);
} }
/**
* Create a new **TypedDataEncoder** for %%types%%.
*/
static from(types: Record<string, Array<TypedDataField>>): TypedDataEncoder { static from(types: Record<string, Array<TypedDataField>>): TypedDataEncoder {
return new TypedDataEncoder(types); return new TypedDataEncoder(types);
} }
/**
* Return the primary type for %%types%%.
*/
static getPrimaryType(types: Record<string, Array<TypedDataField>>): string { static getPrimaryType(types: Record<string, Array<TypedDataField>>): string {
return TypedDataEncoder.from(types).primaryType; return TypedDataEncoder.from(types).primaryType;
} }
/**
* Return the hashed struct for %%value%% using %%types%% and %%name%%.
*/
static hashStruct(name: string, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string { static hashStruct(name: string, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
return TypedDataEncoder.from(types).hashStruct(name, value); return TypedDataEncoder.from(types).hashStruct(name, value);
} }
/**
* Return the domain hash for %%domain%%.
*/
static hashDomain(domain: TypedDataDomain): string { static hashDomain(domain: TypedDataDomain): string {
const domainFields: Array<TypedDataField> = [ ]; const domainFields: Array<TypedDataField> = [ ];
for (const name in domain) { for (const name in domain) {
@ -373,6 +474,9 @@ export class TypedDataEncoder {
return TypedDataEncoder.hashStruct("EIP712Domain", { EIP712Domain: domainFields }, domain); return TypedDataEncoder.hashStruct("EIP712Domain", { EIP712Domain: domainFields }, domain);
} }
/**
* Return the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%.
*/
static encode(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string { static encode(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
return concat([ return concat([
"0x1901", "0x1901",
@ -381,11 +485,18 @@ export class TypedDataEncoder {
]); ]);
} }
/**
* Return the hash of the fully encoded [[link-eip-712]] %%value%% for %%types%% with %%domain%%.
*/
static hash(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string { static hash(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): string {
return keccak256(TypedDataEncoder.encode(domain, types, value)); return keccak256(TypedDataEncoder.encode(domain, types, value));
} }
// Replaces all address types with ENS names with their looked up address // Replaces all address types with ENS names with their looked up address
/**
* Resolves to the value from resolving all addresses in %%value%% for
* %%types%% and the %%domain%%.
*/
static async resolveNames(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, resolveName: (name: string) => Promise<string>): Promise<{ domain: TypedDataDomain, value: any }> { static async resolveNames(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, resolveName: (name: string) => Promise<string>): Promise<{ domain: TypedDataDomain, value: any }> {
// Make a copy to isolate it from the object passed in // Make a copy to isolate it from the object passed in
domain = Object.assign({ }, domain); domain = Object.assign({ }, domain);
@ -435,6 +546,10 @@ export class TypedDataEncoder {
return { domain, value }; return { domain, value };
} }
/**
* Returns the JSON-encoded payload expected by nodes which implement
* the JSON-RPC [[link-eip-712]] method.
*/
static getPayload(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): any { static getPayload(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): any {
// Validate the domain fields // Validate the domain fields
TypedDataEncoder.hashDomain(domain); TypedDataEncoder.hashDomain(domain);

@ -1,5 +1,7 @@
/** /**
* About Subclassing the Provider... * The available providers should suffice for most developers purposes,
* but the [[AbstractProvider]] class has many features which enable
* sub-classing it for specific purposes.
* *
* @_section: api/providers/abstract-provider: Subclassing Provider [abstract-provider] * @_section: api/providers/abstract-provider: Subclassing Provider [abstract-provider]
*/ */
@ -86,6 +88,10 @@ function getTag(prefix: string, value: any): string {
}); });
} }
/**
* The types of additional event values that can be emitted for the
* ``"debug"`` event.
*/
export type DebugEventAbstractProvider = { export type DebugEventAbstractProvider = {
action: "sendCcipReadFetchRequest", action: "sendCcipReadFetchRequest",
request: FetchRequest request: FetchRequest
@ -113,7 +119,12 @@ export type DebugEventAbstractProvider = {
}; };
// Only sub-classes overriding the _getSubscription method will care about this /**
* The value passed to the [[AbstractProvider-_getSubscriber]] method.
*
* Only developers sub-classing [[AbstractProvider[[ will care about this,
* if they are modifying a low-level feature of how subscriptions operate.
*/
export type Subscription = { export type Subscription = {
type: "block" | "close" | "debug" | "error" | "network" | "pending", type: "block" | "close" | "debug" | "error" | "network" | "pending",
tag: string tag: string
@ -131,22 +142,59 @@ export type Subscription = {
filter: OrphanFilter filter: OrphanFilter
}; };
/**
* A **Subscriber** manages a subscription.
*
* Only developers sub-classing [[AbstractProvider[[ will care about this,
* if they are modifying a low-level feature of how subscriptions operate.
*/
export interface Subscriber { export interface Subscriber {
/**
* Called initially when a subscriber is added the first time.
*/
start(): void; start(): void;
/**
* Called when there are no more subscribers to the event.
*/
stop(): void; stop(): void;
/**
* Called when the subscription should pause.
*
* If %%dropWhilePaused%%, events that occur while paused should not
* be emitted [[resume]].
*/
pause(dropWhilePaused?: boolean): void; pause(dropWhilePaused?: boolean): void;
/**
* Resume a paused subscriber.
*/
resume(): void; resume(): void;
// Subscribers which use polling should implement this to allow /**
// Providers the ability to update underlying polling intervals * The frequency (in ms) to poll for events, if polling is used by
// If not supported, accessing this property should return undefined * the subscriber.
*
* For non-polling subscribers, this must return ``undefined``.
*/
pollingInterval?: number; pollingInterval?: number;
} }
/**
* An **UnmanagedSubscriber** is useful for events which do not require
* any additional management, such as ``"debug"`` which only requires
* emit in synchronous event loop triggered calls.
*/
export class UnmanagedSubscriber implements Subscriber { export class UnmanagedSubscriber implements Subscriber {
/**
* The name fof the event.
*/
name!: string; name!: string;
/**
* Create a new UnmanagedSubscriber with %%name%%.
*/
constructor(name: string) { defineProperties<UnmanagedSubscriber>(this, { name }); } constructor(name: string) { defineProperties<UnmanagedSubscriber>(this, { name }); }
start(): void { } start(): void { }
@ -247,11 +295,26 @@ async function getSubscription(_event: ProviderEvent, provider: AbstractProvider
function getTime(): number { return (new Date()).getTime(); } function getTime(): number { return (new Date()).getTime(); }
/**
* An **AbstractPlugin** is used to provide additional internal services
* to an [[AbstractProvider]] without adding backwards-incompatible changes
* to method signatures or other internal and complex logic.
*/
export interface AbstractProviderPlugin { export interface AbstractProviderPlugin {
/**
* The reverse domain notation of the plugin.
*/
readonly name: string; readonly name: string;
/**
* Creates a new instance of the plugin, connected to %%provider%%.
*/
connect(provider: AbstractProvider): AbstractProviderPlugin; connect(provider: AbstractProvider): AbstractProviderPlugin;
} }
/**
* A normalized filter used for [[PerformActionRequest]] objects.
*/
export type PerformActionFilter = { export type PerformActionFilter = {
address?: string | Array<string>; address?: string | Array<string>;
topics?: Array<null | string | Array<string>>; topics?: Array<null | string | Array<string>>;
@ -263,11 +326,25 @@ export type PerformActionFilter = {
blockHash?: string; blockHash?: string;
}; };
/**
* A normalized transactions used for [[PerformActionRequest]] objects.
*/
export interface PerformActionTransaction extends PreparedTransactionRequest { export interface PerformActionTransaction extends PreparedTransactionRequest {
/**
* The ``to`` address of the transaction.
*/
to?: string; to?: string;
/**
* The sender of the transaction.
*/
from?: string; from?: string;
} }
/**
* The [[AbstractProvider]] methods will normalize all values and pass this
* type to [[AbstractProvider-_perform]].
*/
export type PerformActionRequest = { export type PerformActionRequest = {
method: "broadcastTransaction", method: "broadcastTransaction",
signedTransaction: string signedTransaction: string
@ -330,7 +407,12 @@ type CcipArgs = {
errorArgs: Array<any> errorArgs: Array<any>
}; };
/**
* An **AbstractProvider** provides a base class for other sub-classes to
* implement the [[Provider]] API by normalizing input arguments and
* formatting output results as well as tracking events for consistent
* behaviour on an eventually-consistent network.
*/
export class AbstractProvider implements Provider { export class AbstractProvider implements Provider {
#subs: Map<string, Sub>; #subs: Map<string, Sub>;
@ -352,8 +434,11 @@ export class AbstractProvider implements Provider {
#disableCcipRead: boolean; #disableCcipRead: boolean;
// @TODO: This should be a () => Promise<Network> so network can be /**
// done when needed; or rely entirely on _detectNetwork? * 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) {
if (_network === "any") { if (_network === "any") {
@ -383,12 +468,22 @@ export class AbstractProvider implements Provider {
this.#disableCcipRead = false; this.#disableCcipRead = false;
} }
/**
* Returns ``this``, to allow an **AbstractProvider** to implement
* the [[ContractRunner]] interface.
*/
get provider(): this { return this; } get provider(): this { return this; }
/**
* Returns all the registered plug-ins.
*/
get plugins(): Array<AbstractProviderPlugin> { get plugins(): Array<AbstractProviderPlugin> {
return Array.from(this.#plugins.values()); return Array.from(this.#plugins.values());
} }
/**
* Attach a new plug-in.
*/
attachPlugin(plugin: AbstractProviderPlugin): this { attachPlugin(plugin: AbstractProviderPlugin): this {
if (this.#plugins.get(plugin.name)) { if (this.#plugins.get(plugin.name)) {
throw new Error(`cannot replace existing plugin: ${ plugin.name } `); throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
@ -397,10 +492,17 @@ export class AbstractProvider implements Provider {
return this; return this;
} }
/**
* Get a plugin by name.
*/
getPlugin<T extends AbstractProviderPlugin = AbstractProviderPlugin>(name: string): null | T { getPlugin<T extends AbstractProviderPlugin = AbstractProviderPlugin>(name: string): null | T {
return <T>(this.#plugins.get(name)) || null; return <T>(this.#plugins.get(name)) || null;
} }
/**
* Prevent any CCIP-read operation, regardless of whether requested
* in a [[call]] using ``enableCcipRead``.
*/
get disableCcipRead(): boolean { return this.#disableCcipRead; } get disableCcipRead(): boolean { return this.#disableCcipRead; }
set disableCcipRead(value: boolean) { this.#disableCcipRead = !!value; } set disableCcipRead(value: boolean) { this.#disableCcipRead = !!value; }
@ -424,6 +526,9 @@ export class AbstractProvider implements Provider {
return await perform; return await perform;
} }
/**
* Resolves to the data for executing the CCIP-read operations.
*/
async ccipReadFetch(tx: PerformActionTransaction, calldata: string, urls: Array<string>): Promise<null | string> { async ccipReadFetch(tx: PerformActionTransaction, calldata: string, urls: Array<string>): Promise<null | string> {
if (this.disableCcipRead || urls.length === 0 || tx.to == null) { return null; } if (this.disableCcipRead || urls.length === 0 || tx.to == null) { return null; }
@ -479,30 +584,60 @@ export class AbstractProvider implements Provider {
}); });
} }
/**
* Provides the opportunity for a sub-class to wrap a block before
* returning it, to add additional properties or an alternate
* sub-class of [[Block]].
*/
_wrapBlock(value: BlockParams, network: Network): Block { _wrapBlock(value: BlockParams, network: Network): Block {
return new Block(formatBlock(value), this); return new Block(formatBlock(value), this);
} }
/**
* Provides the opportunity for a sub-class to wrap a log before
* returning it, to add additional properties or an alternate
* sub-class of [[Log]].
*/
_wrapLog(value: LogParams, network: Network): Log { _wrapLog(value: LogParams, network: Network): Log {
return new Log(formatLog(value), this); return new Log(formatLog(value), this);
} }
/**
* Provides the opportunity for a sub-class to wrap a transaction
* receipt before returning it, to add additional properties or an
* alternate sub-class of [[TransactionReceipt]].
*/
_wrapTransactionReceipt(value: TransactionReceiptParams, network: Network): TransactionReceipt { _wrapTransactionReceipt(value: TransactionReceiptParams, network: Network): TransactionReceipt {
return new TransactionReceipt(formatTransactionReceipt(value), this); return new TransactionReceipt(formatTransactionReceipt(value), this);
} }
/**
* Provides the opportunity for a sub-class to wrap a transaction
* response before returning it, to add additional properties or an
* alternate sub-class of [[TransactionResponse]].
*/
_wrapTransactionResponse(tx: TransactionResponseParams, network: Network): TransactionResponse { _wrapTransactionResponse(tx: TransactionResponseParams, network: Network): TransactionResponse {
return new TransactionResponse(formatTransactionResponse(tx), this); return new TransactionResponse(formatTransactionResponse(tx), this);
} }
/**
* Resolves to the Network, forcing a network detection using whatever
* technique the sub-class requires.
*
* Sub-classes **must** override this.
*/
_detectNetwork(): Promise<Network> { _detectNetwork(): Promise<Network> {
assert(false, "sub-classes must implement this", "UNSUPPORTED_OPERATION", { assert(false, "sub-classes must implement this", "UNSUPPORTED_OPERATION", {
operation: "_detectNetwork" operation: "_detectNetwork"
}); });
} }
// Sub-classes should override this and handle PerformActionRequest requests, calling /**
// the super for any unhandled actions. * Sub-classes should use this to perform all built-in operations. All
* methods sanitizes and normalizes the values passed into this.
*
* Sub-classes **must** override this.
*/
async _perform<T = any>(req: PerformActionRequest): Promise<T> { async _perform<T = any>(req: PerformActionRequest): Promise<T> {
assert(false, `unsupported method: ${ req.method }`, "UNSUPPORTED_OPERATION", { assert(false, `unsupported method: ${ req.method }`, "UNSUPPORTED_OPERATION", {
operation: req.method, operation: req.method,
@ -511,16 +646,26 @@ export class AbstractProvider implements Provider {
} }
// State // State
async getBlockNumber(): Promise<number> { async getBlockNumber(): Promise<number> {
const blockNumber = getNumber(await this.#perform({ method: "getBlockNumber" }), "%response"); const blockNumber = getNumber(await this.#perform({ method: "getBlockNumber" }), "%response");
if (this.#lastBlockNumber >= 0) { this.#lastBlockNumber = blockNumber; } if (this.#lastBlockNumber >= 0) { this.#lastBlockNumber = blockNumber; }
return blockNumber; return blockNumber;
} }
/**
* Returns or resolves to the address for %%address%%, resolving ENS
* names and [[Addressable]] objects and returning if already an
* address.
*/
_getAddress(address: AddressLike): string | Promise<string> { _getAddress(address: AddressLike): string | Promise<string> {
return resolveAddress(address, this); return resolveAddress(address, this);
} }
/**
* Returns or resolves to a valid block tag for %%blockTag%%, resolving
* negative values and returning if already a valid block tag.
*/
_getBlockTag(blockTag?: BlockTag): string | Promise<string> { _getBlockTag(blockTag?: BlockTag): string | Promise<string> {
if (blockTag == null) { return "latest"; } if (blockTag == null) { return "latest"; }
@ -550,6 +695,11 @@ export class AbstractProvider implements Provider {
assertArgument(false, "invalid blockTag", "blockTag", blockTag); assertArgument(false, "invalid blockTag", "blockTag", blockTag);
} }
/**
* Returns or resolves to a filter for %%filter%%, resolving any ENS
* names or [[Addressable]] object and returning if already a valid
* filter.
*/
_getFilter(filter: Filter | FilterByBlockHash): PerformActionFilter | Promise<PerformActionFilter> { _getFilter(filter: Filter | FilterByBlockHash): PerformActionFilter | Promise<PerformActionFilter> {
// Create a canonical representation of the topics // Create a canonical representation of the topics
@ -619,6 +769,11 @@ export class AbstractProvider implements Provider {
return resolve(<Array<string>>address, fromBlock, toBlock); return resolve(<Array<string>>address, fromBlock, toBlock);
} }
/**
* Returns or resovles to a transaction for %%request%%, resolving
* any ENS names or [[Addressable]] and returning if already a valid
* transaction.
*/
_getTransactionRequest(_request: TransactionRequest): PerformActionTransaction | Promise<PerformActionTransaction> { _getTransactionRequest(_request: TransactionRequest): PerformActionTransaction | Promise<PerformActionTransaction> {
const request = <PerformActionTransaction>copyRequest(_request); const request = <PerformActionTransaction>copyRequest(_request);
@ -1059,6 +1214,9 @@ export class AbstractProvider implements Provider {
}); });
} }
/**
* Clear a timer created using the [[_setTimeout]] method.
*/
_clearTimeout(timerId: number): void { _clearTimeout(timerId: number): void {
const timer = this.#timers.get(timerId); const timer = this.#timers.get(timerId);
if (!timer) { return; } if (!timer) { return; }
@ -1066,6 +1224,14 @@ export class AbstractProvider implements Provider {
this.#timers.delete(timerId); this.#timers.delete(timerId);
} }
/**
* Create a timer that will execute %%func%% after at least %%timeout%%
* (in ms). If %%timeout%% is unspecified, then %%func%% will execute
* in the next event loop.
*
* [Pausing](AbstractProvider-paused) the provider will pause any
* associated timers.
*/
_setTimeout(_func: () => void, timeout?: number): number { _setTimeout(_func: () => void, timeout?: number): number {
if (timeout == null) { timeout = 0; } if (timeout == null) { timeout = 0; }
const timerId = this.#nextTimer++; const timerId = this.#nextTimer++;
@ -1084,14 +1250,19 @@ export class AbstractProvider implements Provider {
return timerId; return timerId;
} }
/**
* Perform %%func%% on each subscriber.
*/
_forEachSubscriber(func: (s: Subscriber) => void): void { _forEachSubscriber(func: (s: Subscriber) => void): void {
for (const sub of this.#subs.values()) { for (const sub of this.#subs.values()) {
func(sub.subscriber); func(sub.subscriber);
} }
} }
// Event API; sub-classes should override this; any supported /**
// event filter will have been munged into an EventFilter * Sub-classes may override this to customize subscription
* implementations.
*/
_getSubscriber(sub: Subscription): Subscriber { _getSubscriber(sub: Subscription): Subscriber {
switch (sub.type) { switch (sub.type) {
case "debug": case "debug":
@ -1111,6 +1282,15 @@ export class AbstractProvider implements Provider {
throw new Error(`unsupported event: ${ sub.type }`); throw new Error(`unsupported event: ${ sub.type }`);
} }
/**
* If a [[Subscriber]] fails and needs to replace itself, this
* method may be used.
*
* For example, this is used for providers when using the
* ``eth_getFilterChanges`` method, which can return null if state
* filters are not supported by the backend, allowing the Subscriber
* to swap in a [[PollingEventSubscriber]].
*/
_recoverSubscriber(oldSub: Subscriber, newSub: Subscriber): void { _recoverSubscriber(oldSub: Subscriber, newSub: Subscriber): void {
for (const sub of this.#subs.values()) { for (const sub of this.#subs.values()) {
if (sub.subscriber === oldSub) { if (sub.subscriber === oldSub) {
@ -1265,8 +1445,12 @@ export class AbstractProvider implements Provider {
return this.off(event, listener); return this.off(event, listener);
} }
// Sub-classes should override this to shutdown any sockets, etc. /**
// but MUST call this super.shutdown. * Sub-classes may use this to shutdown any sockets or release their
* resources.
*
* Sub-classes **must** call ``super.destroy()``.
*/
destroy(): void { destroy(): void {
// Stop all listeners // Stop all listeners
this.removeAllListeners(); this.removeAllListeners();
@ -1277,6 +1461,17 @@ export class AbstractProvider implements Provider {
} }
} }
/**
* Whether the provider is currently paused.
*
* A paused provider will not emit any events, and generally should
* not make any requests to the network, but that is up to sub-classes
* to manage.
*
* Setting ``paused = true`` is identical to calling ``.pause(false)``,
* which will buffer any events that occur while paused until the
* provider is unpaused.
*/
get paused(): boolean { return (this.#pausedState != null); } get paused(): boolean { return (this.#pausedState != null); }
set paused(pause: boolean) { set paused(pause: boolean) {
if (!!pause === this.paused) { return; } if (!!pause === this.paused) { return; }
@ -1288,6 +1483,11 @@ export class AbstractProvider implements Provider {
} }
} }
/**
* Pause the provider. If %%dropWhilePaused%%, any events that occur
* while paused are dropped, otherwise all events will be emitted once
* the provider is unpaused.
*/
pause(dropWhilePaused?: boolean): void { pause(dropWhilePaused?: boolean): void {
this.#lastBlockNumber = -1; this.#lastBlockNumber = -1;
@ -1310,6 +1510,9 @@ export class AbstractProvider implements Provider {
} }
} }
/**
* Resume the provider.
*/
resume(): void { resume(): void {
if (this.#pausedState == null) { return; } if (this.#pausedState == null) { return; }

@ -1,5 +1,7 @@
/** /**
* About Abstract Signer and subclassing * Generally the [[Wallet]] and [[JsonRpcSigner]] and their sub-classes
* are sufficent for most developers, but this is provided to
* fascilitate more complex Signers.
* *
* @_section: api/providers/abstract-signer: Subclassing Signer [abstract-signer] * @_section: api/providers/abstract-signer: Subclassing Signer [abstract-signer]
*/ */
@ -49,14 +51,36 @@ async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise
} }
/**
* An **AbstractSigner** includes most of teh functionality required
* to get a [[Signer]] working as expected, but requires a few
* Signer-specific methods be overridden.
*
*/
export abstract class AbstractSigner<P extends null | Provider = null | Provider> implements Signer { export abstract class AbstractSigner<P extends null | Provider = null | Provider> implements Signer {
/**
* The provider this signer is connected to.
*/
readonly provider!: P; readonly provider!: P;
/**
* Creates a new Signer connected to %%provider%%.
*/
constructor(provider?: P) { constructor(provider?: P) {
defineProperties<AbstractSigner>(this, { provider: (provider || null) }); defineProperties<AbstractSigner>(this, { provider: (provider || null) });
} }
/**
* Resolves to the Signer address.
*/
abstract getAddress(): Promise<string>; abstract getAddress(): Promise<string>;
/**
* Returns the signer connected to %%provider%%.
*
* This may throw, for example, a Signer connected over a Socket or
* to a specific instance of a node may not be transferrable.
*/
abstract connect(provider: null | Provider): Signer; abstract connect(provider: null | Provider): Signer;
async getNonce(blockTag?: BlockTag): Promise<number> { async getNonce(blockTag?: BlockTag): Promise<number> {
@ -216,9 +240,24 @@ export abstract class AbstractSigner<P extends null | Provider = null | Provider
abstract signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>; abstract signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
} }
/**
* A **VoidSigner** is a class deisgned to allow an address to be used
* in any API which accepts a Signer, but for which there are no
* credentials available to perform any actual signing.
*
* This for example allow impersonating an account for the purpose of
* static calls or estimating gas, but does not allow sending transactions.
*/
export class VoidSigner extends AbstractSigner { export class VoidSigner extends AbstractSigner {
/**
* The signer address.
*/
readonly address!: string; readonly address!: string;
/**
* Creates a new **VoidSigner** with %%address%% attached to
* %%provider%%.
*/
constructor(address: string, provider?: null | Provider) { constructor(address: string, provider?: null | Provider) {
super(provider); super(provider);
defineProperties<VoidSigner>(this, { address }); defineProperties<VoidSigner>(this, { address });

@ -2,20 +2,41 @@ import type {
Provider, TransactionRequest, TransactionResponse Provider, TransactionRequest, TransactionResponse
} from "./provider.js"; } from "./provider.js";
// The object that will be used to run Contracts. The Signer and Provider /**
// both adhere to this, but other types of objects may wish to as well. * A **ContractRunner** is a generic interface which defines an object
* capable of interacting with a Contract on the network.
*
* The more operations supported, the more utility it is capable of.
*
* The most common ContractRunners are [Providers](Provider) which enable
* read-only access and [Signers](Signer) which enable write-access.
*/
export interface ContractRunner { export interface ContractRunner {
/**
* The provider used for necessary state querying operations.
*
* This can also point to the **ContractRunner** itself, in the
* case of an [[AbstractProvider]].
*/
provider: null | Provider; provider: null | Provider;
// Required to estimate gas; usually a Signer or Provider /**
* Required to estimate gas.
*/
estimateGas?: (tx: TransactionRequest) => Promise<bigint>; estimateGas?: (tx: TransactionRequest) => Promise<bigint>;
// Required for pure, view or static calls to contracts; usually a Signer or Provider /**
* Required for pure, view or static calls to contracts.
*/
call?: (tx: TransactionRequest) => Promise<string>; call?: (tx: TransactionRequest) => Promise<string>;
// Required to support ENS names; usually a Signer or Provider /**
* Required to support ENS names
*/
resolveName?: (name: string) => Promise<null | string>; resolveName?: (name: string) => Promise<null | string>;
// Required for mutating calls; usually a Signer /**
* Required for state mutating calls
*/
sendTransaction?: (tx: TransactionRequest) => Promise<TransactionResponse>; sendTransaction?: (tx: TransactionRequest) => Promise<TransactionResponse>;
} }

@ -1,5 +1,6 @@
/** /**
* About ENS Resolver * ENS is a service which allows easy-to-remember names to map to
* network addresses.
* *
* @_section: api/providers/ens-resolver:ENS Resolver [about-ens-rsolver] * @_section: api/providers/ens-resolver:ENS Resolver [about-ens-rsolver]
*/ */
@ -50,7 +51,14 @@ export type AvatarLinkageType = "name" | "avatar" | "!avatar" | "url" | "data" |
* An individual record for each step during avatar resolution. * An individual record for each step during avatar resolution.
*/ */
export interface AvatarLinkage { export interface AvatarLinkage {
/**
* The type of linkage.
*/
type: AvatarLinkageType; type: AvatarLinkageType;
/**
* The linkage value.
*/
value: string; value: string;
}; };
@ -63,7 +71,17 @@ export interface AvatarLinkage {
* each completed step during avatar resolution. * each completed step during avatar resolution.
*/ */
export interface AvatarResult { export interface AvatarResult {
/**
* How the [[url]] was arrived at, resolving the many steps required
* for an avatar URL.
*/
linkage: Array<AvatarLinkage>; linkage: Array<AvatarLinkage>;
/**
* The avatar URL or null if the avatar was not set, or there was
* an issue during validation (such as the address not owning the
* avatar or a metadata error).
*/
url: null | string; url: null | string;
}; };
@ -71,8 +89,14 @@ export interface AvatarResult {
* A provider plugin super-class for processing multicoin address types. * A provider plugin super-class for processing multicoin address types.
*/ */
export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin { export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin {
/**
* The name.
*/
readonly name!: string; readonly name!: string;
/**
* Creates a new **MulticoinProviderPluing** for %%name%%.
*/
constructor(name: string) { constructor(name: string) {
defineProperties<MulticoinProviderPlugin>(this, { name }); defineProperties<MulticoinProviderPlugin>(this, { name });
} }
@ -81,14 +105,23 @@ export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin
return this; return this;
} }
/**
* Returns ``true`` if %%coinType%% is supported by this plugin.
*/
supportsCoinType(coinType: number): boolean { supportsCoinType(coinType: number): boolean {
return false; return false;
} }
/**
* Resovles to the encoded %%address%% for %%coinType%%.
*/
async encodeAddress(coinType: number, address: string): Promise<string> { async encodeAddress(coinType: number, address: string): Promise<string> {
throw new Error("unsupported coin"); throw new Error("unsupported coin");
} }
/**
* Resovles to the decoded %%data%% for %%coinType%%.
*/
async decodeAddress(coinType: number, data: BytesLike): Promise<string> { async decodeAddress(coinType: number, data: BytesLike): Promise<string> {
throw new Error("unsupported coin"); throw new Error("unsupported coin");
} }
@ -97,9 +130,14 @@ export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin
const BasicMulticoinPluginId = "org.ethers.plugins.provider.BasicMulticoin"; const BasicMulticoinPluginId = "org.ethers.plugins.provider.BasicMulticoin";
/** /**
* A basic multicoin provider plugin. * A **BasicMulticoinProviderPlugin** provides service for common
* coin types, which do not require additional libraries to encode or
* decode.
*/ */
export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin { export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin {
/**
* Creates a new **BasicMulticoinProviderPlugin**.
*/
constructor() { constructor() {
super(BasicMulticoinPluginId); super(BasicMulticoinPluginId);
} }

@ -7,86 +7,78 @@
import type { Signature } from "../crypto/index.js"; import type { Signature } from "../crypto/index.js";
import type { AccessList } from "../transaction/index.js"; import type { AccessList } from "../transaction/index.js";
/*
export interface TransactionRequest {
type?: null | number;
to?: null | AddressLike;
from?: null | AddressLike;
nonce?: null | number;
gasLimit?: null | BigNumberish;
gasPrice?: null | BigNumberish;
maxPriorityFeePerGas?: null | BigNumberish;
maxFeePerGas?: null | BigNumberish;
data?: null | string;
value?: null | BigNumberish;
chainId?: null | BigNumberish;
accessList?: null | AccessListish;
customData?: any;
// Only meaningful when used for call
blockTag?: BlockTag;
enableCcipRead?: boolean;
// Todo?
//gasMultiplier?: number;
};
export interface PreparedTransactionRequest {
type?: number;
to?: AddressLike;
from?: AddressLike;
nonce?: number;
gasLimit?: bigint;
gasPrice?: bigint;
maxPriorityFeePerGas?: bigint;
maxFeePerGas?: bigint;
data?: string;
value?: bigint;
chainId?: bigint;
accessList?: AccessList;
customData?: any;
blockTag?: BlockTag;
enableCcipRead?: boolean;
}
*/
////////////////////// //////////////////////
// Block // Block
/**
* a **BlockParams** encodes the minimal required properties for a
* formatted block.
*/
export interface BlockParams { export interface BlockParams {
/**
* The block hash.
*/
hash?: null | string; hash?: null | string;
/**
* The block number.
*/
number: number; number: number;
/**
* The timestamp for this block, which is the number of seconds
* since epoch that this block was included.
*/
timestamp: number; timestamp: number;
/**
* The hash of the previous block in the blockchain. The genesis block
* has the parentHash of the [[ZeroHash]].
*/
parentHash: string; parentHash: string;
/**
* A random sequence provided during the mining process for
* proof-of-work networks.
*/
nonce: string; nonce: string;
/**
* For proof-of-work networks, the difficulty target is used to
* adjust the difficulty in mining to ensure a expected block rate.
*/
difficulty: bigint; difficulty: bigint;
/**
* The maximum amount of gas a block can consume.
*/
gasLimit: bigint; gasLimit: bigint;
/**
* The amount of gas a block consumed.
*/
gasUsed: bigint; gasUsed: bigint;
/**
* The miner (or author) of a block.
*/
miner: string; miner: string;
/**
* Additional data the miner choose to include.
*/
extraData: string; extraData: string;
/**
* The protocol-defined base fee per gas in an [[link-eip-1559]]
* block.
*/
baseFeePerGas: null | bigint; baseFeePerGas: null | bigint;
/**
* The list of transactions in the block.
*/
transactions: ReadonlyArray<string | TransactionResponseParams>; transactions: ReadonlyArray<string | TransactionResponseParams>;
}; };
@ -94,19 +86,57 @@ export interface BlockParams {
////////////////////// //////////////////////
// Log // Log
/**
* a **LogParams** encodes the minimal required properties for a
* formatted log.
*/
export interface LogParams { export interface LogParams {
/**
* The transaction hash for the transaxction the log occurred in.
*/
transactionHash: string; transactionHash: string;
/**
* The block hash of the block that included the transaction for this
* log.
*/
blockHash: string; blockHash: string;
/**
* The block number of the block that included the transaction for this
* log.
*/
blockNumber: number; blockNumber: number;
/**
* Whether this log was removed due to the transaction it was included
* in being removed dur to an orphaned block.
*/
removed: boolean; removed: boolean;
/**
* The address of the contract that emitted this log.
*/
address: string; address: string;
/**
* The data emitted with this log.
*/
data: string; data: string;
/**
* The topics emitted with this log.
*/
topics: ReadonlyArray<string>; topics: ReadonlyArray<string>;
/**
* The index of this log.
*/
index: number; index: number;
/**
* The transaction index of this log.
*/
transactionIndex: number; transactionIndex: number;
} }
@ -114,28 +144,99 @@ export interface LogParams {
////////////////////// //////////////////////
// Transaction Receipt // Transaction Receipt
/**
* a **TransactionReceiptParams** encodes the minimal required properties
* for a formatted transaction receipt.
*/
export interface TransactionReceiptParams { export interface TransactionReceiptParams {
/**
* The target of the transaction. If null, the transaction was trying
* to deploy a transaction with the ``data`` as the initi=code.
*/
to: null | string; to: null | string;
/**
* The sender of the transaction.
*/
from: string; from: string;
/**
* If the transaction was directly deploying a contract, the [[to]]
* will be null, the ``data`` will be initcode and if successful, this
* will be the address of the contract deployed.
*/
contractAddress: null | string; contractAddress: null | string;
/**
* The transaction hash.
*/
hash: string; hash: string;
/**
* The transaction index.
*/
index: number; index: number;
/**
* The block hash of the block that included this transaction.
*/
blockHash: string; blockHash: string;
/**
* The block number of the block that included this transaction.
*/
blockNumber: number; blockNumber: number;
/**
* The bloom filter for the logs emitted during execution of this
* transaction.
*/
logsBloom: string; logsBloom: string;
/**
* The logs emitted during the execution of this transaction.
*/
logs: ReadonlyArray<LogParams>; logs: ReadonlyArray<LogParams>;
/**
* The amount of gas consumed executing this transaciton.
*/
gasUsed: bigint; gasUsed: bigint;
/**
* The total amount of gas consumed during the entire block up to
* and including this transaction.
*/
cumulativeGasUsed: bigint; cumulativeGasUsed: bigint;
/**
* The actual gas price per gas charged for this transaction.
*/
gasPrice?: null | bigint; gasPrice?: null | bigint;
/**
* The actual gas price per gas charged for this transaction.
*/
effectiveGasPrice?: null | bigint; effectiveGasPrice?: null | bigint;
/**
* The [[link-eip-2718]] envelope type.
*/
type: number; type: number;
//byzantium: boolean; //byzantium: boolean;
/**
* The status of the transaction execution. If ``1`` then the
* the transaction returned success, if ``0`` then the transaction
* was reverted. For pre-byzantium blocks, this is usually null, but
* some nodes may have backfilled this data.
*/
status: null | number; status: null | number;
/**
* The root of this transaction in a pre-bazatium block. In
* post-byzantium blocks this is null.
*/
root: null | string; root: null | string;
} }
@ -158,33 +259,97 @@ export interface ByzantiumTransactionReceipt {
////////////////////// //////////////////////
// Transaction Response // Transaction Response
/**
* a **TransactionResponseParams** encodes the minimal required properties
* for a formatted transaction response.
*/
export interface TransactionResponseParams { export interface TransactionResponseParams {
/**
* The block number of the block that included this transaction.
*/
blockNumber: null | number; blockNumber: null | number;
/**
* The block hash of the block that included this transaction.
*/
blockHash: null | string; blockHash: null | string;
/**
* The transaction hash.
*/
hash: string; hash: string;
/**
* The transaction index.
*/
index: number; index: number;
/**
* The [[link-eip-2718]] transaction type.
*/
type: number; type: number;
/**
* The target of the transaction. If ``null``, the ``data`` is initcode
* and this transaction is a deployment transaction.
*/
to: null | string; to: null | string;
/**
* The sender of the transaction.
*/
from: string; from: string;
/**
* The nonce of the transaction, used for replay protection.
*/
nonce: number; nonce: number;
/**
* The maximum amount of gas this transaction is authorized to consume.
*/
gasLimit: bigint; gasLimit: bigint;
/**
* For legacy transactions, this is the gas price per gas to pay.
*/
gasPrice: bigint; gasPrice: bigint;
/**
* For [[link-eip-1559]] transactions, this is the maximum priority
* fee to allow a producer to claim.
*/
maxPriorityFeePerGas: null | bigint; maxPriorityFeePerGas: null | bigint;
/**
* For [[link-eip-1559]] transactions, this is the maximum fee that
* will be paid.
*/
maxFeePerGas: null | bigint; maxFeePerGas: null | bigint;
/**
* The transaction data.
*/
data: string; data: string;
/**
* The transaction value (in wei).
*/
value: bigint; value: bigint;
/**
* The chain ID this transaction is valid on.
*/
chainId: bigint; chainId: bigint;
/**
* The signature of the transaction.
*/
signature: Signature; signature: Signature;
/**
* The transaction access list.
*/
accessList: null | AccessList; accessList: null | AccessList;
}; };

@ -1,11 +1,16 @@
/** /**
* About providers. * A **Provider** provides a connection to the blockchain, whch can be
* used to query its current state, simulate execution and send transactions
* to update the state.
*
* It is one of the most fundamental components of interacting with a
* blockchain application, and there are many ways to connect, such as over
* HTTP, WebSockets or injected providers such as [MetaMask](link-metamask).
* *
* @_section: api/providers:Providers [about-providers] * @_section: api/providers:Providers [about-providers]
*/ */
export { export {
AbstractProvider, UnmanagedSubscriber AbstractProvider, UnmanagedSubscriber
} from "./abstract-provider.js"; } from "./abstract-provider.js";

@ -1,5 +1,6 @@
/** /**
* About networks * A **Network** encapsulates the various properties required to
* interact with a specific chain.
* *
* @_subsection: api/providers:Networks [networks] * @_subsection: api/providers:Networks [networks]
*/ */
@ -91,18 +92,28 @@ const Networks: Map<string | bigint, () => Network> = new Map();
// @TODO: Add a _ethersNetworkObj variable to better detect network ovjects // @TODO: Add a _ethersNetworkObj variable to better detect network ovjects
/**
* A **Network** provides access to a chain's properties and allows
* for plug-ins to extend functionality.
*/
export class Network { export class Network {
#name: string; #name: string;
#chainId: bigint; #chainId: bigint;
#plugins: Map<string, NetworkPlugin>; #plugins: Map<string, NetworkPlugin>;
/**
* Creates a new **Network** for %%name%% and %%chainId%%.
*/
constructor(name: string, chainId: BigNumberish) { constructor(name: string, chainId: BigNumberish) {
this.#name = name; this.#name = name;
this.#chainId = getBigInt(chainId); this.#chainId = getBigInt(chainId);
this.#plugins = new Map(); this.#plugins = new Map();
} }
/**
* Returns a JSON-compatible representation of a Network.
*/
toJSON(): any { toJSON(): any {
return { name: this.name, chainId: String(this.chainId) }; return { name: this.name, chainId: String(this.chainId) };
} }

@ -10,13 +10,28 @@ import type {
const EnsAddress = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"; const EnsAddress = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";
/**
* A **NetworkPlugin** provides additional functionality on a [[Network]].
*/
export class NetworkPlugin { export class NetworkPlugin {
/**
* The name of the plugin.
*
* It is recommended to use reverse-domain-notation, which permits
* unique names with a known authority as well as hierarchal entries.
*/
readonly name!: string; readonly name!: string;
/**
* Creates a new **NetworkPlugin**.
*/
constructor(name: string) { constructor(name: string) {
defineProperties<NetworkPlugin>(this, { name }); defineProperties<NetworkPlugin>(this, { name });
} }
/**
* Creates a copy of this plugin.
*/
clone(): NetworkPlugin { clone(): NetworkPlugin {
return new NetworkPlugin(this.name); return new NetworkPlugin(this.name);
} }
@ -26,28 +41,91 @@ export class NetworkPlugin {
// } // }
} }
// Networks can use this plugin to override calculations for the
// intrinsic gas cost of a transaction for networks that differ /**
// from the latest hardfork on Ethereum mainnet. * The gas cost parameters for a [[GasCostPlugin]].
*/
export type GasCostParameters = { export type GasCostParameters = {
/**
* The transactions base fee.
*/
txBase?: number; txBase?: number;
/**
* The fee for creating a new account.
*/
txCreate?: number; txCreate?: number;
/**
* The fee per zero-byte in the data.
*/
txDataZero?: number; txDataZero?: number;
/**
* The fee per non-zero-byte in the data.
*/
txDataNonzero?: number; txDataNonzero?: number;
/**
* The fee per storage key in the [[link-eip-2930]] access list.
*/
txAccessListStorageKey?: number; txAccessListStorageKey?: number;
/**
* The fee per address in the [[link-eip-2930]] access list.
*/
txAccessListAddress?: number; txAccessListAddress?: number;
}; };
/**
* A **GasCostPlugin** allows a network to provide alternative values when
* computing the intrinsic gas required for a transaction.
*/
export class GasCostPlugin extends NetworkPlugin implements GasCostParameters { export class GasCostPlugin extends NetworkPlugin implements GasCostParameters {
/**
* The block number to treat these values as valid from.
*
* This allows a hardfork to have updated values included as well as
* mulutiple hardforks to be supported.
*/
readonly effectiveBlock!: number; readonly effectiveBlock!: number;
/**
* The transactions base fee.
*/
readonly txBase!: number; readonly txBase!: number;
/**
* The fee for creating a new account.
*/
readonly txCreate!: number; readonly txCreate!: number;
/**
* The fee per zero-byte in the data.
*/
readonly txDataZero!: number; readonly txDataZero!: number;
/**
* The fee per non-zero-byte in the data.
*/
readonly txDataNonzero!: number; readonly txDataNonzero!: number;
/**
* The fee per storage key in the [[link-eip-2930]] access list.
*/
readonly txAccessListStorageKey!: number; readonly txAccessListStorageKey!: number;
/**
* The fee per address in the [[link-eip-2930]] access list.
*/
readonly txAccessListAddress!: number; readonly txAccessListAddress!: number;
/**
* Creates a new GasCostPlugin from %%effectiveBlock%% until the
* latest block or another GasCostPlugin supercedes that block number,
* with the associated %%costs%%.
*/
constructor(effectiveBlock?: number, costs?: GasCostParameters) { constructor(effectiveBlock?: number, costs?: GasCostParameters) {
if (effectiveBlock == null) { effectiveBlock = 0; } if (effectiveBlock == null) { effectiveBlock = 0; }
super(`org.ethers.network.plugins.GasCost#${ (effectiveBlock || 0) }`); super(`org.ethers.network.plugins.GasCost#${ (effectiveBlock || 0) }`);
@ -75,16 +153,32 @@ export class GasCostPlugin extends NetworkPlugin implements GasCostParameters {
} }
} }
// Networks shoudl use this plugin to specify the contract address /**
// and network necessary to resolve ENS names. * An **EnsPlugin** allows a [[Network]] to specify the ENS Registry
* Contract address and the target network to use when using that
* contract.
*
* Various testnets have their own instance of the contract to use, but
* in general, the mainnet instance supports multi-chain addresses and
* should be used.
*/
export class EnsPlugin extends NetworkPlugin { export class EnsPlugin extends NetworkPlugin {
// The ENS contract address /**
* The ENS Registrty Contract address.
*/
readonly address!: string; readonly address!: string;
// The network ID that the ENS contract lives on /**
* The chain ID that the ENS contract lives on.
*/
readonly targetNetwork!: number; readonly targetNetwork!: number;
/**
* Creates a new **EnsPlugin** connected to %%address%% on the
* %%targetNetwork%%. The default ENS address and mainnet is used
* if unspecified.
*/
constructor(address?: null | string, targetNetwork?: null | number) { constructor(address?: null | string, targetNetwork?: null | number) {
super("org.ethers.plugins.network.Ens"); super("org.ethers.plugins.network.Ens");
defineProperties<EnsPlugin>(this, { defineProperties<EnsPlugin>(this, {
@ -98,18 +192,34 @@ export class EnsPlugin extends NetworkPlugin {
} }
} }
/**
* A **FeeDataNetworkPlugin** allows a network to provide and alternate
* means to specify its fee data.
*
* For example, a network which does not support [[link-eip-1559]] may
* choose to use a Gas Station site to approximate the gas price.
*/
export class FeeDataNetworkPlugin extends NetworkPlugin { export class FeeDataNetworkPlugin extends NetworkPlugin {
readonly #feeDataFunc: (provider: Provider) => Promise<FeeData>; readonly #feeDataFunc: (provider: Provider) => Promise<FeeData>;
/**
* The fee data function provided to the constructor.
*/
get feeDataFunc(): (provider: Provider) => Promise<FeeData> { get feeDataFunc(): (provider: Provider) => Promise<FeeData> {
return this.#feeDataFunc; return this.#feeDataFunc;
} }
/**
* Creates a new **FeeDataNetworkPlugin**.
*/
constructor(feeDataFunc: (provider: Provider) => Promise<FeeData>) { constructor(feeDataFunc: (provider: Provider) => Promise<FeeData>) {
super("org.ethers.plugins.network.FeeData"); super("org.ethers.plugins.network.FeeData");
this.#feeDataFunc = feeDataFunc; this.#feeDataFunc = feeDataFunc;
} }
/**
* Resolves to the fee data.
*/
async getFeeData(provider: Provider): Promise<FeeData> { async getFeeData(provider: Provider): Promise<FeeData> {
return await this.#feeDataFunc(provider); return await this.#feeDataFunc(provider);
} }

@ -8,11 +8,22 @@ import type {
} from "./provider-jsonrpc.js"; } from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js"; import type { Networkish } from "./network.js";
/**
* The interface to an [[link-eip-1193]] provider, which is a standard
* used by most injected providers, which the [[BrowserProvider]] accepts
* and exposes the API of.
*/
export interface Eip1193Provider { export interface Eip1193Provider {
/**
* See [[link-eip-1193]] for details on this method.
*/
request(request: { method: string, params?: Array<any> | Record<string, any> }): Promise<any>; request(request: { method: string, params?: Array<any> | Record<string, any> }): Promise<any>;
}; };
/**
* The possible additional events dispatched when using the ``"debug"``
* event on a [[BrowserProvider]].
*/
export type DebugEventBrowserProvider = { export type DebugEventBrowserProvider = {
action: "sendEip1193Payload", action: "sendEip1193Payload",
payload: { method: string, params: Array<any> } payload: { method: string, params: Array<any> }
@ -25,10 +36,18 @@ export type DebugEventBrowserProvider = {
}; };
/**
* A **BrowserProvider** is intended to wrap an injected provider which
* adheres to the [[link-eip-1193]] standard, which most (if not all)
* currently do.
*/
export class BrowserProvider extends JsonRpcApiPollingProvider { export class BrowserProvider extends JsonRpcApiPollingProvider {
#request: (method: string, params: Array<any> | Record<string, any>) => Promise<any>; #request: (method: string, params: Array<any> | Record<string, any>) => Promise<any>;
/**
* Connnect to the %%ethereum%% provider, optionally forcing the
* %%network%%.
*/
constructor(ethereum: Eip1193Provider, network?: Networkish) { constructor(ethereum: Eip1193Provider, network?: Networkish) {
super(network, { batchMaxCount: 1 }); super(network, { batchMaxCount: 1 });
@ -88,6 +107,9 @@ export class BrowserProvider extends JsonRpcApiPollingProvider {
return super.getRpcError(payload, error); return super.getRpcError(payload, error);
} }
/**
* Resolves to ``true`` if the provider manages the %%address%%.
*/
async hasSigner(address: number | string): Promise<boolean> { async hasSigner(address: number | string): Promise<boolean> {
if (address == null) { address = 0; } if (address == null) { address = 0; }

@ -1,5 +1,6 @@
/** /**
* Explain all the nitty-gritty about the **FallbackProvider**. * A **FallbackProvider** providers resiliance, security and performatnce
* in a way that is customizable and configurable.
* *
* @_section: api/providers/fallback-provider:Fallback Provider [about-fallback-provider] * @_section: api/providers/fallback-provider:Fallback Provider [about-fallback-provider]
*/ */
@ -45,17 +46,27 @@ function stringify(value: any): string {
*/ */
export interface FallbackProviderConfig { export interface FallbackProviderConfig {
// The provider /**
* The provider.
*/
provider: AbstractProvider; provider: AbstractProvider;
// How long to wait for a response before getting impatient /**
// and ispatching the next provider * The amount of time to wait before kicking off the next provider.
*
* Any providers that have not responded can still respond and be
* counted, but this ensures new providers start.
*/
stallTimeout?: number; stallTimeout?: number;
// Lower values are dispatched first /**
* The priority. Lower priority providers are dispatched first.
*/
priority?: number; priority?: number;
// How much this provider contributes to the quorum /**
* The amount of weight a provider is given against the quorum.
*/
weight?: number; weight?: number;
}; };
@ -68,28 +79,44 @@ const defaultConfig = { stallTimeout: 400, priority: 1, weight: 1 };
*/ */
export interface FallbackProviderState extends Required<FallbackProviderConfig> { export interface FallbackProviderState extends Required<FallbackProviderConfig> {
// The most recent blockNumber this provider has reported (-2 if none) /**
* The most recent blockNumber this provider has reported (-2 if none).
*/
blockNumber: number; blockNumber: number;
// The number of total requests ever sent to this provider /**
* The number of total requests ever sent to this provider.
*/
requests: number; requests: number;
// The number of responses that errored /**
* The number of responses that errored.
*/
errorResponses: number; errorResponses: number;
// The number of responses that occured after the result resolved /**
* The number of responses that occured after the result resolved.
*/
lateResponses: number; lateResponses: number;
// How many times syncing was required to catch up the expected block /**
* How many times syncing was required to catch up the expected block.
*/
outOfSync: number; outOfSync: number;
// The number of requests which reported unsupported operation /**
* The number of requests which reported unsupported operation.
*/
unsupportedEvents: number; unsupportedEvents: number;
// A rolling average (5% current duration) for response time /**
* A rolling average (5% current duration) for response time.
*/
rollingDuration: number; rollingDuration: number;
// The ratio of quorum-agreed results to total /**
* The ratio of quorum-agreed results to total.
*/
score: number; score: number;
} }
@ -317,13 +344,28 @@ function getFuzzyMode(quorum: number, results: Array<TallyResult>): undefined |
} }
/** /**
* A Fallback Provider. * A **FallbackProvider** manages several [[Providers]] providing
* resiliance by switching between slow or misbehaving nodes, security
* by requiring multiple backends to aggree and performance by allowing
* faster backends to respond earlier.
* *
*/ */
export class FallbackProvider extends AbstractProvider { export class FallbackProvider extends AbstractProvider {
/**
* The number of backends that must agree on a value before it is
* accpeted.
*/
readonly quorum: number; readonly quorum: number;
/**
* @_ignore:
*/
readonly eventQuorum: number; readonly eventQuorum: number;
/**
* @_ignore:
*/
readonly eventWorkers: number; readonly eventWorkers: number;
readonly #configs: Array<Config>; readonly #configs: Array<Config>;
@ -331,6 +373,13 @@ export class FallbackProvider extends AbstractProvider {
#height: number; #height: number;
#initialSyncPromise: null | Promise<void>; #initialSyncPromise: null | Promise<void>;
/**
* Creates a new **FallbackProvider** with %%providers%% connected to
* %%network%%.
*
* If a [[Provider]] is included in %%providers%%, defaults are used
* for the configuration.
*/
constructor(providers: Array<AbstractProvider | FallbackProviderConfig>, network?: Networkish) { constructor(providers: Array<AbstractProvider | FallbackProviderConfig>, network?: Networkish) {
super(network); super(network);
this.#configs = providers.map((p) => { this.#configs = providers.map((p) => {
@ -371,6 +420,9 @@ export class FallbackProvider extends AbstractProvider {
// throw new Error("@TODO"); // throw new Error("@TODO");
//} //}
/**
* Transforms a %%req%% into the correct method call on %%provider%%.
*/
async _translatePerform(provider: AbstractProvider, req: PerformActionRequest): Promise<any> { async _translatePerform(provider: AbstractProvider, req: PerformActionRequest): Promise<any> {
switch (req.method) { switch (req.method) {
case "broadcastTransaction": case "broadcastTransaction":

@ -22,8 +22,17 @@ function splitBuffer(data: Buffer): { messages: Array<string>, remaining: Buffer
return { messages, remaining: data.subarray(lastStart) }; return { messages, remaining: data.subarray(lastStart) };
} }
/**
* An **IpcSocketProvider** connects over an IPC socket on the host
* which provides fast access to the node, but requires the node and
* the script run on the same machine.
*/
export class IpcSocketProvider extends SocketProvider { export class IpcSocketProvider extends SocketProvider {
#socket: Socket; #socket: Socket;
/**
* The connected socket.
*/
get socket(): Socket { return this.#socket; } get socket(): Socket { return this.#socket; }
constructor(path: string, network?: Networkish) { constructor(path: string, network?: Networkish) {

@ -1,5 +1,11 @@
/** /**
* About JSON-RPC... * One of the most common ways to interact with the blockchain is
* by a node running a JSON-RPC interface which can be connected to,
* based on the transport, using:
*
* - HTTP or HTTPS - [[JsonRpcProvider]]
* - WebSocket - [[WebSocketProvider]]
* - IPC - [[IpcSocketProvider]]
* *
* @_section: api/providers/jsonrpc:JSON-RPC Provider [about-jsonrpcProvider] * @_section: api/providers/jsonrpc:JSON-RPC Provider [about-jsonrpcProvider]
*/ */
@ -81,9 +87,24 @@ function isPollable(value: any): value is Pollable {
* A JSON-RPC payload, which are sent to a JSON-RPC server. * A JSON-RPC payload, which are sent to a JSON-RPC server.
*/ */
export type JsonRpcPayload = { export type JsonRpcPayload = {
/**
* The JSON-RPC request ID.
*/
id: number; id: number;
/**
* The JSON-RPC request method.
*/
method: string; method: string;
/**
* The JSON-RPC request parameters.
*/
params: Array<any> | Record<string, any>; params: Array<any> | Record<string, any>;
/**
* A required constant in the JSON-RPC specification.
*/
jsonrpc: "2.0"; jsonrpc: "2.0";
}; };
@ -91,7 +112,14 @@ export type JsonRpcPayload = {
* A JSON-RPC result, which are returned on success from a JSON-RPC server. * A JSON-RPC result, which are returned on success from a JSON-RPC server.
*/ */
export type JsonRpcResult = { export type JsonRpcResult = {
/**
* The response ID to match it to the relevant request.
*/
id: number; id: number;
/**
* The response result.
*/
result: any; result: any;
}; };
@ -99,7 +127,14 @@ export type JsonRpcResult = {
* A JSON-RPC error, which are returned on failure from a JSON-RPC server. * A JSON-RPC error, which are returned on failure from a JSON-RPC server.
*/ */
export type JsonRpcError = { export type JsonRpcError = {
/**
* The response ID to match it to the relevant request.
*/
id: number; id: number;
/**
* The response error.
*/
error: { error: {
code: number; code: number;
message?: string; message?: string;
@ -168,22 +203,72 @@ const defaultOptions = {
batchMaxCount: 100 // 100 requests batchMaxCount: 100 // 100 requests
} }
/**
* A **JsonRpcTransactionRequest** is formatted as needed by the JSON-RPC
* Ethereum API specification.
*/
export interface JsonRpcTransactionRequest { export interface JsonRpcTransactionRequest {
/**
* The sender address to use when signing.
*/
from?: string; from?: string;
/**
* The target address.
*/
to?: string; to?: string;
/**
* The transaction data.
*/
data?: string; data?: string;
/**
* The chain ID the transaction is valid on.
*/
chainId?: string; chainId?: string;
/**
* The [[link-eip-2718]] transaction type.
*/
type?: string; type?: string;
/**
* The maximum amount of gas to allow a transaction to consume.
*
* In most other places in ethers, this is called ``gasLimit`` which
* differs from the JSON-RPC Ethereum API specification.
*/
gas?: string; gas?: string;
/**
* The gas price per wei for transactions prior to [[link-eip-1559]].
*/
gasPrice?: string; gasPrice?: string;
/**
* The maximum fee per gas for [[link-eip-1559]] transactions.
*/
maxFeePerGas?: string; maxFeePerGas?: string;
/**
* The maximum priority fee per gas for [[link-eip-1559]] transactions.
*/
maxPriorityFeePerGas?: string; maxPriorityFeePerGas?: string;
/**
* The nonce for the transaction.
*/
nonce?: string; nonce?: string;
/**
* The transaction value (in wei).
*/
value?: string; value?: string;
/**
* The transaction access list.
*/
accessList?: Array<{ address: string, storageKeys: Array<string> }>; accessList?: Array<{ address: string, storageKeys: Array<string> }>;
} }

@ -27,10 +27,18 @@ type JsonRpcSubscription = {
} }
}; };
/**
* A **SocketSubscriber** uses a socket transport to handle events and
* should use [[_emit]] to manage the events.
*/
export class SocketSubscriber implements Subscriber { export class SocketSubscriber implements Subscriber {
#provider: SocketProvider; #provider: SocketProvider;
#filter: string; #filter: string;
/**
* The filter.
*/
get filter(): Array<any> { return JSON.parse(this.#filter); } get filter(): Array<any> { return JSON.parse(this.#filter); }
#filterId: null | Promise<string |number>; #filterId: null | Promise<string |number>;
@ -38,6 +46,10 @@ export class SocketSubscriber implements Subscriber {
#emitPromise: null | Promise<void>; #emitPromise: null | Promise<void>;
/**
* Creates a new **SocketSubscriber** attached to %%provider%% listening
* to %%filter%%.
*/
constructor(provider: SocketProvider, filter: Array<any>) { constructor(provider: SocketProvider, filter: Array<any>) {
this.#provider = provider; this.#provider = provider;
this.#filter = JSON.stringify(filter); this.#filter = JSON.stringify(filter);
@ -72,6 +84,9 @@ export class SocketSubscriber implements Subscriber {
this.#paused = null; this.#paused = null;
} }
/**
* @_ignore:
*/
_handleMessage(message: any): void { _handleMessage(message: any): void {
if (this.#filterId == null) { return; } if (this.#filterId == null) { return; }
if (this.#paused === null) { if (this.#paused === null) {
@ -91,12 +106,23 @@ export class SocketSubscriber implements Subscriber {
} }
} }
/**
* Sub-classes **must** override this to emit the events on the
* provider.
*/
async _emit(provider: SocketProvider, message: any): Promise<void> { async _emit(provider: SocketProvider, message: any): Promise<void> {
throw new Error("sub-classes must implemente this; _emit"); throw new Error("sub-classes must implemente this; _emit");
} }
} }
/**
* A **SocketBlockSubscriber** listens for ``newHeads`` events and emits
* ``"block"`` events.
*/
export class SocketBlockSubscriber extends SocketSubscriber { export class SocketBlockSubscriber extends SocketSubscriber {
/**
* @_ignore:
*/
constructor(provider: SocketProvider) { constructor(provider: SocketProvider) {
super(provider, [ "newHeads" ]); super(provider, [ "newHeads" ]);
} }
@ -106,7 +132,15 @@ export class SocketBlockSubscriber extends SocketSubscriber {
} }
} }
/**
* A **SocketPendingSubscriber** listens for pending transacitons and emits
* ``"pending"`` events.
*/
export class SocketPendingSubscriber extends SocketSubscriber { export class SocketPendingSubscriber extends SocketSubscriber {
/**
* @_ignore:
*/
constructor(provider: SocketProvider) { constructor(provider: SocketProvider) {
super(provider, [ "newPendingTransactions" ]); super(provider, [ "newPendingTransactions" ]);
} }
@ -116,10 +150,20 @@ export class SocketPendingSubscriber extends SocketSubscriber {
} }
} }
/**
* A **SocketEventSubscriber** listens for event logs.
*/
export class SocketEventSubscriber extends SocketSubscriber { export class SocketEventSubscriber extends SocketSubscriber {
#logFilter: string; #logFilter: string;
/**
* The filter.
*/
get logFilter(): EventFilter { return JSON.parse(this.#logFilter); } get logFilter(): EventFilter { return JSON.parse(this.#logFilter); }
/**
* @_ignore:
*/
constructor(provider: SocketProvider, filter: EventFilter) { constructor(provider: SocketProvider, filter: EventFilter) {
super(provider, [ "logs", filter ]); super(provider, [ "logs", filter ]);
this.#logFilter = JSON.stringify(filter); this.#logFilter = JSON.stringify(filter);
@ -131,8 +175,9 @@ export class SocketEventSubscriber extends SocketSubscriber {
} }
/** /**
* SocketProvider... * A **SocketProvider** is backed by a long-lived connection over a
* * socket, which can subscribe and receive real-time messages over
* its communication channel.
*/ */
export class SocketProvider extends JsonRpcApiProvider { export class SocketProvider extends JsonRpcApiProvider {
#callbacks: Map<number, { payload: JsonRpcPayload, resolve: (r: any) => void, reject: (e: Error) => void }>; #callbacks: Map<number, { payload: JsonRpcPayload, resolve: (r: any) => void, reject: (e: Error) => void }>;
@ -144,6 +189,11 @@ export class SocketProvider extends JsonRpcApiProvider {
// registering, queue them // registering, queue them
#pending: Map<number | string, Array<any>>; #pending: Map<number | string, Array<any>>;
/**
* Creates a new **SocketProvider** connected to %%network%%.
*
* If unspecified, the network will be discovered.
*/
constructor(network?: Networkish) { constructor(network?: Networkish) {
super(network, { batchMaxCount: 1 }); super(network, { batchMaxCount: 1 });
this.#callbacks = new Map(); this.#callbacks = new Map();
@ -181,6 +231,10 @@ export class SocketProvider extends JsonRpcApiProvider {
return super._getSubscriber(sub); return super._getSubscriber(sub);
} }
/**
* Register a new subscriber. This is used internalled by Subscribers
* and generally is unecessary unless extending capabilities.
*/
_register(filterId: number | string, subscriber: SocketSubscriber): void { _register(filterId: number | string, subscriber: SocketSubscriber): void {
this.#subs.set(filterId, subscriber); this.#subs.set(filterId, subscriber);
const pending = this.#pending.get(filterId); const pending = this.#pending.get(filterId);
@ -227,7 +281,10 @@ export class SocketProvider extends JsonRpcApiProvider {
} }
*/ */
// Sub-classes must call this for each message /**
* Sub-classes **must** call this with messages received over their
* transport to be processed and dispatched.
*/
async _processMessage(message: string): Promise<void> { async _processMessage(message: string): Promise<void> {
const result = <JsonRpcResult | JsonRpcError | JsonRpcSubscription>(JSON.parse(message)); const result = <JsonRpcResult | JsonRpcError | JsonRpcSubscription>(JSON.parse(message));
@ -267,6 +324,10 @@ export class SocketProvider extends JsonRpcApiProvider {
} }
} }
/**
* Sub-classes **must** override this to send %%message%% over their
* transport.
*/
async _write(message: string): Promise<void> { async _write(message: string): Promise<void> {
throw new Error("sub-classes must override this"); throw new Error("sub-classes must override this");
} }

@ -6,6 +6,9 @@ import { SocketProvider } from "./provider-socket.js";
import type { Networkish } from "./network.js"; import type { Networkish } from "./network.js";
/**
* A generic interface to a Websocket-like object.
*/
export interface WebSocketLike { export interface WebSocketLike {
onopen: null | ((...args: Array<any>) => any); onopen: null | ((...args: Array<any>) => any);
onmessage: null | ((...args: Array<any>) => any); onmessage: null | ((...args: Array<any>) => any);
@ -17,8 +20,22 @@ export interface WebSocketLike {
close(code?: number, reason?: string): void; close(code?: number, reason?: string): void;
} }
/**
* A function which can be used to re-create a WebSocket connection
* on disconnect.
*/
export type WebSocketCreator = () => WebSocketLike; export type WebSocketCreator = () => WebSocketLike;
/**
* A JSON-RPC provider which is backed by a WebSocket.
*
* WebSockets are often preferred because they retain a live connection
* to a server, which permits more instant access to events.
*
* However, this incurs higher server infrasturture costs, so additional
* resources may be required to host your own WebSocket nodes and many
* third-party services charge additional fees for WebSocket endpoints.
*/
export class WebSocketProvider extends SocketProvider { export class WebSocketProvider extends SocketProvider {
#connect: null | WebSocketCreator; #connect: null | WebSocketCreator;

@ -16,6 +16,19 @@ import type { Network } from "./network.js";
const BN_0 = BigInt(0); const BN_0 = BigInt(0);
/**
* A **BlockTag** specifies a specific block.
*
* **numeric value** - specifies the block height, where
* the genesis block is block 0; many operations accept a negative
* value which indicates the block number should be deducted from
* the most recent block. A numeric value may be a ``number``, ``bigint``,
* or a decimal of hex string.
*
* **blockhash** - specifies a specific block by its blockhash; this allows
* potentially orphaned blocks to be specifed, without ambiguity, but many
* backends do not support this for some operations.
*/
export type BlockTag = BigNumberish | string; export type BlockTag = BigNumberish | string;
import { import {
@ -101,62 +114,215 @@ export class FeeData {
} }
/**
* A **TransactionRequest** is a transactions with potentially various
* properties not defined, or with less strict types for its values.
*
* This is used to pass to various operations, which will internally
* coerce any types and populate any necessary values.
*/
export interface TransactionRequest { export interface TransactionRequest {
/**
* The transaction type.
*/
type?: null | number; type?: null | number;
/**
* The target of the transaction.
*/
to?: null | AddressLike; to?: null | AddressLike;
/**
* The sender of the transaction.
*/
from?: null | AddressLike; from?: null | AddressLike;
/**
* The nonce of the transaction, used to prevent replay attacks.
*/
nonce?: null | number; nonce?: null | number;
/**
* The maximum amount of gas to allow this transaction to consime.
*/
gasLimit?: null | BigNumberish; gasLimit?: null | BigNumberish;
/**
* The gas price to use for legacy transactions or transactions on
* legacy networks.
*
* Most of the time the ``max*FeePerGas`` is preferred.
*/
gasPrice?: null | BigNumberish; gasPrice?: null | BigNumberish;
/**
* The [[link-eip-1559]] maximum priority fee to pay per gas.
*/
maxPriorityFeePerGas?: null | BigNumberish; maxPriorityFeePerGas?: null | BigNumberish;
/**
* The [[link-eip-1559]] maximum total fee to pay per gas. The actual
* value used is protocol enforced to be the block's base fee.
*/
maxFeePerGas?: null | BigNumberish; maxFeePerGas?: null | BigNumberish;
/**
* The transaction data.
*/
data?: null | string; data?: null | string;
/**
* The transaction value (in wei).
*/
value?: null | BigNumberish; value?: null | BigNumberish;
/**
* The chain ID for the network this transaction is valid on.
*/
chainId?: null | BigNumberish; chainId?: null | BigNumberish;
/**
* The [[link-eip-2930]] access list. Storage slots included in the access
* list are //warmed// by pre-loading them, so their initial cost to
* fetch is guaranteed, but then each additional access is cheaper.
*/
accessList?: null | AccessListish; accessList?: null | AccessListish;
/**
* A custom object, which can be passed along for network-specific
* values.
*/
customData?: any; customData?: any;
// Only meaningful when used for call // Only meaningful when used for call
/**
* When using ``call`` or ``estimateGas``, this allows a specific
* block to be queried. Many backends do not support this and when
* unsupported errors are silently squelched and ``"latest"`` is used.
*/
blockTag?: BlockTag; blockTag?: BlockTag;
/**
* When using ``call``, this enables CCIP-read, which permits the
* provider to be redirected to web-based content during execution,
* which is then further validated by the contract.
*
* There are potential security implications allowing CCIP-read, as
* it could be used to expose the IP address or user activity during
* the fetch to unexpected parties.
*/
enableCcipRead?: boolean; enableCcipRead?: boolean;
// Todo? // Todo?
//gasMultiplier?: number; //gasMultiplier?: number;
}; };
/**
* A **PreparedTransactionRequest** is identical to a [[TransactionRequest]]
* except all the property types are strictly enforced.
*/
export interface PreparedTransactionRequest { export interface PreparedTransactionRequest {
/**
* The transaction type.
*/
type?: number; type?: number;
/**
* The target of the transaction.
*/
to?: AddressLike; to?: AddressLike;
/**
* The sender of the transaction.
*/
from?: AddressLike; from?: AddressLike;
/**
* The nonce of the transaction, used to prevent replay attacks.
*/
nonce?: number; nonce?: number;
/**
* The maximum amount of gas to allow this transaction to consime.
*/
gasLimit?: bigint; gasLimit?: bigint;
/**
* The gas price to use for legacy transactions or transactions on
* legacy networks.
*
* Most of the time the ``max*FeePerGas`` is preferred.
*/
gasPrice?: bigint; gasPrice?: bigint;
/**
* The [[link-eip-1559]] maximum priority fee to pay per gas.
*/
maxPriorityFeePerGas?: bigint; maxPriorityFeePerGas?: bigint;
/**
* The [[link-eip-1559]] maximum total fee to pay per gas. The actual
* value used is protocol enforced to be the block's base fee.
*/
maxFeePerGas?: bigint; maxFeePerGas?: bigint;
/**
* The transaction data.
*/
data?: string; data?: string;
/**
* The transaction value (in wei).
*/
value?: bigint; value?: bigint;
/**
* The chain ID for the network this transaction is valid on.
*/
chainId?: bigint; chainId?: bigint;
/**
* The [[link-eip-2930]] access list. Storage slots included in the access
* list are //warmed// by pre-loading them, so their initial cost to
* fetch is guaranteed, but then each additional access is cheaper.
*/
accessList?: AccessList; accessList?: AccessList;
/**
* A custom object, which can be passed along for network-specific
* values.
*/
customData?: any; customData?: any;
/**
* When using ``call`` or ``estimateGas``, this allows a specific
* block to be queried. Many backends do not support this and when
* unsupported errors are silently squelched and ``"latest"`` is used.
*/
blockTag?: BlockTag; blockTag?: BlockTag;
/**
* When using ``call``, this enables CCIP-read, which permits the
* provider to be redirected to web-based content during execution,
* which is then further validated by the contract.
*
* There are potential security implications allowing CCIP-read, as
* it could be used to expose the IP address or user activity during
* the fetch to unexpected parties.
*/
enableCcipRead?: boolean; enableCcipRead?: boolean;
} }
/**
* Returns a copy of %%req%% with all properties coerced to their strict
* types.
*/
export function copyRequest(req: TransactionRequest): PreparedTransactionRequest { export function copyRequest(req: TransactionRequest): PreparedTransactionRequest {
const result: any = { }; const result: any = { };
@ -206,10 +372,30 @@ export function copyRequest(req: TransactionRequest): PreparedTransactionRequest
* Before a block is included, it is a //pending// block. * Before a block is included, it is a //pending// block.
*/ */
export interface MinedBlock extends Block { export interface MinedBlock extends Block {
/**
* The block number also known as the block height.
*/
readonly number: number; readonly number: number;
/**
* The block hash.
*/
readonly hash: string; readonly hash: string;
/**
* The block timestamp, in seconds from epoch.
*/
readonly timestamp: number; readonly timestamp: number;
/**
* The block date, created from the [[timestamp]].
*/
readonly date: Date; readonly date: Date;
/**
* The miner of the block, also known as the ``author`` or
* block ``producer``.
*/
readonly miner: string; readonly miner: string;
} }
@ -218,6 +404,7 @@ export interface MinedBlock extends Block {
* Ethereum. * Ethereum.
*/ */
export class Block implements BlockParams, Iterable<string> { export class Block implements BlockParams, Iterable<string> {
/** /**
* The provider connected to the block used to fetch additional details * The provider connected to the block used to fetch additional details
* if necessary. * if necessary.
@ -232,6 +419,9 @@ export class Block implements BlockParams, Iterable<string> {
/** /**
* The block hash. * The block hash.
*
* This hash includes all properties, so can be safely used to identify
* an exact set of block properties.
*/ */
readonly hash!: null | string; readonly hash!: null | string;
@ -348,7 +538,7 @@ export class Block implements BlockParams, Iterable<string> {
/** /**
* Returns the complete transactions for blocks which * Returns the complete transactions for blocks which
* prefetched them, by passing ``true`` to %%prefetchTxs%% * prefetched them, by passing ``true`` to %%prefetchTxs%%
* into [[provider_getBlock]]. * into [[Provider-getBlock]].
*/ */
get prefetchedTransactions(): Array<TransactionResponse> { get prefetchedTransactions(): Array<TransactionResponse> {
const txs = this.#transactions.slice(); const txs = this.#transactions.slice();
@ -445,6 +635,12 @@ export class Block implements BlockParams, Iterable<string> {
} }
} }
/**
* If a **Block** was fetched with a request to include the transactions
* this will allow synchronous access to those transactions.
*
* If the transactions were not prefetched, this will throw.
*/
getPrefetchedTransaction(indexOrHash: number | string): TransactionResponse { getPrefetchedTransaction(indexOrHash: number | string): TransactionResponse {
const txs = this.prefetchedTransactions; const txs = this.prefetchedTransactions;
if (typeof(indexOrHash) === "number") { if (typeof(indexOrHash) === "number") {
@ -460,20 +656,21 @@ export class Block implements BlockParams, Iterable<string> {
} }
/** /**
* Has this block been mined. * Returns true if this block been mined. This provides a type guard
* * for all properties on a [[MinedBlock]].
* If true, the block has been typed-gaurded that all mined
* properties are non-null.
*/ */
isMined(): this is MinedBlock { return !!this.hash; } isMined(): this is MinedBlock { return !!this.hash; }
/** /**
* * Returns true if this block is an [[link-eip-2930]] block.
*/ */
isLondon(): this is (Block & { baseFeePerGas: bigint }) { isLondon(): this is (Block & { baseFeePerGas: bigint }) {
return !!this.baseFeePerGas; return !!this.baseFeePerGas;
} }
/**
* @_ignore:
*/
orphanedEvent(): OrphanFilter { orphanedEvent(): OrphanFilter {
if (!this.isMined()) { throw new Error(""); } if (!this.isMined()) { throw new Error(""); }
return createOrphanedBlockFilter(this); return createOrphanedBlockFilter(this);
@ -483,24 +680,80 @@ export class Block implements BlockParams, Iterable<string> {
////////////////////// //////////////////////
// Log // Log
/**
* A **Log** in Ethereum represents an event that has been included in a
* transaction using the ``LOG*`` opcodes, which are most commonly used by
* Solidity's emit for announcing events.
*/
export class Log implements LogParams { export class Log implements LogParams {
/**
* The provider connected to the log used to fetch additional details
* if necessary.
*/
readonly provider: Provider; readonly provider: Provider;
/**
* The transaction hash of the transaction this log occurred in. Use the
* [[Log-getTransaction]] to get the [[TransactionResponse]].
*/
readonly transactionHash!: string; readonly transactionHash!: string;
/**
* The block hash of the block this log occurred in. Use the
* [[Log-getBlock]] to get the [[Block]].
*/
readonly blockHash!: string; readonly blockHash!: string;
/**
* The block number of the block this log occurred in. It is preferred
* to use the [[Block-hash]] when fetching the related [[Block]],
* since in the case of an orphaned block, the block at that height may
* have changed.
*/
readonly blockNumber!: number; readonly blockNumber!: number;
/**
* If the **Log** represents a block that was removed due to an orphaned
* block, this will be true.
*
* This can only happen within an orphan event listener.
*/
readonly removed!: boolean; readonly removed!: boolean;
/**
* The address of the contract that emitted this log.
*/
readonly address!: string; readonly address!: string;
/**
* The data included in this log when it was emitted.
*/
readonly data!: string; readonly data!: string;
/**
* The indexed topics included in this log when it was emitted.
*
* All topics are included in the bloom filters, so they can be
* efficiently filtered using the [[Provider-getLogs]] method.
*/
readonly topics!: ReadonlyArray<string>; readonly topics!: ReadonlyArray<string>;
/**
* The index within the block this log occurred at. This is generally
* not useful to developers, but can be used with the various roots
* to proof inclusion within a block.
*/
readonly index!: number; readonly index!: number;
/**
* The index within the transaction of this log.
*/
readonly transactionIndex!: number; readonly transactionIndex!: number;
/**
* @_ignore:
*/
constructor(log: LogParams, provider: Provider) { constructor(log: LogParams, provider: Provider) {
this.provider = provider; this.provider = provider;
@ -522,6 +775,9 @@ export class Log implements LogParams {
}); });
} }
/**
* Returns a JSON-compatible object.
*/
toJSON(): any { toJSON(): any {
const { const {
address, blockHash, blockNumber, data, index, address, blockHash, blockNumber, data, index,
@ -535,24 +791,37 @@ export class Log implements LogParams {
}; };
} }
/**
* Returns the block that this log occurred in.
*/
async getBlock(): Promise<Block> { async getBlock(): Promise<Block> {
const block = await this.provider.getBlock(this.blockHash); const block = await this.provider.getBlock(this.blockHash);
assert(!!block, "failed to find transaction", "UNKNOWN_ERROR", { }); assert(!!block, "failed to find transaction", "UNKNOWN_ERROR", { });
return block; return block;
} }
/**
* Returns the transaction that this log occurred in.
*/
async getTransaction(): Promise<TransactionResponse> { async getTransaction(): Promise<TransactionResponse> {
const tx = await this.provider.getTransaction(this.transactionHash); const tx = await this.provider.getTransaction(this.transactionHash);
assert(!!tx, "failed to find transaction", "UNKNOWN_ERROR", { }); assert(!!tx, "failed to find transaction", "UNKNOWN_ERROR", { });
return tx; return tx;
} }
/**
* Returns the transaction receipt fot the transaction that this
* log occurred in.
*/
async getTransactionReceipt(): Promise<TransactionReceipt> { async getTransactionReceipt(): Promise<TransactionReceipt> {
const receipt = await this.provider.getTransactionReceipt(this.transactionHash); const receipt = await this.provider.getTransactionReceipt(this.transactionHash);
assert(!!receipt, "failed to find transaction receipt", "UNKNOWN_ERROR", { }); assert(!!receipt, "failed to find transaction receipt", "UNKNOWN_ERROR", { });
return receipt; return receipt;
} }
/**
* @_ignore:
*/
removedEvent(): OrphanFilter { removedEvent(): OrphanFilter {
return createRemovedLogFilter(this); return createRemovedLogFilter(this);
} }
@ -575,32 +844,118 @@ export interface ByzantiumTransactionReceipt {
} }
*/ */
/**
* A **TransactionReceipt** includes additional information about a
* transaction that is only available after it has been mined.
*/
export class TransactionReceipt implements TransactionReceiptParams, Iterable<Log> { export class TransactionReceipt implements TransactionReceiptParams, Iterable<Log> {
/**
* The provider connected to the log used to fetch additional details
* if necessary.
*/
readonly provider!: Provider; readonly provider!: Provider;
/**
* The address the transaction was send to.
*/
readonly to!: null | string; readonly to!: null | string;
/**
* The sender of the transaction.
*/
readonly from!: string; readonly from!: string;
/**
* The address of the contract if the transaction was directly
* responsible for deploying one.
*
* This is non-null **only** if the ``to`` is empty and the ``data``
* was successfully executed as initcode.
*/
readonly contractAddress!: null | string; readonly contractAddress!: null | string;
/**
* The transaction hash.
*/
readonly hash!: string; readonly hash!: string;
/**
* The index of this transaction within the block transactions.
*/
readonly index!: number; readonly index!: number;
/**
* The block hash of the [[Block]] this transaction was included in.
*/
readonly blockHash!: string; readonly blockHash!: string;
/**
* The block number of the [[Block]] this transaction was included in.
*/
readonly blockNumber!: number; readonly blockNumber!: number;
/**
* The bloom filter bytes that represent all logs that occurred within
* this transaction. This is generally not useful for most developers,
* but can be used to validate the included logs.
*/
readonly logsBloom!: string; readonly logsBloom!: string;
/**
* The actual amount of gas used by this transaction.
*
* When creating a transaction, the amount of gas that will be used can
* only be approximated, but the sender must pay the gas fee for the
* entire gas limit. After the transaction, the difference is refunded.
*/
readonly gasUsed!: bigint; readonly gasUsed!: bigint;
/**
* The amount of gas used by all transactions within the block for this
* and all transactions with a lower ``index``.
*
* This is generally not useful for developers but can be used to
* validate certain aspects of execution.
*/
readonly cumulativeGasUsed!: bigint; readonly cumulativeGasUsed!: bigint;
/**
* The actual gas price used during execution.
*
* Due to the complexity of [[link-eip-1559]] this value can only
* be caluclated after the transaction has been mined, snce the base
* fee is protocol-enforced.
*/
readonly gasPrice!: bigint; readonly gasPrice!: bigint;
/**
* The [[link-eip-2718]] transaction type.
*/
readonly type!: number; readonly type!: number;
//readonly byzantium!: boolean; //readonly byzantium!: boolean;
/**
* The status of this transaction, indicating success (i.e. ``1``) or
* a revert (i.e. ``0``).
*
* This is available in post-byzantium blocks, but some backends may
* backfill this value.
*/
readonly status!: null | number; readonly status!: null | number;
/**
* The root hash of this transaction.
*
* This is no present and was only included in pre-byzantium blocks, but
* could be used to validate certain parts of the receipt.
*/
readonly root!: null | string; readonly root!: null | string;
readonly #logs: ReadonlyArray<Log>; readonly #logs: ReadonlyArray<Log>;
/**
* @_ignore:
*/
constructor(tx: TransactionReceiptParams, provider: Provider) { constructor(tx: TransactionReceiptParams, provider: Provider) {
this.#logs = Object.freeze(tx.logs.map((log) => { this.#logs = Object.freeze(tx.logs.map((log) => {
return new Log(log, provider); return new Log(log, provider);
@ -639,8 +994,14 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable<Lo
}); });
} }
/**
* The logs for this transaction.
*/
get logs(): ReadonlyArray<Log> { return this.#logs; } get logs(): ReadonlyArray<Log> { return this.#logs; }
/**
* Returns a JSON-compatible representation.
*/
toJSON(): any { toJSON(): any {
const { const {
to, from, contractAddress, hash, index, blockHash, blockNumber, logsBloom, to, from, contractAddress, hash, index, blockHash, blockNumber, logsBloom,
@ -661,6 +1022,9 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable<Lo
}; };
} }
/**
* @_ignore:
*/
get length(): number { return this.logs.length; } get length(): number { return this.logs.length; }
[Symbol.iterator](): Iterator<Log> { [Symbol.iterator](): Iterator<Log> {
@ -675,34 +1039,58 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable<Lo
}; };
} }
/**
* The total fee for this transaction, in wei.
*/
get fee(): bigint { get fee(): bigint {
return this.gasUsed * this.gasPrice; return this.gasUsed * this.gasPrice;
} }
/**
* Resolves to the block this transaction occurred in.
*/
async getBlock(): Promise<Block> { async getBlock(): Promise<Block> {
const block = await this.provider.getBlock(this.blockHash); const block = await this.provider.getBlock(this.blockHash);
if (block == null) { throw new Error("TODO"); } if (block == null) { throw new Error("TODO"); }
return block; return block;
} }
/**
* Resolves to the transaction this transaction occurred in.
*/
async getTransaction(): Promise<TransactionResponse> { async getTransaction(): Promise<TransactionResponse> {
const tx = await this.provider.getTransaction(this.hash); const tx = await this.provider.getTransaction(this.hash);
if (tx == null) { throw new Error("TODO"); } if (tx == null) { throw new Error("TODO"); }
return tx; return tx;
} }
/**
* Resolves to the return value of the execution of this transaction.
*
* Support for this feature is limited, as it requires an archive node
* with the ``debug_`` or ``trace_`` API enabled.
*/
async getResult(): Promise<string> { async getResult(): Promise<string> {
return <string>(await this.provider.getTransactionResult(this.hash)); return <string>(await this.provider.getTransactionResult(this.hash));
} }
/**
* Resolves to the number of confirmations this transaction has.
*/
async confirmations(): Promise<number> { async confirmations(): Promise<number> {
return (await this.provider.getBlockNumber()) - this.blockNumber + 1; return (await this.provider.getBlockNumber()) - this.blockNumber + 1;
} }
/**
* @_ignore:
*/
removedEvent(): OrphanFilter { removedEvent(): OrphanFilter {
return createRemovedTransactionFilter(this); return createRemovedTransactionFilter(this);
} }
/**
* @_ignore:
*/
reorderedEvent(other?: TransactionResponse): OrphanFilter { reorderedEvent(other?: TransactionResponse): OrphanFilter {
assert(!other || other.isMined(), "unmined 'other' transction cannot be orphaned", assert(!other || other.isMined(), "unmined 'other' transction cannot be orphaned",
"UNSUPPORTED_OPERATION", { operation: "reorderedEvent(other)" }); "UNSUPPORTED_OPERATION", { operation: "reorderedEvent(other)" });
@ -714,22 +1102,38 @@ export class TransactionReceipt implements TransactionReceiptParams, Iterable<Lo
////////////////////// //////////////////////
// Transaction Response // Transaction Response
/**
* A **MinedTransactionResponse** is an interface representing a
* transaction which has been mined and allows for a type guard for its
* property values being defined.
*/
export interface MinedTransactionResponse extends TransactionResponse { export interface MinedTransactionResponse extends TransactionResponse {
/**
* The block number this transaction occurred in.
*/
blockNumber: number; blockNumber: number;
/**
* The block hash this transaction occurred in.
*/
blockHash: string; blockHash: string;
/**
* The date this transaction occurred on.
*/
date: Date; date: Date;
} }
/*
export type ReplacementDetectionSetup = { /**
to: string; * A **TransactionResponse** includes all properties about a transaction
from: string; * that was sent to the network, which may or may not be included in a
value: bigint; * block.
data: string; *
nonce: number; * The [[TransactionResponse-isMined]] can be used to check if the
block: number; * transaction has been mined as well as type guard that the otherwise
}; * possibly ``null`` properties are defined.
*/ */
export class TransactionResponse implements TransactionLike<string>, TransactionResponseParams { export class TransactionResponse implements TransactionLike<string>, TransactionResponseParams {
/** /**
* The provider this is connected to, which will influence how its * The provider this is connected to, which will influence how its
@ -858,8 +1262,7 @@ export class TransactionResponse implements TransactionLike<string>, Transaction
#startBlock: number; #startBlock: number;
/** /**
* Create a new TransactionResponse with %%tx%% parameters * @_ignore:
* connected to %%provider%%.
*/ */
constructor(tx: TransactionResponseParams, provider: Provider) { constructor(tx: TransactionResponseParams, provider: Provider) {
this.provider = provider; this.provider = provider;
@ -893,7 +1296,7 @@ export class TransactionResponse implements TransactionLike<string>, Transaction
} }
/** /**
* Returns a JSON representation of this transaction. * Returns a JSON-compatible representation of this transaction.
*/ */
toJSON(): any { toJSON(): any {
const { const {
@ -1272,17 +1675,40 @@ export type TopicFilter = Array<null | string | Array<string>>;
// @TODO: // @TODO:
//export type DeferableTopicFilter = Array<null | string | Promise<string> | Array<string | Promise<string>>>; //export type DeferableTopicFilter = Array<null | string | Promise<string> | Array<string | Promise<string>>>;
/**
* An **EventFilter** allows efficiently filtering logs (also known as
* events) using bloom filters included within blocks.
*/
export interface EventFilter { export interface EventFilter {
address?: AddressLike | Array<AddressLike>; address?: AddressLike | Array<AddressLike>;
topics?: TopicFilter; topics?: TopicFilter;
} }
/**
* A **Filter** allows searching a specific range of blocks for mathcing
* logs.
*/
export interface Filter extends EventFilter { export interface Filter extends EventFilter {
/**
* The start block for the filter (inclusive).
*/
fromBlock?: BlockTag; fromBlock?: BlockTag;
/**
* The end block for the filter (inclusive).
*/
toBlock?: BlockTag; toBlock?: BlockTag;
} }
/**
* A **FilterByBlockHash** allows searching a specific block for mathcing
* logs.
*/
export interface FilterByBlockHash extends EventFilter { export interface FilterByBlockHash extends EventFilter {
/**
* The blockhash of the specific block for the filter.
*/
blockHash?: string; blockHash?: string;
} }
@ -1290,6 +1716,30 @@ export interface FilterByBlockHash extends EventFilter {
////////////////////// //////////////////////
// ProviderEvent // ProviderEvent
/**
* A **ProviderEvent** provides the types of events that can be subscribed
* to on a [[Provider]].
*
* Each provider may include additional possible events it supports, but
* the most commonly supported are:
*
* **``"block"``** - calls the listener with the current block number on each
* new block.
*
* **``"error"``** - calls the listener on each async error that occurs during
* the event loop, with the error.
*
* **``"debug"``** - calls the listener on debug events, which can be used to
* troubleshoot network errors, provider problems, etc.
*
* **``transaction hash``** - calls the listener on each block after the
* transaction has been mined; generally ``.once`` is more appropriate for
* this event.
*
* **``Array``** - calls the listener on each log that matches the filter.
*
* [[EventFilter]] - calls the listener with each matching log
*/
export type ProviderEvent = string | Array<string | Array<string>> | EventFilter | OrphanFilter; export type ProviderEvent = string | Array<string | Array<string>> | EventFilter | OrphanFilter;

@ -9,12 +9,23 @@ import type {
import type { Signer } from "./signer.js"; import type { Signer } from "./signer.js";
/**
* A **NonceManager** wraps another [[Signer]] and automatically manages
* the nonce, ensuring serialized and sequential nonces are used during
* transaction.
*/
export class NonceManager extends AbstractSigner { export class NonceManager extends AbstractSigner {
/**
* The Signer being managed.
*/
signer!: Signer; signer!: Signer;
#noncePromise: null | Promise<number>; #noncePromise: null | Promise<number>;
#delta: number; #delta: number;
/**
* Creates a new **NonceManager** to manage %%signer%%.
*/
constructor(signer: Signer) { constructor(signer: Signer) {
super(signer.provider); super(signer.provider);
defineProperties<NonceManager>(this, { signer }); defineProperties<NonceManager>(this, { signer });
@ -44,10 +55,18 @@ export class NonceManager extends AbstractSigner {
return super.getNonce(blockTag); return super.getNonce(blockTag);
} }
/**
* Manually increment the nonce. This may be useful when managng
* offline transactions.
*/
increment(): void { increment(): void {
this.#delta++; this.#delta++;
} }
/**
* Resets the nonce, causing the **NonceManager** to reload the current
* nonce from the blockchain on the next transaction.
*/
reset(): void { reset(): void {
this.#delta = 0; this.#delta = 0;
this.#noncePromise = null; this.#noncePromise = null;

@ -33,6 +33,11 @@ export class FilterIdSubscriber implements Subscriber {
#hault: boolean; #hault: boolean;
/**
* Creates a new **FilterIdSubscriber** which will used [[_subscribe]]
* and [[_emitResults]] to setup the subscription and provide the event
* to the %%provider%%.
*/
constructor(provider: JsonRpcApiProvider) { constructor(provider: JsonRpcApiProvider) {
this.#provider = provider; this.#provider = provider;
@ -46,14 +51,23 @@ export class FilterIdSubscriber implements Subscriber {
this.#hault = false; this.#hault = false;
} }
/**
* Sub-classes **must** override this to begin the subscription.
*/
_subscribe(provider: JsonRpcApiProvider): Promise<string> { _subscribe(provider: JsonRpcApiProvider): Promise<string> {
throw new Error("subclasses must override this"); throw new Error("subclasses must override this");
} }
/**
* Sub-classes **must** override this handle the events.
*/
_emitResults(provider: AbstractProvider, result: Array<any>): Promise<void> { _emitResults(provider: AbstractProvider, result: Array<any>): Promise<void> {
throw new Error("subclasses must override this"); throw new Error("subclasses must override this");
} }
/**
* Sub-classes **must** override this handle recovery on errors.
*/
_recover(provider: AbstractProvider): Subscriber { _recover(provider: AbstractProvider): Subscriber {
throw new Error("subclasses must override this"); throw new Error("subclasses must override this");
} }
@ -141,6 +155,10 @@ export class FilterIdSubscriber implements Subscriber {
export class FilterIdEventSubscriber extends FilterIdSubscriber { export class FilterIdEventSubscriber extends FilterIdSubscriber {
#event: EventFilter; #event: EventFilter;
/**
* Creates a new **FilterIdEventSubscriber** attached to %%provider%%
* listening for %%filter%%.
*/
constructor(provider: JsonRpcApiProvider, filter: EventFilter) { constructor(provider: JsonRpcApiProvider, filter: EventFilter) {
super(provider); super(provider);
this.#event = copy(filter); this.#event = copy(filter);

@ -8,7 +8,7 @@ function copy(obj: any): any {
} }
/** /**
* @TODO * Return the polling subscriber for common events.
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
@ -24,7 +24,8 @@ export function getPollingSubscriber(provider: AbstractProvider, event: Provider
// @TODO: refactor this // @TODO: refactor this
/** /**
* @TODO * A **PollingBlockSubscriber** polls at a regular interval for a change
* in the block number.
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
@ -38,6 +39,9 @@ export class PollingBlockSubscriber implements Subscriber {
// indicates we still need to fetch an initial block number // indicates we still need to fetch an initial block number
#blockNumber: number; #blockNumber: number;
/**
* Create a new **PollingBlockSubscriber** attached to %%provider%%.
*/
constructor(provider: AbstractProvider) { constructor(provider: AbstractProvider) {
this.#provider = provider; this.#provider = provider;
this.#poller = null; this.#poller = null;
@ -46,6 +50,9 @@ export class PollingBlockSubscriber implements Subscriber {
this.#blockNumber = -2; this.#blockNumber = -2;
} }
/**
* The polling interval.
*/
get pollingInterval(): number { return this.#interval; } get pollingInterval(): number { return this.#interval; }
set pollingInterval(value: number) { this.#interval = value; } set pollingInterval(value: number) { this.#interval = value; }
@ -107,7 +114,8 @@ export class PollingBlockSubscriber implements Subscriber {
} }
/** /**
* @TODO * An **OnBlockSubscriber** can be sub-classed, with a [[_poll]]
* implmentation which will be called on every new block.
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
@ -116,6 +124,9 @@ export class OnBlockSubscriber implements Subscriber {
#poll: (b: number) => void; #poll: (b: number) => void;
#running: boolean; #running: boolean;
/**
* Create a new **OnBlockSubscriber** attached to %%provider%%.
*/
constructor(provider: AbstractProvider) { constructor(provider: AbstractProvider) {
this.#provider = provider; this.#provider = provider;
this.#running = false; this.#running = false;
@ -124,6 +135,9 @@ export class OnBlockSubscriber implements Subscriber {
} }
} }
/**
* Called on every new block.
*/
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> { async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
throw new Error("sub-classes must override this"); throw new Error("sub-classes must override this");
} }
@ -148,7 +162,7 @@ export class OnBlockSubscriber implements Subscriber {
} }
/** /**
* @TODO * @_ignore:
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
@ -167,13 +181,18 @@ export class PollingOrphanSubscriber extends OnBlockSubscriber {
} }
/** /**
* @TODO * A **PollingTransactionSubscriber** will poll for a given transaction
* hash for its receipt.
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
export class PollingTransactionSubscriber extends OnBlockSubscriber { export class PollingTransactionSubscriber extends OnBlockSubscriber {
#hash: string; #hash: string;
/**
* Create a new **PollingTransactionSubscriber** attached to
* %%provider%%, listening for %%hash%%.
*/
constructor(provider: AbstractProvider, hash: string) { constructor(provider: AbstractProvider, hash: string) {
super(provider); super(provider);
this.#hash = hash; this.#hash = hash;
@ -186,7 +205,7 @@ export class PollingTransactionSubscriber extends OnBlockSubscriber {
} }
/** /**
* @TODO * A **PollingEventSubscriber** will poll for a given filter for its logs.
* *
* @_docloc: api/providers/abstract-provider * @_docloc: api/providers/abstract-provider
*/ */
@ -201,6 +220,10 @@ export class PollingEventSubscriber implements Subscriber {
// indicates we still need to fetch an initial block number // indicates we still need to fetch an initial block number
#blockNumber: number; #blockNumber: number;
/**
* Create a new **PollingTransactionSubscriber** attached to
* %%provider%%, listening for %%filter%%.
*/
constructor(provider: AbstractProvider, filter: EventFilter) { constructor(provider: AbstractProvider, filter: EventFilter) {
this.#provider = provider; this.#provider = provider;
this.#filter = copy(filter); this.#filter = copy(filter);

@ -1,5 +1,5 @@
/** /**
* Transactions.. * Each state-changing operation on Ethereum requires a transaction.
* *
* @_section api/transaction:Transactions [about-transactions] * @_section api/transaction:Transactions [about-transactions]
*/ */

@ -22,6 +22,11 @@ const BN_28 = BigInt(28)
const BN_35 = BigInt(35); const BN_35 = BigInt(35);
const BN_MAX_UINT = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); const BN_MAX_UINT = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
/**
* A **TransactionLike** is an object which is appropriate as a loose
* input for many operations which will populate missing properties of
* a transaction.
*/
export interface TransactionLike<A = string> { export interface TransactionLike<A = string> {
/** /**
* The type. * The type.

@ -1,5 +1,9 @@
/** /**
* About Errors. * All errors in ethers include properties to ensure they are both
* human-readable (i.e. ``.message``) and machine-readable (i.e. ``.code``).
*
* The [[isError]] function can be used to check the error ``code`` and
* provide a type guard for the properties present on that error interface.
* *
* @_section: api/utils/errors:Errors [about-errors] * @_section: api/utils/errors:Errors [about-errors]
*/ */
@ -14,6 +18,10 @@ import type {
import type { FetchRequest, FetchResponse } from "./fetch.js"; import type { FetchRequest, FetchResponse } from "./fetch.js";
/**
* An error may contain additional properties, but those must not
* conflict with any impliciat properties.
*/
export type ErrorInfo<T> = Omit<T, "code" | "name" | "message">; export type ErrorInfo<T> = Omit<T, "code" | "name" | "message">;
@ -202,6 +210,9 @@ export interface UnsupportedOperationError extends EthersError<"UNSUPPORTED_OPER
operation: string; operation: string;
} }
/**
* This Error indicates a proplem connecting to a network.
*/
export interface NetworkError extends EthersError<"NETWORK_ERROR"> { export interface NetworkError extends EthersError<"NETWORK_ERROR"> {
event: string; event: string;
} }

@ -1,5 +1,7 @@
/** /**
* Explain events... * Events allow for applications to use the observer pattern, which
* allows subscribing and publishing events, outside the normal
* execution paths.
* *
* @_section api/utils/events:Events [about-events] * @_section api/utils/events:Events [about-events]
*/ */

@ -1,5 +1,19 @@
/** /**
* Explain fetching here... * Fetching content from the web is environment-specific, so Ethers
* provides an abstraction the each environment can implement to provide
* this service.
*
* On [Node.js](link-node), the ``http`` and ``https`` libs are used to
* create a request object, register event listeners and process data
* and populate the [[FetchResponse]].
*
* In a browser, the [DOM fetch](link-js-fetch) is used, and the resulting
* ``Promise`` is waited on to retreive the payload.
*
* The [[FetchRequest]] is responsible for handling many common situations,
* such as redirects, server throttling, authentcation, etc.
*
* It also handles common gateways, such as IPFS and data URIs.
* *
* @_section api/utils/fetching:Fetching Web Content [about-fetch] * @_section api/utils/fetching:Fetching Web Content [about-fetch]
*/ */
@ -11,7 +25,9 @@ import { toUtf8Bytes, toUtf8String } from "./utf8.js"
import { getUrl } from "./geturl.js"; import { getUrl } from "./geturl.js";
/**
* An environments implementation of ``getUrl`` must return this type.
*/
export type GetUrlResponse = { export type GetUrlResponse = {
statusCode: number, statusCode: number,
statusMessage: string, statusMessage: string,
@ -19,10 +35,15 @@ export type GetUrlResponse = {
body: null | Uint8Array body: null | Uint8Array
}; };
/**
* This can be used to control how throttling is handled in
* [[FetchRequest-setThrottleParams]].
*/
export type FetchThrottleParams = { export type FetchThrottleParams = {
maxAttempts?: number; maxAttempts?: number;
slotInterval?: number; slotInterval?: number;
}; };
/** /**
* Called before any network request, allowing updated headers (e.g. Bearer tokens), etc. * Called before any network request, allowing updated headers (e.g. Bearer tokens), etc.
*/ */

@ -1,5 +1,10 @@
/** /**
* About fixed-point math... * The **FixedNumber** class permits using values with decimal places,
* using fixed-pont math.
*
* Fixed-point math is still based on integers under-the-hood, but uses an
* internal offset to store fractional components below, and each operation
* corrects for this after each operation.
* *
* @_section: api/utils/fixed-point-math:Fixed-Point Maths [about-fixed-point-math] * @_section: api/utils/fixed-point-math:Fixed-Point Maths [about-fixed-point-math]
*/ */

@ -113,6 +113,10 @@ export function getBigInt(value: BigNumberish, name?: string): bigint {
assertArgument(false, "invalid BigNumberish value", name || "value", value); assertArgument(false, "invalid BigNumberish value", name || "value", value);
} }
/**
* Returns %%value%% as a bigint, validating it is valid as a bigint
* value and that it is positive.
*/
export function getUint(value: BigNumberish, name?: string): bigint { export function getUint(value: BigNumberish, name?: string): bigint {
const result = getBigInt(value, name); const result = getBigInt(value, name);
assert(result >= BN_0, "unsigned value cannot be negative", "NUMERIC_FAULT", { assert(result >= BN_0, "unsigned value cannot be negative", "NUMERIC_FAULT", {

@ -18,7 +18,7 @@ import { Wordlist } from "./wordlist.js";
* based on ASCII-7 small. * based on ASCII-7 small.
* *
* If necessary, there are tools within the ``generation/`` folder * If necessary, there are tools within the ``generation/`` folder
* to create these necessary data. * to create the necessary data.
*/ */
export class WordlistOwl extends Wordlist { export class WordlistOwl extends Wordlist {
#data: string; #data: string;
@ -35,8 +35,14 @@ export class WordlistOwl extends Wordlist {
this.#words = null; this.#words = null;
} }
/**
* The OWL-encoded data.
*/
get _data(): string { return this.#data; } get _data(): string { return this.#data; }
/**
* Decode all the words for the wordlist.
*/
_decodeWords(): Array<string> { _decodeWords(): Array<string> {
return decodeOwl(this.#data); return decodeOwl(this.#data);
} }

@ -12,18 +12,29 @@ import { decodeOwlA } from "./decode-owla.js";
* based on latin-1 small. * based on latin-1 small.
* *
* If necessary, there are tools within the ``generation/`` folder * If necessary, there are tools within the ``generation/`` folder
* to create these necessary data. * to create the necessary data.
*/ */
export class WordlistOwlA extends WordlistOwl { export class WordlistOwlA extends WordlistOwl {
#accent: string; #accent: string;
/**
* Creates a new Wordlist for %%locale%% using the OWLA %%data%%
* and %%accent%% data and validated against the %%checksum%%.
*/
constructor(locale: string, data: string, accent: string, checksum: string) { constructor(locale: string, data: string, accent: string, checksum: string) {
super(locale, data, checksum); super(locale, data, checksum);
this.#accent = accent; this.#accent = accent;
} }
/**
* The OWLA-encoded accent data.
*/
get _accent(): string { return this.#accent; } get _accent(): string { return this.#accent; }
/**
* Decode all the words for the wordlist.
*/
_decodeWords(): Array<string> { _decodeWords(): Array<string> {
return decodeOwlA(this._data, this._accent); return decodeOwlA(this._data, this._accent);
} }