refactor: move brother_node development artifact to dev/test-nodes subdirectory

Development Artifact Cleanup:
 BROTHER_NODE REORGANIZATION: Moved development test node to appropriate location
- dev/test-nodes/brother_node/: Moved from root directory for better organization
- Contains development configuration, test logs, and test chain data
- No impact on production systems - purely development/testing artifact

 DEVELOPMENT ARTIFACTS IDENTIFIED:
- Chain ID: aitbc-brother-chain (test/development chain)
- Ports: 8010 (P2P) and 8011 (RPC) - different from production
- Environment: .env file with test configuration
- Logs: rpc.log and node.log from development testing session (March 15, 2026)

 ROOT DIRECTORY CLEANUP: Removed development clutter from production directory
- brother_node/ moved to dev/test-nodes/brother_node/
- Root directory now contains only production-ready components
- Development artifacts properly organized in dev/ subdirectory

DIRECTORY STRUCTURE IMPROVEMENT:
📁 dev/test-nodes/: Development and testing node configurations
🏗️ Root Directory: Clean production structure with only essential components
🧪 Development Isolation: Test environments separated from production

BENEFITS:
 Clean Production Directory: No development artifacts in root
 Better Organization: Development nodes grouped in dev/ subdirectory
 Clear Separation: Production vs development environments clearly distinguished
 Maintainability: Easier to identify and manage development components

RESULT: Successfully moved brother_node development artifact to dev/test-nodes/ subdirectory, cleaning up the root directory while preserving development testing environment for future use.
This commit is contained in:
2026-03-30 17:09:06 +02:00
parent bf730dcb4a
commit 816e258d4c
11734 changed files with 2001707 additions and 0 deletions

6
dev/env/node_modules/ethers/src.ts/_version.ts generated vendored Executable file
View File

@@ -0,0 +1,6 @@
/* Do NOT modify this file; see /src.ts/_admin/update-version.ts */
/**
* The current version of Ethers.
*/
export const version: string = "6.16.0";

237
dev/env/node_modules/ethers/src.ts/abi/abi-coder.ts generated vendored Executable file
View File

@@ -0,0 +1,237 @@
/**
* When sending values to or receiving values from a [[Contract]], the
* data is generally encoded using the [ABI standard](link-solc-abi).
*
* The AbiCoder provides a utility to encode values to ABI data and
* decode values from ABI data.
*
* Most of the time, developers should favour the [[Contract]] class,
* which further abstracts a lot of the finer details of ABI data.
*
* @_section api/abi/abi-coder:ABI Encoding
*/
// See: https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
import { assertArgumentCount, assertArgument } from "../utils/index.js";
import { Coder, Reader, Result, Writer } from "./coders/abstract-coder.js";
import { AddressCoder } from "./coders/address.js";
import { ArrayCoder } from "./coders/array.js";
import { BooleanCoder } from "./coders/boolean.js";
import { BytesCoder } from "./coders/bytes.js";
import { FixedBytesCoder } from "./coders/fixed-bytes.js";
import { NullCoder } from "./coders/null.js";
import { NumberCoder } from "./coders/number.js";
import { StringCoder } from "./coders/string.js";
import { TupleCoder } from "./coders/tuple.js";
import { ParamType } from "./fragments.js";
import { getAddress } from "../address/index.js";
import { getBytes, hexlify, makeError } from "../utils/index.js";
import type {
BytesLike,
CallExceptionAction, CallExceptionError, CallExceptionTransaction
} from "../utils/index.js";
// https://docs.soliditylang.org/en/v0.8.17/control-structures.html
const PanicReasons: Map<number, string> = new Map();
PanicReasons.set(0x00, "GENERIC_PANIC");
PanicReasons.set(0x01, "ASSERT_FALSE");
PanicReasons.set(0x11, "OVERFLOW");
PanicReasons.set(0x12, "DIVIDE_BY_ZERO");
PanicReasons.set(0x21, "ENUM_RANGE_ERROR");
PanicReasons.set(0x22, "BAD_STORAGE_DATA");
PanicReasons.set(0x31, "STACK_UNDERFLOW");
PanicReasons.set(0x32, "ARRAY_RANGE_ERROR");
PanicReasons.set(0x41, "OUT_OF_MEMORY");
PanicReasons.set(0x51, "UNINITIALIZED_FUNCTION_CALL");
const paramTypeBytes = new RegExp(/^bytes([0-9]*)$/);
const paramTypeNumber = new RegExp(/^(u?int)([0-9]*)$/);
let defaultCoder: null | AbiCoder = null;
let defaultMaxInflation = 1024;
function getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike, abiCoder: AbiCoder): CallExceptionError {
let message = "missing revert data";
let reason: null | string = null;
const invocation = null;
let revert: null | { signature: string, name: string, args: Array<any> } = null;
if (data) {
message = "execution reverted";
const bytes = getBytes(data);
data = hexlify(data);
if (bytes.length === 0) {
message += " (no data present; likely require(false) occurred";
reason = "require(false)";
} else if (bytes.length % 32 !== 4) {
message += " (could not decode reason; invalid data length)";
} else if (hexlify(bytes.slice(0, 4)) === "0x08c379a0") {
// Error(string)
try {
reason = abiCoder.decode([ "string" ], bytes.slice(4))[0]
revert = {
signature: "Error(string)",
name: "Error",
args: [ reason ]
};
message += `: ${ JSON.stringify(reason) }`;
} catch (error) {
message += " (could not decode reason; invalid string data)";
}
} else if (hexlify(bytes.slice(0, 4)) === "0x4e487b71") {
// Panic(uint256)
try {
const code = Number(abiCoder.decode([ "uint256" ], bytes.slice(4))[0]);
revert = {
signature: "Panic(uint256)",
name: "Panic",
args: [ code ]
};
reason = `Panic due to ${ PanicReasons.get(code) || "UNKNOWN" }(${ code })`;
message += `: ${ reason }`;
} catch (error) {
message += " (could not decode panic code)";
}
} else {
message += " (unknown custom error)";
}
}
const transaction: CallExceptionTransaction = {
to: (tx.to ? getAddress(tx.to): null),
data: (tx.data || "0x")
};
if (tx.from) { transaction.from = getAddress(tx.from); }
return makeError(message, "CALL_EXCEPTION", {
action, data, reason, transaction, invocation, revert
});
}
/**
* 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 {
#getCoder(param: ParamType): Coder {
if (param.isArray()) {
return new ArrayCoder(this.#getCoder(param.arrayChildren), param.arrayLength, param.name);
}
if (param.isTuple()) {
return new TupleCoder(param.components.map((c) => this.#getCoder(c)), param.name);
}
switch (param.baseType) {
case "address":
return new AddressCoder(param.name);
case "bool":
return new BooleanCoder(param.name);
case "string":
return new StringCoder(param.name);
case "bytes":
return new BytesCoder(param.name);
case "":
return new NullCoder(param.name);
}
// u?int[0-9]*
let match = param.type.match(paramTypeNumber);
if (match) {
let size = parseInt(match[2] || "256");
assertArgument(size !== 0 && size <= 256 && (size % 8) === 0,
"invalid " + match[1] + " bit length", "param", param);
return new NumberCoder(size / 8, (match[1] === "int"), param.name);
}
// bytes[0-9]+
match = param.type.match(paramTypeBytes);
if (match) {
let size = parseInt(match[1]);
assertArgument(size !== 0 && size <= 32, "invalid bytes length", "param", param);
return new FixedBytesCoder(size, param.name);
}
assertArgument(false, "invalid type", "type", param.type);
}
/**
* Get the default values for the given %%types%%.
*
* For example, a ``uint`` is by default ``0`` and ``bool``
* is by default ``false``.
*/
getDefaultValue(types: ReadonlyArray<string | ParamType>): Result {
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
const coder = new TupleCoder(coders, "_");
return coder.defaultValue();
}
/**
* Encode the %%values%% as the %%types%% into ABI data.
*
* @returns DataHexstring
*/
encode(types: ReadonlyArray<string | ParamType>, values: ReadonlyArray<any>): string {
assertArgumentCount(values.length, types.length, "types/values length mismatch");
const coders = types.map((type) => this.#getCoder(ParamType.from(type)));
const coder = (new TupleCoder(coders, "_"));
const writer = new Writer();
coder.encode(writer, values);
return writer.data;
}
/**
* Decode the ABI %%data%% as the %%types%% into values.
*
* If %%loose%% decoding is enabled, then strict padding is
* not enforced. Some older versions of Solidity incorrectly
* padded event data emitted from ``external`` functions.
*/
decode(types: ReadonlyArray<string | ParamType>, data: BytesLike, loose?: boolean): Result {
const coders: Array<Coder> = types.map((type) => this.#getCoder(ParamType.from(type)));
const coder = new TupleCoder(coders, "_");
return coder.decode(new Reader(data, loose, defaultMaxInflation));
}
static _setDefaultMaxInflation(value: number): void {
assertArgument(typeof(value) === "number" && Number.isInteger(value), "invalid defaultMaxInflation factor", "value", value);
defaultMaxInflation = value;
}
/**
* Returns the shared singleton instance of a default [[AbiCoder]].
*
* On the first call, the instance is created internally.
*/
static defaultAbiCoder(): AbiCoder {
if (defaultCoder == null) {
defaultCoder = new AbiCoder();
}
return defaultCoder;
}
/**
* Returns an ethers-compatible [[CallExceptionError]] Error for the given
* result %%data%% for the [[CallExceptionAction]] %%action%% against
* the Transaction %%tx%%.
*/
static getBuiltinCallException(action: CallExceptionAction, tx: { to?: null | string, from?: null | string, data?: string }, data: null | BytesLike): CallExceptionError {
return getBuiltinCallException(action, tx, data, AbiCoder.defaultAbiCoder());
}
}

45
dev/env/node_modules/ethers/src.ts/abi/bytes32.ts generated vendored Executable file
View File

@@ -0,0 +1,45 @@
/**
* About bytes32 strings...
*
* @_docloc: api/utils:Bytes32 Strings
*/
import {
getBytes, toUtf8Bytes, toUtf8String, zeroPadBytes
} from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
/**
* Encodes %%text%% as a Bytes32 string.
*/
export function encodeBytes32String(text: string): string {
// Get the bytes
const bytes = toUtf8Bytes(text);
// Check we have room for null-termination
if (bytes.length > 31) { throw new Error("bytes32 string must be less than 32 bytes"); }
// Zero-pad (implicitly null-terminates)
return zeroPadBytes(bytes, 32);
}
/**
* Encodes the Bytes32-encoded %%bytes%% into a string.
*/
export function decodeBytes32String(_bytes: BytesLike): string {
const data = getBytes(_bytes, "bytes");
// Must be 32 bytes with a null-termination
if (data.length !== 32) { throw new Error("invalid bytes32 - not 32 bytes long"); }
if (data[31] !== 0) { throw new Error("invalid bytes32 string - no null terminator"); }
// Find the null termination
let length = 31;
while (data[length - 1] === 0) { length--; }
// Determine the string value
return toUtf8String(data.slice(0, length));
}

View File

@@ -0,0 +1,541 @@
import {
defineProperties, concat, getBytesCopy, getNumber, hexlify,
toBeArray, toBigInt, toNumber,
assert, assertArgument
/*, isError*/
} from "../../utils/index.js";
import type { BigNumberish, BytesLike } from "../../utils/index.js";
/**
* @_ignore:
*/
export const WordSize: number = 32;
const Padding = new Uint8Array(WordSize);
// Properties used to immediate pass through to the underlying object
// - `then` is used to detect if an object is a Promise for await
const passProperties = [ "then" ];
const _guard = { };
const resultNames: WeakMap<Result, ReadonlyArray<null | string>> = new WeakMap();
function getNames(result: Result): ReadonlyArray<null | string> {
return resultNames.get(result)!;
}
function setNames(result: Result, names: ReadonlyArray<null | string>): void {
resultNames.set(result, names);
}
function throwError(name: string, error: Error): never {
const wrapped = new Error(`deferred error during ABI decoding triggered accessing ${ name }`);
(<any>wrapped).error = error;
throw wrapped;
}
function toObject(names: ReadonlyArray<null | string>, items: Result, deep?: boolean): Record<string, any> | Array<any> {
if (names.indexOf(null) >= 0) {
return items.map((item, index) => {
if (item instanceof Result) {
return toObject(getNames(item), item, deep);
}
return item;
});
}
return (<Array<string>>names).reduce((accum, name, index) => {
let item = items.getValue(name);
if (!(name in accum)) {
if (deep && item instanceof Result) {
item = toObject(getNames(item), item, deep);
}
accum[name] = item;
}
return accum;
}, <Record<string, any>>{ });
}
/**
* A [[Result]] is a sub-class of Array, which allows accessing any
* of its values either positionally by its index or, if keys are
* provided by its name.
*
* @_docloc: api/abi
*/
export class Result extends Array<any> {
// No longer used; but cannot be removed as it will remove the
// #private field from the .d.ts which may break backwards
// compatibility
readonly #names: ReadonlyArray<null | string>;
[ K: string | number ]: any
/**
* @private
*/
constructor(...args: Array<any>) {
// To properly sub-class Array so the other built-in
// functions work, the constructor has to behave fairly
// well. So, in the event we are created via fromItems()
// we build the read-only Result object we want, but on
// any other input, we use the default constructor
// constructor(guard: any, items: Array<any>, keys?: Array<null | string>);
const guard = args[0];
let items: Array<any> = args[1];
let names: Array<null | string> = (args[2] || [ ]).slice();
let wrap = true;
if (guard !== _guard) {
items = args;
names = [ ];
wrap = false;
}
// Can't just pass in ...items since an array of length 1
// is a special case in the super.
super(items.length);
items.forEach((item, index) => { this[index] = item; });
// Find all unique keys
const nameCounts = names.reduce((accum, name) => {
if (typeof(name) === "string") {
accum.set(name, (accum.get(name) || 0) + 1);
}
return accum;
}, <Map<string, number>>(new Map()));
// Remove any key thats not unique
setNames(this, Object.freeze(items.map((item, index) => {
const name = names[index];
if (name != null && nameCounts.get(name) === 1) {
return name;
}
return null;
})));
// Dummy operations to prevent TypeScript from complaining
this.#names = [ ];
if (this.#names == null) { void(this.#names); }
if (!wrap) { return; }
// A wrapped Result is immutable
Object.freeze(this);
// Proxy indices and names so we can trap deferred errors
const proxy = new Proxy(this, {
get: (target, prop, receiver) => {
if (typeof(prop) === "string") {
// Index accessor
if (prop.match(/^[0-9]+$/)) {
const index = getNumber(prop, "%index");
if (index < 0 || index >= this.length) {
throw new RangeError("out of result range");
}
const item = target[index];
if (item instanceof Error) {
throwError(`index ${ index }`, item);
}
return item;
}
// Pass important checks (like `then` for Promise) through
if (passProperties.indexOf(prop) >= 0) {
return Reflect.get(target, prop, receiver);
}
const value = target[prop];
if (value instanceof Function) {
// Make sure functions work with private variables
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding
return function(this: any, ...args: Array<any>) {
return value.apply((this === receiver) ? target: this, args);
};
} else if (!(prop in target)) {
// Possible name accessor
return target.getValue.apply((this === receiver) ? target: this, [ prop ]);
}
}
return Reflect.get(target, prop, receiver);
}
});
setNames(proxy, getNames(this));
return proxy;
}
/**
* Returns the Result as a normal Array. If %%deep%%, any children
* which are Result objects are also converted to a normal Array.
*
* This will throw if there are any outstanding deferred
* errors.
*/
toArray(deep?: boolean): Array<any> {
const result: Array<any> = [ ];
this.forEach((item, index) => {
if (item instanceof Error) { throwError(`index ${ index }`, item); }
if (deep && item instanceof Result) {
item = item.toArray(deep);
}
result.push(item);
});
return result;
}
/**
* Returns the Result as an Object with each name-value pair. If
* %%deep%%, any children which are Result objects are also
* converted to an Object.
*
* This will throw if any value is unnamed, or if there are
* any outstanding deferred errors.
*/
toObject(deep?: boolean): Record<string, any> {
const names = getNames(this);
return names.reduce((accum, name, index) => {
assert(name != null, `value at index ${ index } unnamed`, "UNSUPPORTED_OPERATION", {
operation: "toObject()"
});
return toObject(names, this, deep);
}, <Record<string, any>>{});
}
/**
* @_ignore
*/
slice(start?: number | undefined, end?: number | undefined): Result {
if (start == null) { start = 0; }
if (start < 0) {
start += this.length;
if (start < 0) { start = 0; }
}
if (end == null) { end = this.length; }
if (end < 0) {
end += this.length;
if (end < 0) { end = 0; }
}
if (end > this.length) { end = this.length; }
const _names = getNames(this);
const result: Array<any> = [ ], names: Array<null | string> = [ ];
for (let i = start; i < end; i++) {
result.push(this[i]);
names.push(_names[i]);
}
return new Result(_guard, result, names);
}
/**
* @_ignore
*/
filter(callback: (el: any, index: number, array: Result) => boolean, thisArg?: any): Result {
const _names = getNames(this);
const result: Array<any> = [ ], names: Array<null | string> = [ ];
for (let i = 0; i < this.length; i++) {
const item = this[i];
if (item instanceof Error) {
throwError(`index ${ i }`, item);
}
if (callback.call(thisArg, item, i, this)) {
result.push(item);
names.push(_names[i]);
}
}
return new Result(_guard, result, names);
}
/**
* @_ignore
*/
map<T extends any = any>(callback: (el: any, index: number, array: Result) => T, thisArg?: any): Array<T> {
const result: Array<T> = [ ];
for (let i = 0; i < this.length; i++) {
const item = this[i];
if (item instanceof Error) {
throwError(`index ${ i }`, item);
}
result.push(callback.call(thisArg, item, i, this));
}
return result;
}
/**
* Returns the value for %%name%%.
*
* Since it is possible to have a key whose name conflicts with
* a method on a [[Result]] or its superclass Array, or any
* JavaScript keyword, this ensures all named values are still
* accessible by name.
*/
getValue(name: string): any {
const index = getNames(this).indexOf(name);
if (index === -1) { return undefined; }
const value = this[index];
if (value instanceof Error) {
throwError(`property ${ JSON.stringify(name) }`, (<any>value).error);
}
return value;
}
/**
* Creates a new [[Result]] for %%items%% with each entry
* also accessible by its corresponding name in %%keys%%.
*/
static fromItems(items: Array<any>, keys?: Array<null | string>): Result {
return new Result(_guard, items, keys);
}
}
/**
* Returns all errors found in a [[Result]].
*
* Since certain errors encountered when creating a [[Result]] do
* not impact the ability to continue parsing data, they are
* deferred until they are actually accessed. Hence a faulty string
* in an Event that is never used does not impact the program flow.
*
* However, sometimes it may be useful to access, identify or
* validate correctness of a [[Result]].
*
* @_docloc api/abi
*/
export function checkResultErrors(result: Result): Array<{ path: Array<string | number>, error: Error }> {
// Find the first error (if any)
const errors: Array<{ path: Array<string | number>, error: Error }> = [ ];
const checkErrors = function(path: Array<string | number>, object: any): void {
if (!Array.isArray(object)) { return; }
for (let key in object) {
const childPath = path.slice();
childPath.push(key);
try {
checkErrors(childPath, object[key]);
} catch (error: any) {
errors.push({ path: childPath, error: error });
}
}
}
checkErrors([ ], result);
return errors;
}
function getValue(value: BigNumberish): Uint8Array {
let bytes = toBeArray(value);
assert (bytes.length <= WordSize, "value out-of-bounds",
"BUFFER_OVERRUN", { buffer: bytes, length: WordSize, offset: bytes.length });
if (bytes.length !== WordSize) {
bytes = getBytesCopy(concat([ Padding.slice(bytes.length % WordSize), bytes ]));
}
return bytes;
}
/**
* @_ignore
*/
export abstract class Coder {
// The coder name:
// - address, uint256, tuple, array, etc.
readonly name!: string;
// The fully expanded type, including composite types:
// - address, uint256, tuple(address,bytes), uint256[3][4][], etc.
readonly type!: string;
// The localName bound in the signature, in this example it is "baz":
// - tuple(address foo, uint bar) baz
readonly localName!: string;
// Whether this type is dynamic:
// - Dynamic: bytes, string, address[], tuple(boolean[]), etc.
// - Not Dynamic: address, uint256, boolean[3], tuple(address, uint8)
readonly dynamic!: boolean;
constructor(name: string, type: string, localName: string, dynamic: boolean) {
defineProperties<Coder>(this, { name, type, localName, dynamic }, {
name: "string", type: "string", localName: "string", dynamic: "boolean"
});
}
_throwError(message: string, value: any): never {
assertArgument(false, message, this.localName, value);
}
abstract encode(writer: Writer, value: any): number;
abstract decode(reader: Reader): any;
abstract defaultValue(): any;
}
/**
* @_ignore
*/
export class Writer {
// An array of WordSize lengthed objects to concatenation
#data: Array<Uint8Array>;
#dataLength: number;
constructor() {
this.#data = [ ];
this.#dataLength = 0;
}
get data(): string {
return concat(this.#data);
}
get length(): number { return this.#dataLength; }
#writeData(data: Uint8Array): number {
this.#data.push(data);
this.#dataLength += data.length;
return data.length;
}
appendWriter(writer: Writer): number {
return this.#writeData(getBytesCopy(writer.data));
}
// Arrayish item; pad on the right to *nearest* WordSize
writeBytes(value: BytesLike): number {
let bytes = getBytesCopy(value);
const paddingOffset = bytes.length % WordSize;
if (paddingOffset) {
bytes = getBytesCopy(concat([ bytes, Padding.slice(paddingOffset) ]))
}
return this.#writeData(bytes);
}
// Numeric item; pad on the left *to* WordSize
writeValue(value: BigNumberish): number {
return this.#writeData(getValue(value));
}
// Inserts a numeric place-holder, returning a callback that can
// be used to asjust the value later
writeUpdatableValue(): (value: BigNumberish) => void {
const offset = this.#data.length;
this.#data.push(Padding);
this.#dataLength += WordSize;
return (value: BigNumberish) => {
this.#data[offset] = getValue(value);
};
}
}
/**
* @_ignore
*/
export class Reader {
// Allows incomplete unpadded data to be read; otherwise an error
// is raised if attempting to overrun the buffer. This is required
// to deal with an old Solidity bug, in which event data for
// external (not public thoguh) was tightly packed.
readonly allowLoose!: boolean;
readonly #data: Uint8Array;
#offset: number;
#bytesRead: number;
#parent: null | Reader;
#maxInflation: number;
constructor(data: BytesLike, allowLoose?: boolean, maxInflation?: number) {
defineProperties<Reader>(this, { allowLoose: !!allowLoose });
this.#data = getBytesCopy(data);
this.#bytesRead = 0;
this.#parent = null;
this.#maxInflation = (maxInflation != null) ? maxInflation: 1024;
this.#offset = 0;
}
get data(): string { return hexlify(this.#data); }
get dataLength(): number { return this.#data.length; }
get consumed(): number { return this.#offset; }
get bytes(): Uint8Array { return new Uint8Array(this.#data); }
#incrementBytesRead(count: number): void {
if (this.#parent) { return this.#parent.#incrementBytesRead(count); }
this.#bytesRead += count;
// Check for excessive inflation (see: #4537)
assert(this.#maxInflation < 1 || this.#bytesRead <= this.#maxInflation * this.dataLength, `compressed ABI data exceeds inflation ratio of ${ this.#maxInflation } ( see: https:/\/github.com/ethers-io/ethers.js/issues/4537 )`, "BUFFER_OVERRUN", {
buffer: getBytesCopy(this.#data), offset: this.#offset,
length: count, info: {
bytesRead: this.#bytesRead,
dataLength: this.dataLength
}
});
}
#peekBytes(offset: number, length: number, loose?: boolean): Uint8Array {
let alignedLength = Math.ceil(length / WordSize) * WordSize;
if (this.#offset + alignedLength > this.#data.length) {
if (this.allowLoose && loose && this.#offset + length <= this.#data.length) {
alignedLength = length;
} else {
assert(false, "data out-of-bounds", "BUFFER_OVERRUN", {
buffer: getBytesCopy(this.#data),
length: this.#data.length,
offset: this.#offset + alignedLength
});
}
}
return this.#data.slice(this.#offset, this.#offset + alignedLength)
}
// Create a sub-reader with the same underlying data, but offset
subReader(offset: number): Reader {
const reader = new Reader(this.#data.slice(this.#offset + offset), this.allowLoose, this.#maxInflation);
reader.#parent = this;
return reader;
}
// Read bytes
readBytes(length: number, loose?: boolean): Uint8Array {
let bytes = this.#peekBytes(0, length, !!loose);
this.#incrementBytesRead(length);
this.#offset += bytes.length;
// @TODO: Make sure the length..end bytes are all 0?
return bytes.slice(0, length);
}
// Read a numeric values
readValue(): bigint {
return toBigInt(this.readBytes(WordSize));
}
readIndex(): number {
return toNumber(this.readBytes(WordSize));
}
}

36
dev/env/node_modules/ethers/src.ts/abi/coders/address.ts generated vendored Executable file
View File

@@ -0,0 +1,36 @@
import { getAddress } from "../../address/index.js";
import { toBeHex } from "../../utils/maths.js";
import { Typed } from "../typed.js";
import { Coder } from "./abstract-coder.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class AddressCoder extends Coder {
constructor(localName: string) {
super("address", "address", localName, false);
}
defaultValue(): string {
return "0x0000000000000000000000000000000000000000";
}
encode(writer: Writer, _value: string | Typed): number {
let value = Typed.dereference(_value, "string");
try {
value = getAddress(value);
} catch (error: any) {
return this._throwError(error.message, _value);
}
return writer.writeValue(value);
}
decode(reader: Reader): any {
return getAddress(toBeHex(reader.readValue(), 20));
}
}

29
dev/env/node_modules/ethers/src.ts/abi/coders/anonymous.ts generated vendored Executable file
View File

@@ -0,0 +1,29 @@
import { Coder } from "./abstract-coder.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* Clones the functionality of an existing Coder, but without a localName
*
* @_ignore
*/
export class AnonymousCoder extends Coder {
private coder: Coder;
constructor(coder: Coder) {
super(coder.name, coder.type, "_", coder.dynamic);
this.coder = coder;
}
defaultValue(): any {
return this.coder.defaultValue();
}
encode(writer: Writer, value: any): number {
return this.coder.encode(writer, value);
}
decode(reader: Reader): any {
return this.coder.decode(reader);
}
}

199
dev/env/node_modules/ethers/src.ts/abi/coders/array.ts generated vendored Executable file
View File

@@ -0,0 +1,199 @@
import {
defineProperties, isError, assert, assertArgument, assertArgumentCount
} from "../../utils/index.js";
import { Typed } from "../typed.js";
import { Coder, Result, WordSize, Writer } from "./abstract-coder.js";
import { AnonymousCoder } from "./anonymous.js";
import type { Reader } from "./abstract-coder.js";
/**
* @_ignore
*/
export function pack(writer: Writer, coders: ReadonlyArray<Coder>, values: Array<any> | { [ name: string ]: any }): number {
let arrayValues: Array<any> = [ ];
if (Array.isArray(values)) {
arrayValues = values;
} else if (values && typeof(values) === "object") {
let unique: { [ name: string ]: boolean } = { };
arrayValues = coders.map((coder) => {
const name = coder.localName;
assert(name, "cannot encode object for signature with missing names",
"INVALID_ARGUMENT", { argument: "values", info: { coder }, value: values });
assert(!unique[name], "cannot encode object for signature with duplicate names",
"INVALID_ARGUMENT", { argument: "values", info: { coder }, value: values });
unique[name] = true;
return values[name];
});
} else {
assertArgument(false, "invalid tuple value", "tuple", values);
}
assertArgument(coders.length === arrayValues.length, "types/value length mismatch", "tuple", values);
let staticWriter = new Writer();
let dynamicWriter = new Writer();
let updateFuncs: Array<(baseOffset: number) => void> = [];
coders.forEach((coder, index) => {
let value = arrayValues[index];
if (coder.dynamic) {
// Get current dynamic offset (for the future pointer)
let dynamicOffset = dynamicWriter.length;
// Encode the dynamic value into the dynamicWriter
coder.encode(dynamicWriter, value);
// Prepare to populate the correct offset once we are done
let updateFunc = staticWriter.writeUpdatableValue();
updateFuncs.push((baseOffset: number) => {
updateFunc(baseOffset + dynamicOffset);
});
} else {
coder.encode(staticWriter, value);
}
});
// Backfill all the dynamic offsets, now that we know the static length
updateFuncs.forEach((func) => { func(staticWriter.length); });
let length = writer.appendWriter(staticWriter);
length += writer.appendWriter(dynamicWriter);
return length;
}
/**
* @_ignore
*/
export function unpack(reader: Reader, coders: ReadonlyArray<Coder>): Result {
let values: Array<any> = [];
let keys: Array<null | string> = [ ];
// A reader anchored to this base
let baseReader = reader.subReader(0);
coders.forEach((coder) => {
let value: any = null;
if (coder.dynamic) {
let offset = reader.readIndex();
let offsetReader = baseReader.subReader(offset);
try {
value = coder.decode(offsetReader);
} catch (error: any) {
// Cannot recover from this
if (isError(error, "BUFFER_OVERRUN")) {
throw error;
}
value = error;
value.baseType = coder.name;
value.name = coder.localName;
value.type = coder.type;
}
} else {
try {
value = coder.decode(reader);
} catch (error: any) {
// Cannot recover from this
if (isError(error, "BUFFER_OVERRUN")) {
throw error;
}
value = error;
value.baseType = coder.name;
value.name = coder.localName;
value.type = coder.type;
}
}
if (value == undefined) {
throw new Error("investigate");
}
values.push(value);
keys.push(coder.localName || null);
});
return Result.fromItems(values, keys);
}
/**
* @_ignore
*/
export class ArrayCoder extends Coder {
readonly coder!: Coder;
readonly length!: number;
constructor(coder: Coder, length: number, localName: string) {
const type = (coder.type + "[" + (length >= 0 ? length: "") + "]");
const dynamic = (length === -1 || coder.dynamic);
super("array", type, localName, dynamic);
defineProperties<ArrayCoder>(this, { coder, length });
}
defaultValue(): Array<any> {
// Verifies the child coder is valid (even if the array is dynamic or 0-length)
const defaultChild = this.coder.defaultValue();
const result: Array<any> = [];
for (let i = 0; i < this.length; i++) {
result.push(defaultChild);
}
return result;
}
encode(writer: Writer, _value: Array<any> | Typed): number {
const value = Typed.dereference(_value, "array");
if(!Array.isArray(value)) {
this._throwError("expected array value", value);
}
let count = this.length;
if (count === -1) {
count = value.length;
writer.writeValue(value.length);
}
assertArgumentCount(value.length, count, "coder array" + (this.localName? (" "+ this.localName): ""));
let coders: Array<Coder> = [ ];
for (let i = 0; i < value.length; i++) { coders.push(this.coder); }
return pack(writer, coders, value);
}
decode(reader: Reader): any {
let count = this.length;
if (count === -1) {
count = reader.readIndex();
// Check that there is *roughly* enough data to ensure
// stray random data is not being read as a length. Each
// slot requires at least 32 bytes for their value (or 32
// bytes as a link to the data). This could use a much
// tighter bound, but we are erroring on the side of safety.
assert(count * WordSize <= reader.dataLength, "insufficient data length",
"BUFFER_OVERRUN", { buffer: reader.bytes, offset: count * WordSize, length: reader.dataLength });
}
let coders: Array<Coder> = [];
for (let i = 0; i < count; i++) { coders.push(new AnonymousCoder(this.coder)); }
return unpack(reader, coders);
}
}

27
dev/env/node_modules/ethers/src.ts/abi/coders/boolean.ts generated vendored Executable file
View File

@@ -0,0 +1,27 @@
import { Typed } from "../typed.js";
import { Coder } from "./abstract-coder.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class BooleanCoder extends Coder {
constructor(localName: string) {
super("bool", "bool", localName, false);
}
defaultValue(): boolean {
return false;
}
encode(writer: Writer, _value: boolean | Typed): number {
const value = Typed.dereference(_value, "bool");
return writer.writeValue(value ? 1: 0);
}
decode(reader: Reader): any {
return !!reader.readValue();
}
}

43
dev/env/node_modules/ethers/src.ts/abi/coders/bytes.ts generated vendored Executable file
View File

@@ -0,0 +1,43 @@
import { getBytesCopy, hexlify } from "../../utils/index.js";
import { Coder } from "./abstract-coder.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class DynamicBytesCoder extends Coder {
constructor(type: string, localName: string) {
super(type, type, localName, true);
}
defaultValue(): string {
return "0x";
}
encode(writer: Writer, value: any): number {
value = getBytesCopy(value);
let length = writer.writeValue(value.length);
length += writer.writeBytes(value);
return length;
}
decode(reader: Reader): any {
return reader.readBytes(reader.readIndex(), true);
}
}
/**
* @_ignore
*/
export class BytesCoder extends DynamicBytesCoder {
constructor(localName: string) {
super("bytes", localName);
}
decode(reader: Reader): any {
return hexlify(super.decode(reader));
}
}

37
dev/env/node_modules/ethers/src.ts/abi/coders/fixed-bytes.ts generated vendored Executable file
View File

@@ -0,0 +1,37 @@
import { defineProperties, getBytesCopy, hexlify } from "../../utils/index.js";
import { Typed } from "../typed.js";
import { Coder } from "./abstract-coder.js";
import type { BytesLike } from "../../utils/index.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class FixedBytesCoder extends Coder {
readonly size!: number;
constructor(size: number, localName: string) {
let name = "bytes" + String(size);
super(name, name, localName, false);
defineProperties<FixedBytesCoder>(this, { size }, { size: "number" });
}
defaultValue(): string {
return ("0x0000000000000000000000000000000000000000000000000000000000000000").substring(0, 2 + this.size * 2);
}
encode(writer: Writer, _value: BytesLike | Typed): number {
let data = getBytesCopy(Typed.dereference(_value, this.type));
if (data.length !== this.size) { this._throwError("incorrect data length", _value); }
return writer.writeBytes(data);
}
decode(reader: Reader): any {
return hexlify(reader.readBytes(this.size));
}
}

28
dev/env/node_modules/ethers/src.ts/abi/coders/null.ts generated vendored Executable file
View File

@@ -0,0 +1,28 @@
import { Coder } from "./abstract-coder.js";
import type { Reader, Writer } from "./abstract-coder.js";
const Empty = new Uint8Array([ ]);
/**
* @_ignore
*/
export class NullCoder extends Coder {
constructor(localName: string) {
super("null", "", localName, false);
}
defaultValue(): null {
return null;
}
encode(writer: Writer, value: any): number {
if (value != null) { this._throwError("not null", value); }
return writer.writeBytes(Empty);
}
decode(reader: Reader): any {
reader.readBytes(0);
return null;
}
}

63
dev/env/node_modules/ethers/src.ts/abi/coders/number.ts generated vendored Executable file
View File

@@ -0,0 +1,63 @@
import {
defineProperties, fromTwos, getBigInt, mask, toTwos
} from "../../utils/index.js";
import { Typed } from "../typed.js";
import { Coder, WordSize } from "./abstract-coder.js";
import type { BigNumberish } from "../../utils/index.js";
import type { Reader, Writer } from "./abstract-coder.js";
const BN_0 = BigInt(0);
const BN_1 = BigInt(1);
const BN_MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
/**
* @_ignore
*/
export class NumberCoder extends Coder {
readonly size!: number;
readonly signed!: boolean;
constructor(size: number, signed: boolean, localName: string) {
const name = ((signed ? "int": "uint") + (size * 8));
super(name, name, localName, false);
defineProperties<NumberCoder>(this, { size, signed }, { size: "number", signed: "boolean" });
}
defaultValue(): number {
return 0;
}
encode(writer: Writer, _value: BigNumberish | Typed): number {
let value = getBigInt(Typed.dereference(_value, this.type));
// Check bounds are safe for encoding
let maxUintValue = mask(BN_MAX_UINT256, WordSize * 8);
if (this.signed) {
let bounds = mask(maxUintValue, (this.size * 8) - 1);
if (value > bounds || value < -(bounds + BN_1)) {
this._throwError("value out-of-bounds", _value);
}
value = toTwos(value, 8 * WordSize);
} else if (value < BN_0 || value > mask(maxUintValue, this.size * 8)) {
this._throwError("value out-of-bounds", _value);
}
return writer.writeValue(value);
}
decode(reader: Reader): any {
let value = mask(reader.readValue(), this.size * 8);
if (this.signed) {
value = fromTwos(value, this.size * 8);
}
return value;
}
}

29
dev/env/node_modules/ethers/src.ts/abi/coders/string.ts generated vendored Executable file
View File

@@ -0,0 +1,29 @@
import { toUtf8Bytes, toUtf8String } from "../../utils/utf8.js";
import { Typed } from "../typed.js";
import { DynamicBytesCoder } from "./bytes.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class StringCoder extends DynamicBytesCoder {
constructor(localName: string) {
super("string", localName);
}
defaultValue(): string {
return "";
}
encode(writer: Writer, _value: string | Typed): number {
return super.encode(writer, toUtf8Bytes(Typed.dereference(_value, "string")));
}
decode(reader: Reader): any {
return toUtf8String(super.decode(reader));
}
}

69
dev/env/node_modules/ethers/src.ts/abi/coders/tuple.ts generated vendored Executable file
View File

@@ -0,0 +1,69 @@
import { defineProperties } from "../../utils/properties.js";
import { Typed } from "../typed.js";
import { Coder } from "./abstract-coder.js";
import { pack, unpack } from "./array.js";
import type { Reader, Writer } from "./abstract-coder.js";
/**
* @_ignore
*/
export class TupleCoder extends Coder {
readonly coders!: ReadonlyArray<Coder>;
constructor(coders: Array<Coder>, localName: string) {
let dynamic = false;
const types: Array<string> = [];
coders.forEach((coder) => {
if (coder.dynamic) { dynamic = true; }
types.push(coder.type);
});
const type = ("tuple(" + types.join(",") + ")");
super("tuple", type, localName, dynamic);
defineProperties<TupleCoder>(this, { coders: Object.freeze(coders.slice()) });
}
defaultValue(): any {
const values: any = [ ];
this.coders.forEach((coder) => {
values.push(coder.defaultValue());
});
// We only output named properties for uniquely named coders
const uniqueNames = this.coders.reduce((accum, coder) => {
const name = coder.localName;
if (name) {
if (!accum[name]) { accum[name] = 0; }
accum[name]++;
}
return accum;
}, <{ [ name: string ]: number }>{ });
// Add named values
this.coders.forEach((coder: Coder, index: number) => {
let name = coder.localName;
if (!name || uniqueNames[name] !== 1) { return; }
if (name === "length") { name = "_length"; }
if (values[name] != null) { return; }
values[name] = values[index];
});
return Object.freeze(values);
}
encode(writer: Writer, _value: Array<any> | { [ name: string ]: any } | Typed): number {
const value = Typed.dereference(_value, "tuple");
return pack(writer, this.coders, value);
}
decode(reader: Reader): any {
return unpack(reader, this.coders);
}
}

1617
dev/env/node_modules/ethers/src.ts/abi/fragments.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

41
dev/env/node_modules/ethers/src.ts/abi/index.ts generated vendored Executable file
View File

@@ -0,0 +1,41 @@
/**
* 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]
* @_navTitle: ABI
*/
//////
export { AbiCoder } from "./abi-coder.js";
export { decodeBytes32String, encodeBytes32String } from "./bytes32.js";
export {
ConstructorFragment, ErrorFragment, EventFragment, FallbackFragment,
Fragment, FunctionFragment, NamedFragment, ParamType, StructFragment,
} from "./fragments.js";
export {
checkResultErrors,
Indexed,
Interface,
ErrorDescription, LogDescription, TransactionDescription,
Result
} from "./interface.js";
export { Typed } from "./typed.js";
export type {
JsonFragment, JsonFragmentType,
FormatType, FragmentType, ParamTypeWalkAsyncFunc, ParamTypeWalkFunc
} from "./fragments.js";
export type {
InterfaceAbi,
} from "./interface.js";

1271
dev/env/node_modules/ethers/src.ts/abi/interface.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

796
dev/env/node_modules/ethers/src.ts/abi/typed.ts generated vendored Executable file
View File

@@ -0,0 +1,796 @@
/**
* A Typed object allows a value to have its type explicitly
* specified.
*
* For example, in Solidity, the value ``45`` could represent a
* ``uint8`` or a ``uint256``. The value ``0x1234`` could represent
* a ``bytes2`` or ``bytes``.
*
* Since JavaScript has no meaningful way to explicitly inform any
* APIs which what the type is, this allows transparent interoperation
* with Soldity.
*
* @_subsection: api/abi:Typed Values
*/
import { assertPrivate, defineProperties } from "../utils/index.js";
import type { Addressable } from "../address/index.js";
import type { BigNumberish, BytesLike } from "../utils/index.js";
import type { Result } from "./coders/abstract-coder.js";
const _gaurd = { };
function n(value: BigNumberish, width: number): Typed {
let signed = false;
if (width < 0) {
signed = true;
width *= -1;
}
// @TODO: Check range is valid for value
return new Typed(_gaurd, `${ signed ? "": "u" }int${ width }`, value, { signed, width });
}
function b(value: BytesLike, size?: number): Typed {
// @TODO: Check range is valid for value
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 {
value: number;
defaultValue(): number;
minValue(): number;
maxValue(): number;
}
/**
* A **Typed** that represents a numeric value.
*/
export interface TypedBigInt extends Typed {
/**
* The value.
*/
value: bigint;
/**
* The default value for all numeric types is ``0``.
*/
defaultValue(): bigint;
/**
* The minimum value for this type, accounting for bit-width and signed-ness.
*/
minValue(): bigint;
/**
* The minimum value for this type, accounting for bit-width.
*/
maxValue(): bigint;
}
/**
* A **Typed** that represents a binary sequence of data as bytes.
*/
export interface TypedData extends Typed {
/**
* The value.
*/
value: string;
/**
* The default value for this type.
*/
defaultValue(): string;
}
/**
* A **Typed** that represents a UTF-8 sequence of bytes.
*/
export interface TypedString extends Typed {
/**
* The value.
*/
value: string;
/**
* The default value for the string type is the empty string (i.e. ``""``).
*/
defaultValue(): string;
}
const _typedSymbol = Symbol.for("_ethers_typed");
/**
* The **Typed** class to wrap values providing explicit type information.
*/
export class Typed {
/**
* The type, as a Solidity-compatible type.
*/
readonly type!: string;
/**
* The actual value.
*/
readonly value!: any;
readonly #options: any;
/**
* @_ignore:
*/
readonly _typedSymbol!: Symbol;
/**
* @_ignore:
*/
constructor(gaurd: any, type: string, value: any, options?: any) {
if (options == null) { options = null; }
assertPrivate(_gaurd, gaurd, "Typed");
defineProperties<Typed>(this, { _typedSymbol, type, value });
this.#options = options;
// Check the value is valid
this.format();
}
/**
* Format the type as a Human-Readable type.
*/
format(): string {
if (this.type === "array") {
throw new Error("");
} else if (this.type === "dynamicArray") {
throw new Error("");
} else if (this.type === "tuple") {
return `tuple(${ this.value.map((v: Typed) => v.format()).join(",") })`
}
return this.type;
}
/**
* The default value returned by this type.
*/
defaultValue(): string | number | bigint | Result {
return 0;
}
/**
* The minimum value for numeric types.
*/
minValue(): string | number | bigint {
return 0;
}
/**
* The maximum value for numeric types.
*/
maxValue(): string | number | bigint {
return 0;
}
/**
* Returns ``true`` and provides a type guard is this is a [[TypedBigInt]].
*/
isBigInt(): this is TypedBigInt {
return !!(this.type.match(/^u?int[0-9]+$/));
}
/**
* Returns ``true`` and provides a type guard is this is a [[TypedData]].
*/
isData(): this is TypedData {
return this.type.startsWith("bytes");
}
/**
* Returns ``true`` and provides a type guard is this is a [[TypedString]].
*/
isString(): this is TypedString {
return (this.type === "string");
}
/**
* Returns the tuple name, if this is a tuple. Throws otherwise.
*/
get tupleName(): null | string {
if (this.type !== "tuple") { throw TypeError("not a tuple"); }
return this.#options;
}
// Returns the length of this type as an array
// - `null` indicates the length is unforced, it could be dynamic
// - `-1` indicates the length is dynamic
// - 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 {
if (this.type !== "array") { throw TypeError("not an array"); }
if (this.#options === true) { return -1; }
if (this.#options === false) { return (<Array<any>>(this.value)).length; }
return null;
}
/**
* Returns a new **Typed** of %%type%% with the %%value%%.
*/
static from(type: string, value: any): Typed {
return new Typed(_gaurd, type, value);
}
/**
* Return a new ``uint8`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint24`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint40`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint56`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint72`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint88`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint104`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint120`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint136`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint152`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint168`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint184`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint200`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint216`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint232`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint248`` type for %%v%%.
*/
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); }
/**
* Return a new ``uint256`` type for %%v%%.
*/
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); }
/**
* Return a new ``int16`` type for %%v%%.
*/
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); }
/**
* Return a new ``int32`` type for %%v%%.
*/
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); }
/**
* Return a new ``int48`` type for %%v%%.
*/
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); }
/**
* Return a new ``int64`` type for %%v%%.
*/
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); }
/**
* Return a new ``int80`` type for %%v%%.
*/
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); }
/**
* Return a new ``int96`` type for %%v%%.
*/
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); }
/**
* Return a new ``int112`` type for %%v%%.
*/
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); }
/**
* Return a new ``int128`` type for %%v%%.
*/
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); }
/**
* Return a new ``int144`` type for %%v%%.
*/
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); }
/**
* Return a new ``int160`` type for %%v%%.
*/
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); }
/**
* Return a new ``int176`` type for %%v%%.
*/
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); }
/**
* Return a new ``int92`` type for %%v%%.
*/
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); }
/**
* Return a new ``int208`` type for %%v%%.
*/
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); }
/**
* Return a new ``int224`` type for %%v%%.
*/
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); }
/**
* Return a new ``int240`` type for %%v%%.
*/
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); }
/**
* Return a new ``int256`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes1`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes3`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes5`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes7`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes9`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes11`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes13`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes15`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes17`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes19`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes21`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes23`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes25`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes27`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes29`` type for %%v%%.
*/
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); }
/**
* Return a new ``bytes31`` type for %%v%%.
*/
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); }
/**
* Return a new ``address`` type for %%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); }
/**
* Return a new ``bytes`` type for %%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); }
/**
* Return a new ``array`` type for %%v%%, allowing %%dynamic%% length.
*/
static array(v: Array<any | Typed>, dynamic?: null | boolean): Typed {
throw new Error("not implemented yet");
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 {
throw new Error("not implemented yet");
return new Typed(_gaurd, "tuple", v, name);
}
/**
* Return a new ``uint8`` type for %%v%%.
*/
static overrides(v: Record<string, any>): Typed {
return new Typed(_gaurd, "overrides", Object.assign({ }, v));
}
/**
* Returns true only if %%value%% is a [[Typed]] instance.
*/
static isTyped(value: any): value is Typed {
return (value
&& typeof(value) === "object"
&& "_typedSymbol" in value
&& value._typedSymbol === _typedSymbol);
}
/**
* If the value is a [[Typed]] instance, validates the underlying value
* and returns it, otherwise returns value directly.
*
* This is useful for functions that with to accept either a [[Typed]]
* object or values.
*/
static dereference<T>(value: Typed | T, type: string): T {
if (Typed.isTyped(value)) {
if (value.type !== type) {
throw new Error(`invalid type: expecetd ${ type }, got ${ value.type }`);
}
return value.value;
}
return value;
}
}

173
dev/env/node_modules/ethers/src.ts/address/address.ts generated vendored Executable file
View File

@@ -0,0 +1,173 @@
import { keccak256 } from "../crypto/index.js";
import { getBytes, assertArgument } from "../utils/index.js";
const BN_0 = BigInt(0);
const BN_36 = BigInt(36);
function getChecksumAddress(address: string): string {
// if (!isHexString(address, 20)) {
// logger.throwArgumentError("invalid address", "address", address);
// }
address = address.toLowerCase();
const chars = address.substring(2).split("");
const expanded = new Uint8Array(40);
for (let i = 0; i < 40; i++) {
expanded[i] = chars[i].charCodeAt(0);
}
const hashed = getBytes(keccak256(expanded));
for (let i = 0; i < 40; i += 2) {
if ((hashed[i >> 1] >> 4) >= 8) {
chars[i] = chars[i].toUpperCase();
}
if ((hashed[i >> 1] & 0x0f) >= 8) {
chars[i + 1] = chars[i + 1].toUpperCase();
}
}
return "0x" + chars.join("");
}
// See: https://en.wikipedia.org/wiki/International_Bank_Account_Number
// Create lookup table
const ibanLookup: { [character: string]: string } = { };
for (let i = 0; i < 10; i++) { ibanLookup[String(i)] = String(i); }
for (let i = 0; i < 26; i++) { ibanLookup[String.fromCharCode(65 + i)] = String(10 + i); }
// How many decimal digits can we process? (for 64-bit float, this is 15)
// i.e. Math.floor(Math.log10(Number.MAX_SAFE_INTEGER));
const safeDigits = 15;
function ibanChecksum(address: string): string {
address = address.toUpperCase();
address = address.substring(4) + address.substring(0, 2) + "00";
let expanded = address.split("").map((c) => { return ibanLookup[c]; }).join("");
// Javascript can handle integers safely up to 15 (decimal) digits
while (expanded.length >= safeDigits){
let block = expanded.substring(0, safeDigits);
expanded = parseInt(block, 10) % 97 + expanded.substring(block.length);
}
let checksum = String(98 - (parseInt(expanded, 10) % 97));
while (checksum.length < 2) { checksum = "0" + checksum; }
return checksum;
};
const Base36 = (function() {;
const result: Record<string, bigint> = { };
for (let i = 0; i < 36; i++) {
const key = "0123456789abcdefghijklmnopqrstuvwxyz"[i];
result[key] = BigInt(i);
}
return result;
})();
function fromBase36(value: string): bigint {
value = value.toLowerCase();
let result = BN_0;
for (let i = 0; i < value.length; i++) {
result = result * BN_36 + Base36[value[i]];
}
return result;
}
/**
* Returns a normalized and checksumed address for %%address%%.
* This accepts non-checksum addresses, checksum addresses and
* [[getIcapAddress]] formats.
*
* The checksum in Ethereum uses the capitalization (upper-case
* vs lower-case) of the characters within an address to encode
* its checksum, which offers, on average, a checksum of 15-bits.
*
* If %%address%% contains both upper-case and lower-case, it is
* assumed to already be a checksum address and its checksum is
* validated, and if the address fails its expected checksum an
* error is thrown.
*
* If you wish the checksum of %%address%% to be ignore, it should
* be converted to lower-case (i.e. ``.toLowercase()``) before
* being passed in. This should be a very rare situation though,
* that you wish to bypass the safegaurds in place to protect
* against an address that has been incorrectly copied from another
* source.
*
* @example:
* // Adds the checksum (via upper-casing specific letters)
* getAddress("0x8ba1f109551bd432803012645ac136ddd64dba72")
* //_result:
*
* // Converts ICAP address and adds checksum
* getAddress("XE65GB6LDNXYOFTX0NSV3FUWKOWIXAMJK36");
* //_result:
*
* // Throws an error if an address contains mixed case,
* // but the checksum fails
* getAddress("0x8Ba1f109551bD432803012645Ac136ddd64DBA72")
* //_error:
*/
export function getAddress(address: string): string {
assertArgument(typeof(address) === "string", "invalid address", "address", address);
if (address.match(/^(0x)?[0-9a-fA-F]{40}$/)) {
// Missing the 0x prefix
if (!address.startsWith("0x")) { address = "0x" + address; }
const result = getChecksumAddress(address);
// It is a checksummed address with a bad checksum
assertArgument(!address.match(/([A-F].*[a-f])|([a-f].*[A-F])/) || result === address,
"bad address checksum", "address", address);
return result;
}
// Maybe ICAP? (we only support direct mode)
if (address.match(/^XE[0-9]{2}[0-9A-Za-z]{30,31}$/)) {
// It is an ICAP address with a bad checksum
assertArgument(address.substring(2, 4) === ibanChecksum(address), "bad icap checksum", "address", address);
let result = fromBase36(address.substring(4)).toString(16);
while (result.length < 40) { result = "0" + result; }
return getChecksumAddress("0x" + result);
}
assertArgument(false, "invalid address", "address", address);
}
/**
* The [ICAP Address format](link-icap) format is an early checksum
* format which attempts to be compatible with the banking
* industry [IBAN format](link-wiki-iban) for bank accounts.
*
* It is no longer common or a recommended format.
*
* @example:
* getIcapAddress("0x8ba1f109551bd432803012645ac136ddd64dba72");
* //_result:
*
* getIcapAddress("XE65GB6LDNXYOFTX0NSV3FUWKOWIXAMJK36");
* //_result:
*
* // Throws an error if the ICAP checksum is wrong
* getIcapAddress("XE65GB6LDNXYOFTX0NSV3FUWKOWIXAMJK37");
* //_error:
*/
export function getIcapAddress(address: string): string {
//let base36 = _base16To36(getAddress(address).substring(2)).toUpperCase();
let base36 = BigInt(getAddress(address)).toString(36).toUpperCase();
while (base36.length < 30) { base36 = "0" + base36; }
return "XE" + ibanChecksum("XE00" + base36) + base36;
}

123
dev/env/node_modules/ethers/src.ts/address/checks.ts generated vendored Executable file
View File

@@ -0,0 +1,123 @@
import { assert, assertArgument } from "../utils/index.js";
import { getAddress } from "./address.js";
import type { Addressable, AddressLike, NameResolver } from "./index.js";
/**
* Returns true if %%value%% is an object which implements the
* [[Addressable]] interface.
*
* @example:
* // Wallets and AbstractSigner sub-classes
* isAddressable(Wallet.createRandom())
* //_result:
*
* // Contracts
* contract = new Contract("dai.tokens.ethers.eth", [ ], provider)
* isAddressable(contract)
* //_result:
*/
export function isAddressable(value: any): value is Addressable {
return (value && typeof(value.getAddress) === "function");
}
/**
* Returns true if %%value%% is a valid address.
*
* @example:
* // Valid address
* isAddress("0x8ba1f109551bD432803012645Ac136ddd64DBA72")
* //_result:
*
* // Valid ICAP address
* isAddress("XE65GB6LDNXYOFTX0NSV3FUWKOWIXAMJK36")
* //_result:
*
* // Invalid checksum
* isAddress("0x8Ba1f109551bD432803012645Ac136ddd64DBa72")
* //_result:
*
* // Invalid ICAP checksum
* isAddress("0x8Ba1f109551bD432803012645Ac136ddd64DBA72")
* //_result:
*
* // Not an address (an ENS name requires a provided and an
* // asynchronous API to access)
* isAddress("ricmoo.eth")
* //_result:
*/
export function isAddress(value: any): value is string {
try {
getAddress(value);
return true;
} catch (error) { }
return false;
}
async function checkAddress(target: any, promise: Promise<null | string>): Promise<string> {
const result = await promise;
if (result == null || result === "0x0000000000000000000000000000000000000000") {
assert(typeof(target) !== "string", "unconfigured name", "UNCONFIGURED_NAME", { value: target });
assertArgument(false, "invalid AddressLike value; did not resolve to a value address", "target", target);
}
return getAddress(result);
}
/**
* Resolves to an address for the %%target%%, which may be any
* supported address type, an [[Addressable]] or a Promise which
* resolves to an address.
*
* If an ENS name is provided, but that name has not been correctly
* configured a [[UnconfiguredNameError]] is thrown.
*
* @example:
* addr = "0x6B175474E89094C44Da98b954EedeAC495271d0F"
*
* // Addresses are return synchronously
* resolveAddress(addr, provider)
* //_result:
*
* // Address promises are resolved asynchronously
* resolveAddress(Promise.resolve(addr))
* //_result:
*
* // ENS names are resolved asynchronously
* resolveAddress("dai.tokens.ethers.eth", provider)
* //_result:
*
* // Addressable objects are resolved asynchronously
* contract = new Contract(addr, [ ])
* resolveAddress(contract, provider)
* //_result:
*
* // Unconfigured ENS names reject
* resolveAddress("nothing-here.ricmoo.eth", provider)
* //_error:
*
* // ENS names require a NameResolver object passed in
* // (notice the provider was omitted)
* resolveAddress("nothing-here.ricmoo.eth")
* //_error:
*/
export function resolveAddress(target: AddressLike, resolver?: null | NameResolver): string | Promise<string> {
if (typeof(target) === "string") {
if (target.match(/^0x[0-9a-f]{40}$/i)) { return getAddress(target); }
assert(resolver != null, "ENS resolution requires a provider",
"UNSUPPORTED_OPERATION", { operation: "resolveName" });
return checkAddress(target, resolver.resolveName(target));
} else if (isAddressable(target)) {
return checkAddress(target, target.getAddress());
} else if (target && typeof(target.then) === "function") {
return checkAddress(target, target);
}
assertArgument(false, "unsupported addressable value", "target", target);
}

View File

@@ -0,0 +1,80 @@
import { keccak256 } from "../crypto/index.js";
import {
concat, dataSlice, getBigInt, getBytes, encodeRlp, assertArgument
} from "../utils/index.js";
import { getAddress } from "./address.js";
import type { BigNumberish, BytesLike } from "../utils/index.js";
// http://ethereum.stackexchange.com/questions/760/how-is-the-address-of-an-ethereum-contract-computed
/**
* Returns the address that would result from a ``CREATE`` for %%tx%%.
*
* This can be used to compute the address a contract will be
* deployed to by an EOA when sending a deployment transaction (i.e.
* when the ``to`` address is ``null``).
*
* This can also be used to compute the address a contract will be
* deployed to by a contract, by using the contract's address as the
* ``to`` and the contract's nonce.
*
* @example
* from = "0x8ba1f109551bD432803012645Ac136ddd64DBA72";
* nonce = 5;
*
* getCreateAddress({ from, nonce });
* //_result:
*/
export function getCreateAddress(tx: { from: string, nonce: BigNumberish }): string {
const from = getAddress(tx.from);
const nonce = getBigInt(tx.nonce, "tx.nonce");
let nonceHex = nonce.toString(16);
if (nonceHex === "0") {
nonceHex = "0x";
} else if (nonceHex.length % 2) {
nonceHex = "0x0" + nonceHex;
} else {
nonceHex = "0x" + nonceHex;
}
return getAddress(dataSlice(keccak256(encodeRlp([ from, nonceHex ])), 12));
}
/**
* Returns the address that would result from a ``CREATE2`` operation
* with the given %%from%%, %%salt%% and %%initCodeHash%%.
*
* To compute the %%initCodeHash%% from a contract's init code, use
* the [[keccak256]] function.
*
* For a quick overview and example of ``CREATE2``, see [[link-ricmoo-wisps]].
*
* @example
* // The address of the contract
* from = "0x8ba1f109551bD432803012645Ac136ddd64DBA72"
*
* // The salt
* salt = id("HelloWorld")
*
* // The hash of the initCode
* initCode = "0x6394198df16000526103ff60206004601c335afa6040516060f3";
* initCodeHash = keccak256(initCode)
*
* getCreate2Address(from, salt, initCodeHash)
* //_result:
*/
export function getCreate2Address(_from: string, _salt: BytesLike, _initCodeHash: BytesLike): string {
const from = getAddress(_from);
const salt = getBytes(_salt, "salt");
const initCodeHash = getBytes(_initCodeHash, "initCodeHash");
assertArgument(salt.length === 32, "salt must be 32 bytes", "salt", _salt);
assertArgument(initCodeHash.length === 32, "initCodeHash must be 32 bytes", "initCodeHash", _initCodeHash);
return getAddress(dataSlice(keccak256(concat([ "0xff", from, salt, initCodeHash ])), 12))
}

57
dev/env/node_modules/ethers/src.ts/address/index.ts generated vendored Executable file
View File

@@ -0,0 +1,57 @@
/**
* Addresses are a fundamental part of interacting with Ethereum. They
* represent the global identity of Externally Owned Accounts (accounts
* backed by a private key) and contracts.
*
* The Ethereum Naming Service (ENS) provides an interconnected ecosystem
* of contracts, standards and libraries which enable looking up an
* address for an ENS name.
*
* These functions help convert between various formats, validate
* addresses and safely resolve ENS names.
*
* @_section: api/address:Addresses [about-addresses]
*/
null;
/**
* An interface for objects which have an address, and can
* resolve it asyncronously.
*
* This allows objects such as [[Signer]] or [[Contract]] to
* be used most places an address can be, for example getting
* the [balance](Provider-getBalance).
*/
export interface Addressable {
/**
* Get the object address.
*/
getAddress(): Promise<string>;
}
/**
* Anything that can be used to return or resolve an address.
*/
export type AddressLike = string | Promise<string> | Addressable;
/**
* An interface for any object which can resolve an ENS name.
*/
export interface NameResolver {
/**
* Resolve to the address for the ENS %%name%%.
*
* Resolves to ``null`` if the name is unconfigued. Use
* [[resolveAddress]] (passing this object as %%resolver%%) to
* throw for names that are unconfigured.
*/
resolveName(name: string): Promise<null | string>;
}
export { getAddress, getIcapAddress } from "./address.js";
export { getCreateAddress, getCreate2Address } from "./contract-address.js";
export { isAddressable, isAddress, resolveAddress } from "./checks.js";

8
dev/env/node_modules/ethers/src.ts/constants/addresses.ts generated vendored Executable file
View File

@@ -0,0 +1,8 @@
/**
* A constant for the zero address.
*
* (**i.e.** ``"0x0000000000000000000000000000000000000000"``)
*/
export const ZeroAddress: string = "0x0000000000000000000000000000000000000000";

7
dev/env/node_modules/ethers/src.ts/constants/hashes.ts generated vendored Executable file
View File

@@ -0,0 +1,7 @@
/**
* A constant for the zero hash.
*
* (**i.e.** ``"0x0000000000000000000000000000000000000000000000000000000000000000"``)
*/
export const ZeroHash: string = "0x0000000000000000000000000000000000000000000000000000000000000000";

16
dev/env/node_modules/ethers/src.ts/constants/index.ts generated vendored Executable file
View File

@@ -0,0 +1,16 @@
/**
* Some common constants useful for Ethereum.
*
* @_section: api/constants: Constants [about-constants]
*/
export { ZeroAddress } from "./addresses.js";
export { ZeroHash } from "./hashes.js";
export {
N,
WeiPerEther,
MaxUint256,
MinInt256,
MaxInt256
} from "./numbers.js";
export { EtherSymbol, MessagePrefix } from "./strings.js";

35
dev/env/node_modules/ethers/src.ts/constants/numbers.ts generated vendored Executable file
View File

@@ -0,0 +1,35 @@
/**
* A constant for the order N for the secp256k1 curve.
*
* (**i.e.** ``0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n``)
*/
export const N: bigint = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
/**
* A constant for the number of wei in a single ether.
*
* (**i.e.** ``1000000000000000000n``)
*/
export const WeiPerEther: bigint = BigInt("1000000000000000000");
/**
* A constant for the maximum value for a ``uint256``.
*
* (**i.e.** ``0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn``)
*/
export const MaxUint256: bigint = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
/**
* A constant for the minimum value for an ``int256``.
*
* (**i.e.** ``-8000000000000000000000000000000000000000000000000000000000000000n``)
*/
export const MinInt256: bigint = BigInt("0x8000000000000000000000000000000000000000000000000000000000000000") * BigInt(-1);
/**
* A constant for the maximum value for an ``int256``.
*
* (**i.e.** ``0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffn``)
*/
export const MaxInt256: bigint = BigInt("0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");

16
dev/env/node_modules/ethers/src.ts/constants/strings.ts generated vendored Executable file
View File

@@ -0,0 +1,16 @@
// NFKC (composed) // (decomposed)
/**
* A constant for the ether symbol (normalized using NFKC).
*
* (**i.e.** ``"\\u039e"``)
*/
export const EtherSymbol: string = "\u039e"; // "\uD835\uDF63";
/**
* A constant for the [[link-eip-191]] personal message prefix.
*
* (**i.e.** ``"\\x19Ethereum Signed Message:\\n"``)
*/
export const MessagePrefix: string = "\x19Ethereum Signed Message:\n";

1120
dev/env/node_modules/ethers/src.ts/contract/contract.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

143
dev/env/node_modules/ethers/src.ts/contract/factory.ts generated vendored Executable file
View File

@@ -0,0 +1,143 @@
import { Interface } from "../abi/index.js";
import { getCreateAddress } from "../address/index.js";
import {
concat, defineProperties, getBytes, hexlify,
assert, assertArgument
} from "../utils/index.js";
import { BaseContract, copyOverrides, resolveArgs } from "./contract.js";
import type { InterfaceAbi } from "../abi/index.js";
import type { Addressable } from "../address/index.js";
import type { ContractRunner } from "../providers/index.js";
import type { BytesLike } from "../utils/index.js";
import type {
ContractInterface, ContractMethodArgs, ContractDeployTransaction,
} from "./types.js";
import type { ContractTransactionResponse } from "./wrappers.js";
// A = Arguments to the constructor
// 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> {
/**
* The Contract Interface.
*/
readonly interface!: Interface;
/**
* The Contract deployment bytecode. Often called the initcode.
*/
readonly bytecode!: string;
/**
* The ContractRunner to deploy the Contract as.
*/
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) {
const iface = Interface.from(abi);
// Dereference Solidity bytecode objects and allow a missing `0x`-prefix
if (bytecode instanceof Uint8Array) {
bytecode = hexlify(getBytes(bytecode));
} else {
if (typeof(bytecode) === "object") { bytecode = bytecode.object; }
if (!bytecode.startsWith("0x")) { bytecode = "0x" + bytecode; }
bytecode = hexlify(getBytes(bytecode));
}
defineProperties<ContractFactory>(this, {
bytecode, interface: iface, runner: (runner || null)
});
}
attach(target: string | Addressable): BaseContract & Omit<I, keyof BaseContract> {
return new (<any>BaseContract)(target, this.interface, this.runner);
}
/**
* Resolves to the transaction to deploy the contract, passing %%args%%
* into the constructor.
*/
async getDeployTransaction(...args: ContractMethodArgs<A>): Promise<ContractDeployTransaction> {
let overrides: Omit<ContractDeployTransaction, "data"> = { };
const fragment = this.interface.deploy;
if (fragment.inputs.length + 1 === args.length) {
overrides = await copyOverrides(args.pop());
}
if (fragment.inputs.length !== args.length) {
throw new Error("incorrect number of arguments to constructor");
}
const resolvedArgs = await resolveArgs(this.runner, fragment.inputs, args);
const data = concat([ this.bytecode, this.interface.encodeDeploy(resolvedArgs) ]);
return Object.assign({ }, overrides, { data });
}
/**
* Resolves to the Contract deployed by passing %%args%% into the
* constructor.
*
* This will resolve 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>> {
const tx = await this.getDeployTransaction(...args);
assert(this.runner && typeof(this.runner.sendTransaction) === "function",
"factory runner does not support sending transactions", "UNSUPPORTED_OPERATION", {
operation: "sendTransaction" });
const sentTx = await this.runner.sendTransaction(tx);
const address = getCreateAddress(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> {
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> {
assertArgument(output != null, "bad compiler output", "output", output);
if (typeof(output) === "string") { output = JSON.parse(output); }
const abi = output.abi;
let bytecode = "";
if (output.bytecode) {
bytecode = output.bytecode;
} else if (output.evm && output.evm.bytecode) {
bytecode = output.evm.bytecode;
}
return new this(abi, bytecode, runner);
}
}

31
dev/env/node_modules/ethers/src.ts/contract/index.ts generated vendored Executable file
View File

@@ -0,0 +1,31 @@
/**
* 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]
*/
export {
BaseContract, Contract
} from "./contract.js";
export {
ContractFactory
} from "./factory.js";
export {
ContractEventPayload, ContractUnknownEventPayload,
ContractTransactionReceipt, ContractTransactionResponse,
EventLog, UndecodedEventLog
} from "./wrappers.js";
export type {
BaseContractMethod, ConstantContractMethod,
PostfixOverrides,
ContractEvent, ContractEventArgs, ContractEventName,
ContractDeployTransaction,
ContractInterface, ContractMethod, ContractMethodArgs, ContractTransaction,
DeferredTopicFilter, Overrides,
WrappedFallback
} from "./types.js";

236
dev/env/node_modules/ethers/src.ts/contract/types.ts generated vendored Executable file
View File

@@ -0,0 +1,236 @@
import type {
EventFragment, FunctionFragment, Result, Typed
} from "../abi/index.js";
import type {
TransactionRequest, PreparedTransactionRequest, TopicFilter
} from "../providers/index.js";
import type { ContractTransactionResponse } from "./wrappers.js";
/**
* 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
* signature and dereference each parameter when calling the listener.
*/
export type ContractEventName = string | ContractEvent | TopicFilter | DeferredTopicFilter;
/**
* A Contract with no method constraints.
*/
export interface ContractInterface {
[ name: string ]: BaseContractMethod;
};
/**
* When creating a filter using the ``contract.filters``, this is returned.
*/
export interface DeferredTopicFilter {
getTopicFilter(): Promise<TopicFilter>;
fragment: EventFragment;
}
/**
* When populating a transaction this type is returned.
*/
export interface ContractTransaction extends PreparedTransactionRequest {
/**
* The target address.
*/
to: string;
/**
* The transaction data.
*/
data: string;
/**
* The from address, if any.
*/
from?: string;
}
/**
* A deployment transaction for a contract.
*/
export interface ContractDeployTransaction extends Omit<ContractTransaction, "to"> { }
/**
* The overrides for a contract transaction.
*/
export interface Overrides extends Omit<TransactionRequest, "to" | "data"> { };
/**
* 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 ];
/**
* 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 }>;
// A = Arguments passed in as a tuple
// R = The result type of the call (i.e. if only one return type,
// the qualified type, otherwise Result)
// D = The type the default call will return (i.e. R for view/pure,
// 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> {
(...args: ContractMethodArgs<A>): Promise<D>;
/**
* The name of the Contract method.
*/
name: string;
/**
* The fragment of the Contract method. This will throw on ambiguous
* method names.
*/
fragment: FunctionFragment;
/**
* Returns the fragment constrained by %%args%%. This can be used to
* resolve ambiguous method names.
*/
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>;
/**
* 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>;
/**
* Send a transaction for the contract method with %%args%%.
*/
send(...args: ContractMethodArgs<A>): Promise<ContractTransactionResponse>;
/**
* Estimate the gas to send the contract method with %%args%%.
*/
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>;
}
/**
* A contract method on a Contract.
*/
export interface ContractMethod<
A extends Array<any> = Array<any>,
R = any,
D extends R | ContractTransactionResponse = R | ContractTransactionResponse
> extends BaseContractMethod<A, R, D> { }
/**
* A pure of view method on a Contract.
*/
export interface ConstantContractMethod<
A extends Array<any>,
R = any
> extends ContractMethod<A, R, R> { }
/**
* 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 interface ContractEvent<A extends Array<any> = Array<any>> {
(...args: ContractEventArgs<A>): DeferredTopicFilter;
/**
* The name of the Contract event.
*/
name: string;
/**
* The fragment of the Contract event. This will throw on ambiguous
* method names.
*/
fragment: EventFragment;
/**
* Returns the fragment constrained by %%args%%. This can be used to
* resolve ambiguous event names.
*/
getFragment(...args: ContractEventArgs<A>): EventFragment;
};
/**
* A Fallback or Receive function on a Contract.
*/
export interface WrappedFallback {
(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>;
/**
* Call the contract fallback and return the result.
*
* For non-receive fallback, ``data`` may be overridden.
*/
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>;
/**
* 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>;
}

225
dev/env/node_modules/ethers/src.ts/contract/wrappers.ts generated vendored Executable file
View File

@@ -0,0 +1,225 @@
// import from provider.ts instead of index.ts to prevent circular dep
// from EtherscanProvider
import {
Block, Log, TransactionReceipt, TransactionResponse
} from "../providers/provider.js";
import { defineProperties, EventPayload } from "../utils/index.js";
import type { EventFragment, Interface, Result } from "../abi/index.js";
import type { Listener } from "../utils/index.js";
import type {
Provider
} from "../providers/index.js";
import type { BaseContract } from "./contract.js";
import type { ContractEventName } from "./types.js";
/**
* An **EventLog** contains additional properties parsed from the [[Log]].
*/
export class EventLog extends Log {
/**
* The Contract Interface.
*/
readonly interface!: Interface;
/**
* The matching event.
*/
readonly fragment!: EventFragment;
/**
* The parsed arguments passed to the event by ``emit``.
*/
readonly args!: Result;
/**
* @_ignore:
*/
constructor(log: Log, iface: Interface, fragment: EventFragment) {
super(log, log.provider);
const args = iface.decodeEventLog(fragment, log.data, log.topics);
defineProperties<EventLog>(this, { args, fragment, interface: iface });
}
/**
* The name of the event.
*/
get eventName(): string { return this.fragment.name; }
/**
* The signature of the event.
*/
get eventSignature(): string { return this.fragment.format(); }
}
/**
* An **EventLog** contains additional properties parsed from the [[Log]].
*/
export class UndecodedEventLog extends Log {
/**
* The error encounted when trying to decode the log.
*/
readonly error!: Error;
/**
* @_ignore:
*/
constructor(log: Log, error: Error) {
super(log, log.provider);
defineProperties<UndecodedEventLog>(this, { error });
}
}
/**
* A **ContractTransactionReceipt** includes the parsed logs from a
* [[TransactionReceipt]].
*/
export class ContractTransactionReceipt extends TransactionReceipt {
readonly #iface: Interface;
/**
* @_ignore:
*/
constructor(iface: Interface, provider: Provider, tx: TransactionReceipt) {
super(tx, provider);
this.#iface = iface;
}
/**
* The parsed logs for any [[Log]] which has a matching event in the
* Contract ABI.
*/
get logs(): Array<EventLog | Log> {
return super.logs.map((log) => {
const fragment = log.topics.length ? this.#iface.getEvent(log.topics[0]): null;
if (fragment) {
try {
return new EventLog(log, this.#iface, fragment)
} catch (error: any) {
return new UndecodedEventLog(log, error);
}
}
return log;
});
}
}
/**
* A **ContractTransactionResponse** will return a
* [[ContractTransactionReceipt]] when waited on.
*/
export class ContractTransactionResponse extends TransactionResponse {
readonly #iface: Interface;
/**
* @_ignore:
*/
constructor(iface: Interface, provider: Provider, tx: TransactionResponse) {
super(tx, provider);
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, timeout?: number): Promise<null | ContractTransactionReceipt> {
const receipt = await super.wait(confirms, timeout);
if (receipt == null) { return null; }
return new ContractTransactionReceipt(this.#iface, this.provider, receipt);
}
}
/**
* 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> {
/**
* The log with no matching events.
*/
readonly log!: Log;
/**
* @_event:
*/
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, log: Log) {
super(contract, listener, filter);
defineProperties<ContractUnknownEventPayload>(this, { log });
}
/**
* Resolves to the block the event occured in.
*/
async getBlock(): Promise<Block> {
return await this.log.getBlock();
}
/**
* Resolves to the transaction the event occured in.
*/
async getTransaction(): Promise<TransactionResponse> {
return await this.log.getTransaction();
}
/**
* Resolves to the transaction receipt the event occured in.
*/
async getTransactionReceipt(): Promise<TransactionReceipt> {
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 {
/**
* The matching event.
*/
declare readonly fragment: EventFragment;
/**
* The log, with parsed properties.
*/
declare readonly log: EventLog;
/**
* The parsed arguments passed to the event by ``emit``.
*/
declare readonly args: Result;
/**
* @_ignore:
*/
constructor(contract: BaseContract, listener: null | Listener, filter: ContractEventName, fragment: EventFragment, _log: Log) {
super(contract, listener, filter, new EventLog(_log, contract.interface, fragment));
const args = contract.interface.decodeEventLog(fragment, this.log.data, this.log.topics);
defineProperties<ContractEventPayload>(this, { args, fragment });
}
/**
* The event name.
*/
get eventName(): string {
return this.fragment.name;
}
/**
* The event signature.
*/
get eventSignature(): string {
return this.fragment.format();
}
}

64
dev/env/node_modules/ethers/src.ts/crypto/crypto-browser.ts generated vendored Executable file
View File

@@ -0,0 +1,64 @@
/* Browser Crypto Shims */
import { hmac } from "@noble/hashes/hmac";
import { pbkdf2 } from "@noble/hashes/pbkdf2";
import { sha256 } from "@noble/hashes/sha256";
import { sha512 } from "@noble/hashes/sha512";
import { assert, assertArgument } from "../utils/index.js";
declare global {
interface Window { }
const window: Window;
const self: Window;
}
function getGlobal(): any {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
const anyGlobal = getGlobal();
const crypto: any = anyGlobal.crypto || anyGlobal.msCrypto;
export interface CryptoHasher {
update(data: Uint8Array): CryptoHasher;
digest(): Uint8Array;
}
export function createHash(algo: string): CryptoHasher {
switch (algo) {
case "sha256": return sha256.create();
case "sha512": return sha512.create();
}
assertArgument(false, "invalid hashing algorithm name", "algorithm", algo);
}
export function createHmac(_algo: string, key: Uint8Array): CryptoHasher {
const algo = ({ sha256, sha512 }[_algo]);
assertArgument(algo != null, "invalid hmac algorithm", "algorithm", _algo);
return hmac.create(algo, key);
}
export function pbkdf2Sync(password: Uint8Array, salt: Uint8Array, iterations: number, keylen: number, _algo: "sha256" | "sha512"): Uint8Array {
const algo = ({ sha256, sha512 }[_algo]);
assertArgument(algo != null, "invalid pbkdf2 algorithm", "algorithm", _algo);
return pbkdf2(algo, password, salt, { c: iterations, dkLen: keylen });
}
export function randomBytes(length: number): Uint8Array {
assert(crypto != null, "platform does not support secure random numbers", "UNSUPPORTED_OPERATION", {
operation: "randomBytes" });
assertArgument(Number.isInteger(length) && length > 0 && length <= 1024, "invalid length", "length", length);
const result = new Uint8Array(length);
crypto.getRandomValues(result);
return result;
}

4
dev/env/node_modules/ethers/src.ts/crypto/crypto.ts generated vendored Executable file
View File

@@ -0,0 +1,4 @@
export {
createHash, createHmac, pbkdf2Sync, randomBytes
} from "crypto";

51
dev/env/node_modules/ethers/src.ts/crypto/hmac.ts generated vendored Executable file
View File

@@ -0,0 +1,51 @@
/**
* An **HMAC** enables verification that a given key was used
* to authenticate a payload.
*
* See: [[link-wiki-hmac]]
*
* @_subsection: api/crypto:HMAC [about-hmac]
*/
import { createHmac } from "./crypto.js";
import { getBytes, hexlify } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
let locked = false;
const _computeHmac = function(algorithm: "sha256" | "sha512", key: Uint8Array, data: Uint8Array): BytesLike {
return createHmac(algorithm, key).update(data).digest();
}
let __computeHmac = _computeHmac;
/**
* Return the HMAC for %%data%% using the %%key%% key with the underlying
* %%algo%% used for compression.
*
* @example:
* key = id("some-secret")
*
* // Compute the HMAC
* computeHmac("sha256", key, "0x1337")
* //_result:
*
* // To compute the HMAC of UTF-8 data, the data must be
* // converted to UTF-8 bytes
* computeHmac("sha256", key, toUtf8Bytes("Hello World"))
* //_result:
*
*/
export function computeHmac(algorithm: "sha256" | "sha512", _key: BytesLike, _data: BytesLike): string {
const key = getBytes(_key, "key");
const data = getBytes(_data, "data");
return hexlify(__computeHmac(algorithm, key, data));
}
computeHmac._ = _computeHmac;
computeHmac.lock = function() { locked = true; }
computeHmac.register = function(func: (algorithm: "sha256" | "sha512", key: Uint8Array, data: Uint8Array) => BytesLike) {
if (locked) { throw new Error("computeHmac is locked"); }
__computeHmac = func;
}
Object.freeze(computeHmac);

59
dev/env/node_modules/ethers/src.ts/crypto/index.ts generated vendored Executable file
View File

@@ -0,0 +1,59 @@
/**
* A fundamental building block of Ethereum is the underlying
* cryptographic primitives.
*
* @_section: api/crypto:Cryptographic Functions [about-crypto]
*/
null
// We import all these so we can export lock()
import { computeHmac } from "./hmac.js";
import { keccak256 } from "./keccak.js";
import { ripemd160 } from "./ripemd160.js";
import { pbkdf2 } from "./pbkdf2.js";
import { randomBytes } from "./random.js";
import { scrypt, scryptSync } from "./scrypt.js";
import { sha256, sha512 } from "./sha2.js";
export {
computeHmac,
randomBytes,
keccak256,
ripemd160,
sha256, sha512,
pbkdf2,
scrypt, scryptSync
};
export { SigningKey } from "./signing-key.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 {
computeHmac.lock();
keccak256.lock();
pbkdf2.lock();
randomBytes.lock();
ripemd160.lock();
scrypt.lock();
scryptSync.lock();
sha256.lock();
sha512.lock();
randomBytes.lock();
}
export { lock };
/////////////////////////////
// Types
export type { ProgressCallback } from "./scrypt.js";
export type { SignatureLike } from "./signature.js";

54
dev/env/node_modules/ethers/src.ts/crypto/keccak.ts generated vendored Executable file
View File

@@ -0,0 +1,54 @@
/**
* Cryptographic hashing functions
*
* @_subsection: api/crypto:Hash Functions [about-crypto-hashing]
*/
import { keccak_256 } from "@noble/hashes/sha3";
import { getBytes, hexlify } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
let locked = false;
const _keccak256 = function(data: Uint8Array): Uint8Array {
return keccak_256(data);
}
let __keccak256: (data: Uint8Array) => BytesLike = _keccak256;
/**
* Compute the cryptographic KECCAK256 hash of %%data%%.
*
* The %%data%% **must** be a data representation, to compute the
* hash of UTF-8 data use the [[id]] function.
*
* @returns DataHexstring
* @example:
* keccak256("0x")
* //_result:
*
* keccak256("0x1337")
* //_result:
*
* keccak256(new Uint8Array([ 0x13, 0x37 ]))
* //_result:
*
* // Strings are assumed to be DataHexString, otherwise it will
* // throw. To hash UTF-8 data, see the note above.
* keccak256("Hello World")
* //_error:
*/
export function keccak256(_data: BytesLike): string {
const data = getBytes(_data, "data");
return hexlify(__keccak256(data));
}
keccak256._ = _keccak256;
keccak256.lock = function(): void { locked = true; }
keccak256.register = function(func: (data: Uint8Array) => BytesLike) {
if (locked) { throw new TypeError("keccak256 is locked"); }
__keccak256 = func;
}
Object.freeze(keccak256);

55
dev/env/node_modules/ethers/src.ts/crypto/pbkdf2.ts generated vendored Executable file
View File

@@ -0,0 +1,55 @@
/**
* A **Password-Based Key-Derivation Function** is designed to create
* a sequence of bytes suitible as a **key** from a human-rememberable
* password.
*
* @_subsection: api/crypto:Passwords [about-pbkdf]
*/
import { pbkdf2Sync } from "./crypto.js";
import { getBytes, hexlify } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
let locked = false;
const _pbkdf2 = function(password: Uint8Array, salt: Uint8Array, iterations: number, keylen: number, algo: "sha256" | "sha512"): BytesLike {
return pbkdf2Sync(password, salt, iterations, keylen, algo);
}
let __pbkdf2 = _pbkdf2;
/**
* Return the [[link-pbkdf2]] for %%keylen%% bytes for %%password%% using
* the %%salt%% and using %%iterations%% of %%algo%%.
*
* This PBKDF is outdated and should not be used in new projects, but is
* required to decrypt older files.
*
* @example:
* // The password must be converted to bytes, and it is generally
* // best practices to ensure the string has been normalized. Many
* // formats explicitly indicate the normalization form to use.
* password = "hello"
* passwordBytes = toUtf8Bytes(password, "NFKC")
*
* salt = id("some-salt")
*
* // Compute the PBKDF2
* pbkdf2(passwordBytes, salt, 1024, 16, "sha256")
* //_result:
*/
export function pbkdf2(_password: BytesLike, _salt: BytesLike, iterations: number, keylen: number, algo: "sha256" | "sha512"): string {
const password = getBytes(_password, "password");
const salt = getBytes(_salt, "salt");
return hexlify(__pbkdf2(password, salt, iterations, keylen, algo));
}
pbkdf2._ = _pbkdf2;
pbkdf2.lock = function(): void { locked = true; }
pbkdf2.register = function(func: (password: Uint8Array, salt: Uint8Array, iterations: number, keylen: number, algo: "sha256" | "sha512") => BytesLike) {
if (locked) { throw new Error("pbkdf2 is locked"); }
__pbkdf2 = func;
}
Object.freeze(pbkdf2);

36
dev/env/node_modules/ethers/src.ts/crypto/random.ts generated vendored Executable file
View File

@@ -0,0 +1,36 @@
/**
* A **Cryptographically Secure Random Value** is one that has been
* generated with additional care take to prevent side-channels
* from allowing others to detect it and prevent others from through
* coincidence generate the same values.
*
* @_subsection: api/crypto:Random Values [about-crypto-random]
*/
import { randomBytes as crypto_random } from "./crypto.js";
let locked = false;
const _randomBytes = function(length: number): Uint8Array {
return new Uint8Array(crypto_random(length));
}
let __randomBytes = _randomBytes;
/**
* Return %%length%% bytes of cryptographically secure random data.
*
* @example:
* randomBytes(8)
* //_result:
*/
export function randomBytes(length: number): Uint8Array {
return __randomBytes(length);
}
randomBytes._ = _randomBytes;
randomBytes.lock = function(): void { locked = true; }
randomBytes.register = function(func: (length: number) => Uint8Array) {
if (locked) { throw new Error("randomBytes is locked"); }
__randomBytes = func;
}
Object.freeze(randomBytes);

43
dev/env/node_modules/ethers/src.ts/crypto/ripemd160.ts generated vendored Executable file
View File

@@ -0,0 +1,43 @@
import { ripemd160 as noble_ripemd160 } from "@noble/hashes/ripemd160";
import { getBytes, hexlify } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
let locked = false;
const _ripemd160 = function(data: Uint8Array): Uint8Array {
return noble_ripemd160(data);
}
let __ripemd160: (data: Uint8Array) => BytesLike = _ripemd160;
/**
* Compute the cryptographic RIPEMD-160 hash of %%data%%.
*
* @_docloc: api/crypto:Hash Functions
* @returns DataHexstring
*
* @example:
* ripemd160("0x")
* //_result:
*
* ripemd160("0x1337")
* //_result:
*
* ripemd160(new Uint8Array([ 0x13, 0x37 ]))
* //_result:
*
*/
export function ripemd160(_data: BytesLike): string {
const data = getBytes(_data, "data");
return hexlify(__ripemd160(data));
}
ripemd160._ = _ripemd160;
ripemd160.lock = function(): void { locked = true; }
ripemd160.register = function(func: (data: Uint8Array) => BytesLike) {
if (locked) { throw new TypeError("ripemd160 is locked"); }
__ripemd160 = func;
}
Object.freeze(ripemd160);

114
dev/env/node_modules/ethers/src.ts/crypto/scrypt.ts generated vendored Executable file
View File

@@ -0,0 +1,114 @@
import { scrypt as _nobleSync, scryptAsync as _nobleAsync } from "@noble/hashes/scrypt";
import { getBytes, hexlify as H } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
/**
* A callback during long-running operations to update any
* UI or provide programatic access to the progress.
*
* The %%percent%% is a value between ``0`` and ``1``.
*
* @_docloc: api/crypto:Passwords
*/
export type ProgressCallback = (percent: number) => void;
let lockedSync = false, lockedAsync = false;
const _scryptAsync = async function(passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number, onProgress?: ProgressCallback) {
return await _nobleAsync(passwd, salt, { N, r, p, dkLen, onProgress });
}
const _scryptSync = function(passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number) {
return _nobleSync(passwd, salt, { N, r, p, dkLen });
}
let __scryptAsync: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number, onProgress?: ProgressCallback) => Promise<BytesLike> = _scryptAsync;
let __scryptSync: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number) => BytesLike = _scryptSync
/**
* The [[link-wiki-scrypt]] uses a memory and cpu hard method of
* derivation to increase the resource cost to brute-force a password
* for a given key.
*
* This means this algorithm is intentionally slow, and can be tuned to
* become slower. As computation and memory speed improve over time,
* increasing the difficulty maintains the cost of an attacker.
*
* For example, if a target time of 5 seconds is used, a legitimate user
* which knows their password requires only 5 seconds to unlock their
* account. A 6 character password has 68 billion possibilities, which
* would require an attacker to invest over 10,000 years of CPU time. This
* is of course a crude example (as password generally aren't random),
* but demonstrates to value of imposing large costs to decryption.
*
* For this reason, if building a UI which involved decrypting or
* encrypting datsa using scrypt, it is recommended to use a
* [[ProgressCallback]] (as event short periods can seem lik an eternity
* if the UI freezes). Including the phrase //"decrypting"// in the UI
* can also help, assuring the user their waiting is for a good reason.
*
* @_docloc: api/crypto:Passwords
*
* @example:
* // The password must be converted to bytes, and it is generally
* // best practices to ensure the string has been normalized. Many
* // formats explicitly indicate the normalization form to use.
* password = "hello"
* passwordBytes = toUtf8Bytes(password, "NFKC")
*
* salt = id("some-salt")
*
* // Compute the scrypt
* scrypt(passwordBytes, salt, 1024, 8, 1, 16)
* //_result:
*/
export async function scrypt(_passwd: BytesLike, _salt: BytesLike, N: number, r: number, p: number, dkLen: number, progress?: ProgressCallback): Promise<string> {
const passwd = getBytes(_passwd, "passwd");
const salt = getBytes(_salt, "salt");
return H(await __scryptAsync(passwd, salt, N, r, p, dkLen, progress));
}
scrypt._ = _scryptAsync;
scrypt.lock = function(): void { lockedAsync = true; }
scrypt.register = function(func: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number, progress?: ProgressCallback) => Promise<BytesLike>) {
if (lockedAsync) { throw new Error("scrypt is locked"); }
__scryptAsync = func;
}
Object.freeze(scrypt);
/**
* Provides a synchronous variant of [[scrypt]].
*
* This will completely lock up and freeze the UI in a browser and will
* prevent any event loop from progressing. For this reason, it is
* preferred to use the [async variant](scrypt).
*
* @_docloc: api/crypto:Passwords
*
* @example:
* // The password must be converted to bytes, and it is generally
* // best practices to ensure the string has been normalized. Many
* // formats explicitly indicate the normalization form to use.
* password = "hello"
* passwordBytes = toUtf8Bytes(password, "NFKC")
*
* salt = id("some-salt")
*
* // Compute the scrypt
* scryptSync(passwordBytes, salt, 1024, 8, 1, 16)
* //_result:
*/
export function scryptSync(_passwd: BytesLike, _salt: BytesLike, N: number, r: number, p: number, dkLen: number): string {
const passwd = getBytes(_passwd, "passwd");
const salt = getBytes(_salt, "salt");
return H(__scryptSync(passwd, salt, N, r, p, dkLen));
}
scryptSync._ = _scryptSync;
scryptSync.lock = function(): void { lockedSync = true; }
scryptSync.register = function(func: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number) => BytesLike) {
if (lockedSync) { throw new Error("scryptSync is locked"); }
__scryptSync = func;
}
Object.freeze(scryptSync);

78
dev/env/node_modules/ethers/src.ts/crypto/sha2.ts generated vendored Executable file
View File

@@ -0,0 +1,78 @@
import { createHash } from "./crypto.js";
import { getBytes, hexlify } from "../utils/index.js";
import type { BytesLike } from "../utils/index.js";
const _sha256 = function(data: Uint8Array): Uint8Array {
return createHash("sha256").update(data).digest();
}
const _sha512 = function(data: Uint8Array): Uint8Array {
return createHash("sha512").update(data).digest();
}
let __sha256: (data: Uint8Array) => BytesLike = _sha256;
let __sha512: (data: Uint8Array) => BytesLike = _sha512;
let locked256 = false, locked512 = false;
/**
* Compute the cryptographic SHA2-256 hash of %%data%%.
*
* @_docloc: api/crypto:Hash Functions
* @returns DataHexstring
*
* @example:
* sha256("0x")
* //_result:
*
* sha256("0x1337")
* //_result:
*
* sha256(new Uint8Array([ 0x13, 0x37 ]))
* //_result:
*
*/
export function sha256(_data: BytesLike): string {
const data = getBytes(_data, "data");
return hexlify(__sha256(data));
}
sha256._ = _sha256;
sha256.lock = function(): void { locked256 = true; }
sha256.register = function(func: (data: Uint8Array) => BytesLike): void {
if (locked256) { throw new Error("sha256 is locked"); }
__sha256 = func;
}
Object.freeze(sha256);
/**
* Compute the cryptographic SHA2-512 hash of %%data%%.
*
* @_docloc: api/crypto:Hash Functions
* @returns DataHexstring
*
* @example:
* sha512("0x")
* //_result:
*
* sha512("0x1337")
* //_result:
*
* sha512(new Uint8Array([ 0x13, 0x37 ]))
* //_result:
*/
export function sha512(_data: BytesLike): string {
const data = getBytes(_data, "data");
return hexlify(__sha512(data));
}
sha512._ = _sha512;
sha512.lock = function(): void { locked512 = true; }
sha512.register = function(func: (data: Uint8Array) => BytesLike): void {
if (locked512) { throw new Error("sha512 is locked"); }
__sha512 = func;
}
Object.freeze(sha256);

410
dev/env/node_modules/ethers/src.ts/crypto/signature.ts generated vendored Executable file
View File

@@ -0,0 +1,410 @@
import { ZeroHash } from "../constants/index.js";
import {
concat, dataLength, getBigInt, getBytes, getNumber, hexlify,
toBeArray, isHexString, zeroPadValue,
assertArgument, assertPrivate
} from "../utils/index.js";
import type {
BigNumberish, BytesLike, Numeric
} from "../utils/index.js";
// Constants
const BN_0 = BigInt(0);
const BN_1 = BigInt(1);
const BN_2 = BigInt(2);
const BN_27 = BigInt(27);
const BN_28 = BigInt(28);
const BN_35 = BigInt(35);
const BN_N = BigInt("0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141");
const BN_N_2 = BN_N / BN_2; // Must be integer (floor) division; do NOT shifts
const inspect = Symbol.for("nodejs.util.inspect.custom");
const _guard = { };
// @TODO: Allow Uint8Array
/**
* A SignatureLike
*
* @_docloc: api/crypto:Signing
*/
export type SignatureLike = Signature | string | {
r: string;
s: string;
v: BigNumberish;
yParity?: 0 | 1;
yParityAndS?: string;
} | {
r: string;
yParityAndS: string;
yParity?: 0 | 1;
s?: string;
v?: number;
} | {
r: string;
s: string;
yParity: 0 | 1;
v?: BigNumberish;
yParityAndS?: string;
};
function toUint256(value: BigNumberish): string {
return zeroPadValue(toBeArray(value), 32);
}
/**
* A Signature @TODO
*
*
* @_docloc: api/crypto:Signing
*/
export class Signature {
#r: string;
#s: string;
#v: 27 | 28;
#networkV: null | bigint;
/**
* The ``r`` value for a signature.
*
* This represents the ``x`` coordinate of a "reference" or
* challenge point, from which the ``y`` can be computed.
*/
get r(): string { return this.#r; }
set r(value: BytesLike) {
assertArgument(dataLength(value) === 32, "invalid r", "value", value);
this.#r = hexlify(value);
}
/**
* The ``s`` value for a signature.
*/
get s(): string {
assertArgument(parseInt(this.#s.substring(0, 3)) < 8, "non-canonical s; use ._s", "s", this.#s);
return this.#s;
}
set s(_value: BytesLike) {
assertArgument(dataLength(_value) === 32, "invalid s", "value", _value);
this.#s = hexlify(_value);
}
/**
* Return the s value, unchecked for EIP-2 compliance.
*
* This should generally not be used and is for situations where
* a non-canonical S value might be relevant, such as Frontier blocks
* that were mined prior to EIP-2 or invalid Authorization List
* signatures.
*/
get _s(): string { return this.#s; }
/**
* Returns true if the Signature is valid for [[link-eip-2]] signatures.
*/
isValid(): boolean {
const s = BigInt(this.#s);
return (s <= BN_N_2);
}
/**
* The ``v`` value for a signature.
*
* Since a given ``x`` value for ``r`` has two possible values for
* its correspondin ``y``, the ``v`` indicates which of the two ``y``
* values to use.
*
* It is normalized to the values ``27`` or ``28`` for legacy
* purposes.
*/
get v(): 27 | 28 { return this.#v; }
set v(value: BigNumberish) {
const v = getNumber(value, "value");
assertArgument(v === 27 || v === 28, "invalid v", "v", value);
this.#v = v;
}
/**
* The EIP-155 ``v`` for legacy transactions. For non-legacy
* transactions, this value is ``null``.
*/
get networkV(): null | bigint { return this.#networkV; }
/**
* The chain ID for EIP-155 legacy transactions. For non-legacy
* transactions, this value is ``null``.
*/
get legacyChainId(): null | bigint {
const v = this.networkV;
if (v == null) { return null; }
return Signature.getChainId(v);
}
/**
* The ``yParity`` for the signature.
*
* See ``v`` for more details on how this value is used.
*/
get yParity(): 0 | 1 {
return (this.v === 27) ? 0: 1;
}
/**
* The [[link-eip-2098]] compact representation of the ``yParity``
* and ``s`` compacted into a single ``bytes32``.
*/
get yParityAndS(): string {
// The EIP-2098 compact representation
const yParityAndS = getBytes(this.s);
if (this.yParity) { yParityAndS[0] |= 0x80; }
return hexlify(yParityAndS);
}
/**
* The [[link-eip-2098]] compact representation.
*/
get compactSerialized(): string {
return concat([ this.r, this.yParityAndS ]);
}
/**
* The serialized representation.
*/
get serialized(): string {
return concat([ this.r, this.s, (this.yParity ? "0x1c": "0x1b") ]);
}
/**
* @private
*/
constructor(guard: any, r: string, s: string, v: 27 | 28) {
assertPrivate(guard, _guard, "Signature");
this.#r = r;
this.#s = s;
this.#v = v;
this.#networkV = null;
}
/**
* Returns the canonical signature.
*
* This is only necessary when dealing with legacy transaction which
* did not enforce canonical S values (i.e. [[link-eip-2]]. Most
* developers should never require this.
*/
getCanonical(): Signature {
if (this.isValid()) { return this; }
// Compute the canonical signature; s' = N - s, v = !v
const s = BN_N - BigInt(this._s);
const v = <27 | 28>(55 - this.v);
const result = new Signature(_guard, this.r, toUint256(s), v);
// Populate the networkV if necessary
if (this.networkV) { result.#networkV = this.networkV; }
return result;
}
/**
* Returns a new identical [[Signature]].
*/
clone(): Signature {
const clone = new Signature(_guard, this.r, this._s, this.v);
if (this.networkV) { clone.#networkV = this.networkV; }
return clone;
}
/**
* Returns a representation that is compatible with ``JSON.stringify``.
*/
toJSON(): any {
const networkV = this.networkV;
return {
_type: "signature",
networkV: ((networkV != null) ? networkV.toString(): null),
r: this.r, s: this._s, v: this.v,
};
}
[inspect](): string {
return this.toString();
}
toString(): string {
if (this.isValid()) {
return `Signature { r: ${ this.r }, s: ${ this._s }, v: ${ this.v } }`;
}
return `Signature { r: ${ this.r }, s: ${ this._s }, v: ${ this.v }, valid: false }`;
}
/**
* Compute the chain ID from the ``v`` in a legacy EIP-155 transactions.
*
* @example:
* Signature.getChainId(45)
* //_result:
*
* Signature.getChainId(46)
* //_result:
*/
static getChainId(v: BigNumberish): bigint {
const bv = getBigInt(v, "v");
// The v is not an EIP-155 v, so it is the unspecified chain ID
if ((bv == BN_27) || (bv == BN_28)) { return BN_0; }
// Bad value for an EIP-155 v
assertArgument(bv >= BN_35, "invalid EIP-155 v", "v", v);
return (bv - BN_35) / BN_2;
}
/**
* Compute the ``v`` for a chain ID for a legacy EIP-155 transactions.
*
* Legacy transactions which use [[link-eip-155]] hijack the ``v``
* property to include the chain ID.
*
* @example:
* Signature.getChainIdV(5, 27)
* //_result:
*
* Signature.getChainIdV(5, 28)
* //_result:
*
*/
static getChainIdV(chainId: BigNumberish, v: 27 | 28): bigint {
return (getBigInt(chainId) * BN_2) + BigInt(35 + v - 27);
}
/**
* Compute the normalized legacy transaction ``v`` from a ``yParirty``,
* a legacy transaction ``v`` or a legacy [[link-eip-155]] transaction.
*
* @example:
* // The values 0 and 1 imply v is actually yParity
* Signature.getNormalizedV(0)
* //_result:
*
* // Legacy non-EIP-1559 transaction (i.e. 27 or 28)
* Signature.getNormalizedV(27)
* //_result:
*
* // Legacy EIP-155 transaction (i.e. >= 35)
* Signature.getNormalizedV(46)
* //_result:
*
* // Invalid values throw
* Signature.getNormalizedV(5)
* //_error:
*/
static getNormalizedV(v: BigNumberish): 27 | 28 {
const bv = getBigInt(v);
if (bv === BN_0 || bv === BN_27) { return 27; }
if (bv === BN_1 || bv === BN_28) { return 28; }
assertArgument(bv >= BN_35, "invalid v", "v", v);
// Otherwise, EIP-155 v means odd is 27 and even is 28
return (bv & BN_1) ? 27: 28;
}
/**
* Creates a new [[Signature]].
*
* If no %%sig%% is provided, a new [[Signature]] is created
* with default values.
*
* If %%sig%% is a string, it is parsed.
*/
static from(sig?: SignatureLike): Signature {
function assertError(check: unknown, message: string): asserts check {
assertArgument(check, message, "signature", sig);
};
if (sig == null) {
return new Signature(_guard, ZeroHash, ZeroHash, 27);
}
if (typeof(sig) === "string") {
const bytes = getBytes(sig, "signature");
if (bytes.length === 64) {
const r = hexlify(bytes.slice(0, 32));
const s = bytes.slice(32, 64);
const v = (s[0] & 0x80) ? 28: 27;
s[0] &= 0x7f;
return new Signature(_guard, r, hexlify(s), v);
}
if (bytes.length === 65) {
const r = hexlify(bytes.slice(0, 32));
const s = hexlify(bytes.slice(32, 64));
const v = Signature.getNormalizedV(bytes[64]);
return new Signature(_guard, r, s, v);
}
assertError(false, "invalid raw signature length");
}
if (sig instanceof Signature) { return sig.clone(); }
// Get r
const _r = sig.r;
assertError(_r != null, "missing r");
const r = toUint256(_r);
// Get s; by any means necessary (we check consistency below)
const s = (function(s?: string, yParityAndS?: string) {
if (s != null) { return toUint256(s); }
if (yParityAndS != null) {
assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
const bytes = getBytes(yParityAndS);
bytes[0] &= 0x7f;
return hexlify(bytes);
}
assertError(false, "missing s");
})(sig.s, sig.yParityAndS);
// Get v; by any means necessary (we check consistency below)
const { networkV, v } = (function(_v?: BigNumberish, yParityAndS?: string, yParity?: Numeric): { networkV?: bigint, v: 27 | 28 } {
if (_v != null) {
const v = getBigInt(_v);
return {
networkV: ((v >= BN_35) ? v: undefined),
v: Signature.getNormalizedV(v)
};
}
if (yParityAndS != null) {
assertError(isHexString(yParityAndS, 32), "invalid yParityAndS");
return { v: ((getBytes(yParityAndS)[0] & 0x80) ? 28: 27) };
}
if (yParity != null) {
switch (getNumber(yParity, "sig.yParity")) {
case 0: return { v: 27 };
case 1: return { v: 28 };
}
assertError(false, "invalid yParity");
}
assertError(false, "missing v");
})(sig.v, sig.yParityAndS, sig.yParity);
const result = new Signature(_guard, r, s, v);
if (networkV) { result.#networkV = networkV; }
// If multiple of v, yParity, yParityAndS we given, check they match
assertError(sig.yParity == null || getNumber(sig.yParity, "sig.yParity") === result.yParity, "yParity mismatch");
assertError(sig.yParityAndS == null || sig.yParityAndS === result.yParityAndS, "yParityAndS mismatch");
return result;
}
}

196
dev/env/node_modules/ethers/src.ts/crypto/signing-key.ts generated vendored Executable file
View File

@@ -0,0 +1,196 @@
/**
* Add details about signing here.
*
* @_subsection: api/crypto:Signing [about-signing]
*/
import { secp256k1 } from "@noble/curves/secp256k1";
import {
concat, dataLength, getBytes, getBytesCopy, hexlify, toBeHex,
assertArgument
} from "../utils/index.js";
import { Signature } from "./signature.js";
import type { BytesLike } from "../utils/index.js";
import type { SignatureLike } from "./index.js";
/**
* A **SigningKey** provides high-level access to the elliptic curve
* cryptography (ECC) operations and key management.
*/
export class SigningKey {
#privateKey: string;
/**
* Creates a new **SigningKey** for %%privateKey%%.
*/
constructor(privateKey: BytesLike) {
assertArgument(dataLength(privateKey) === 32, "invalid private key", "privateKey", "[REDACTED]");
this.#privateKey = hexlify(privateKey);
}
/**
* The private key.
*/
get privateKey(): string { return this.#privateKey; }
/**
* The uncompressed public key.
*
* This will always begin with the prefix ``0x04`` and be 132
* characters long (the ``0x`` prefix and 130 hexadecimal nibbles).
*/
get publicKey(): string { return SigningKey.computePublicKey(this.#privateKey); }
/**
* The compressed public key.
*
* This will always begin with either the prefix ``0x02`` or ``0x03``
* and be 68 characters long (the ``0x`` prefix and 33 hexadecimal
* nibbles)
*/
get compressedPublicKey(): string { return SigningKey.computePublicKey(this.#privateKey, true); }
/**
* Return the signature of the signed %%digest%%.
*/
sign(digest: BytesLike): Signature {
assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
const sig = secp256k1.sign(getBytesCopy(digest), getBytesCopy(this.#privateKey), {
lowS: true
});
return Signature.from({
r: toBeHex(sig.r, 32),
s: toBeHex(sig.s, 32),
v: (sig.recovery ? 0x1c: 0x1b)
});
}
/**
* Returns the [[link-wiki-ecdh]] shared secret between this
* private key and the %%other%% key.
*
* The %%other%% key may be any type of key, a raw public key,
* a compressed/uncompressed pubic key or aprivate key.
*
* Best practice is usually to use a cryptographic hash on the
* returned value before using it as a symetric secret.
*
* @example:
* sign1 = new SigningKey(id("some-secret-1"))
* sign2 = new SigningKey(id("some-secret-2"))
*
* // Notice that privA.computeSharedSecret(pubB)...
* sign1.computeSharedSecret(sign2.publicKey)
* //_result:
*
* // ...is equal to privB.computeSharedSecret(pubA).
* sign2.computeSharedSecret(sign1.publicKey)
* //_result:
*/
computeSharedSecret(other: BytesLike): string {
const pubKey = SigningKey.computePublicKey(other);
return hexlify(secp256k1.getSharedSecret(getBytesCopy(this.#privateKey), getBytes(pubKey), false));
}
/**
* Compute the public key for %%key%%, optionally %%compressed%%.
*
* The %%key%% may be any type of key, a raw public key, a
* compressed/uncompressed public key or private key.
*
* @example:
* sign = new SigningKey(id("some-secret"));
*
* // Compute the uncompressed public key for a private key
* SigningKey.computePublicKey(sign.privateKey)
* //_result:
*
* // Compute the compressed public key for a private key
* SigningKey.computePublicKey(sign.privateKey, true)
* //_result:
*
* // Compute the uncompressed public key
* SigningKey.computePublicKey(sign.publicKey, false);
* //_result:
*
* // Compute the Compressed a public key
* SigningKey.computePublicKey(sign.publicKey, true);
* //_result:
*/
static computePublicKey(key: BytesLike, compressed?: boolean): string {
let bytes = getBytes(key, "key");
// private key
if (bytes.length === 32) {
const pubKey = secp256k1.getPublicKey(bytes, !!compressed);
return hexlify(pubKey);
}
// raw public key; use uncompressed key with 0x04 prefix
if (bytes.length === 64) {
const pub = new Uint8Array(65);
pub[0] = 0x04;
pub.set(bytes, 1);
bytes = pub;
}
const point = secp256k1.ProjectivePoint.fromHex(bytes);
return hexlify(point.toRawBytes(compressed));
}
/**
* Returns the public key for the private key which produced the
* %%signature%% for the given %%digest%%.
*
* @example:
* key = new SigningKey(id("some-secret"))
* digest = id("hello world")
* sig = key.sign(digest)
*
* // Notice the signer public key...
* key.publicKey
* //_result:
*
* // ...is equal to the recovered public key
* SigningKey.recoverPublicKey(digest, sig)
* //_result:
*
*/
static recoverPublicKey(digest: BytesLike, signature: SignatureLike): string {
assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
const sig = Signature.from(signature);
let secpSig = secp256k1.Signature.fromCompact(getBytesCopy(concat([ sig.r, sig.s ])));
secpSig = secpSig.addRecoveryBit(sig.yParity);
const pubKey = secpSig.recoverPublicKey(getBytesCopy(digest));
assertArgument(pubKey != null, "invalid signature for digest", "signature", signature);
return "0x" + pubKey.toHex(false);
}
/**
* Returns the point resulting from adding the ellipic curve points
* %%p0%% and %%p1%%.
*
* This is not a common function most developers should require, but
* can be useful for certain privacy-specific techniques.
*
* For example, it is used by [[HDNodeWallet]] to compute child
* addresses from parent public keys and chain codes.
*/
static addPoints(p0: BytesLike, p1: BytesLike, compressed?: boolean): string {
const pub0 = secp256k1.ProjectivePoint.fromHex(SigningKey.computePublicKey(p0).substring(2));
const pub1 = secp256k1.ProjectivePoint.fromHex(SigningKey.computePublicKey(p1).substring(2));
return "0x" + pub0.add(pub1).toHex(!!compressed)
}
}

221
dev/env/node_modules/ethers/src.ts/ethers.ts generated vendored Executable file
View File

@@ -0,0 +1,221 @@
/////////////////////////////
//
export { version } from "./_version.js";
export {
decodeBytes32String, encodeBytes32String,
AbiCoder,
ConstructorFragment, ErrorFragment, EventFragment, Fragment, FallbackFragment, FunctionFragment, NamedFragment, ParamType, StructFragment,
checkResultErrors, ErrorDescription, Indexed, Interface, LogDescription, Result, TransactionDescription,
Typed,
} from "./abi/index.js";
export {
getAddress, getIcapAddress,
getCreateAddress, getCreate2Address,
isAddressable, isAddress, resolveAddress
} from "./address/index.js";
export {
ZeroAddress,
WeiPerEther, MaxUint256, MinInt256, MaxInt256, N,
ZeroHash,
EtherSymbol, MessagePrefix
} from "./constants/index.js";
export {
BaseContract, Contract,
ContractFactory,
ContractEventPayload, ContractTransactionReceipt, ContractTransactionResponse, ContractUnknownEventPayload, EventLog, UndecodedEventLog
} from "./contract/index.js";
export {
computeHmac,
randomBytes,
keccak256,
ripemd160,
sha256, sha512,
pbkdf2,
scrypt, scryptSync,
lock,
Signature, SigningKey
} from "./crypto/index.js";
export {
id,
ensNormalize, isValidName, namehash, dnsEncode,
hashAuthorization, verifyAuthorization,
hashMessage, verifyMessage,
solidityPacked, solidityPackedKeccak256, solidityPackedSha256,
TypedDataEncoder,
verifyTypedData
} from "./hash/index.js";
export {
getDefaultProvider,
Block, FeeData, Log, TransactionReceipt, TransactionResponse,
AbstractSigner, NonceManager, VoidSigner,
AbstractProvider,
FallbackProvider,
JsonRpcApiProvider, JsonRpcProvider, JsonRpcSigner,
BrowserProvider,
AlchemyProvider, AnkrProvider, BlockscoutProvider, ChainstackProvider,
CloudflareProvider, EtherscanProvider, InfuraProvider,
InfuraWebSocketProvider, PocketProvider, QuickNodeProvider,
IpcSocketProvider, SocketProvider, WebSocketProvider,
EnsResolver,
Network,
EnsPlugin, EtherscanPlugin,
FeeDataNetworkPlugin, FetchUrlFeeDataNetworkPlugin,
GasCostPlugin, NetworkPlugin, MulticoinProviderPlugin,
SocketBlockSubscriber, SocketEventSubscriber, SocketPendingSubscriber,
SocketSubscriber, UnmanagedSubscriber,
copyRequest, showThrottleMessage
} from "./providers/index.js";
export {
accessListify, authorizationify,
computeAddress, recoverAddress,
Transaction
} from "./transaction/index.js";
export {
decodeBase58, encodeBase58,
decodeBase64, encodeBase64,
concat, dataLength, dataSlice, getBytes, getBytesCopy, hexlify,
isHexString, isBytesLike, stripZerosLeft, zeroPadBytes, zeroPadValue,
defineProperties, resolveProperties,
assert, assertArgument, assertArgumentCount, assertNormalize, assertPrivate,
makeError,
isCallException, isError,
EventPayload,
FetchRequest, FetchResponse, FetchCancelSignal,
FixedNumber,
getBigInt, getNumber, getUint, toBeArray, toBigInt, toBeHex, toNumber, toQuantity,
fromTwos, toTwos, mask,
formatEther, parseEther, formatUnits, parseUnits,
toUtf8Bytes, toUtf8CodePoints, toUtf8String,
Utf8ErrorFuncs,
decodeRlp, encodeRlp,
uuidV4,
} from "./utils/index.js";
export {
Mnemonic,
BaseWallet, HDNodeWallet, HDNodeVoidWallet,
Wallet,
defaultPath,
getAccountPath, getIndexedAccountPath,
isCrowdsaleJson, isKeystoreJson,
decryptCrowdsaleJson, decryptKeystoreJsonSync, decryptKeystoreJson,
encryptKeystoreJson, encryptKeystoreJsonSync,
} from "./wallet/index.js";
export {
Wordlist, LangEn, WordlistOwl, WordlistOwlA, wordlists
} from "./wordlists/index.js";
/////////////////////////////
// Types
export type {
JsonFragment, JsonFragmentType,
FormatType, FragmentType,
InterfaceAbi,
ParamTypeWalkFunc, ParamTypeWalkAsyncFunc
} from "./abi/index.js";
export type {
Addressable, AddressLike, NameResolver
} from "./address/index.js";
export type {
ConstantContractMethod, ContractEvent, ContractEventArgs, ContractEventName,
ContractInterface, ContractMethod, ContractMethodArgs, ContractTransaction,
DeferredTopicFilter, Overrides,
BaseContractMethod, ContractDeployTransaction, PostfixOverrides,
WrappedFallback
} from "./contract/index.js";
export type { ProgressCallback, SignatureLike } from "./crypto/index.js";
export type {
AuthorizationRequest, TypedDataDomain, TypedDataField
} from "./hash/index.js";
export type {
Provider, Signer,
CommunityResourcable,
AbstractProviderOptions, BrowserProviderOptions, FallbackProviderOptions,
AbstractProviderPlugin, BlockParams, BlockTag, BrowserDiscoverOptions,
ContractRunner, DebugEventBrowserProvider, Eip1193Provider,
Eip6963ProviderInfo, EventFilter, Filter, FilterByBlockHash,
GasCostParameters, JsonRpcApiProviderOptions, JsonRpcError,
JsonRpcPayload, JsonRpcResult, JsonRpcTransactionRequest, LogParams,
MinedBlock, MinedTransactionResponse, Networkish, OrphanFilter,
PerformActionFilter, PerformActionRequest, PerformActionTransaction,
PreparedTransactionRequest, ProviderEvent, Subscriber, Subscription,
TopicFilter, TransactionReceiptParams, TransactionRequest,
TransactionResponseParams, WebSocketCreator, WebSocketLike
} from "./providers/index.js";
export type {
AccessList, AccessListish, AccessListEntry,
Authorization, AuthorizationLike,
Blob, BlobLike, KzgLibrary, KzgLibraryLike,
TransactionLike
} from "./transaction/index.js";
export type {
BytesLike,
BigNumberish, Numeric,
ErrorCode,
FixedFormat,
Utf8ErrorFunc, UnicodeNormalizationForm, Utf8ErrorReason,
RlpStructuredData, RlpStructuredDataish,
GetUrlResponse,
FetchPreflightFunc, FetchProcessFunc, FetchRetryFunc,
FetchGatewayFunc, FetchGetUrlFunc,
EthersError, UnknownError, NotImplementedError, UnsupportedOperationError, NetworkError,
ServerError, TimeoutError, BadDataError, CancelledError, BufferOverrunError,
NumericFaultError, InvalidArgumentError, MissingArgumentError, UnexpectedArgumentError,
CallExceptionError, InsufficientFundsError, NonceExpiredError, OffchainFaultError,
ReplacementUnderpricedError, TransactionReplacedError, UnconfiguredNameError,
ActionRejectedError,
CodedEthersError,
CallExceptionAction, CallExceptionTransaction,
EventEmitterable, Listener
} from "./utils/index.js";
export type {
CrowdsaleAccount, KeystoreAccount, EncryptOptions
} from "./wallet/index.js";
// dummy change; to pick-up ws security issue changes

38
dev/env/node_modules/ethers/src.ts/hash/authorization.ts generated vendored Executable file
View File

@@ -0,0 +1,38 @@
import { getAddress } from "../address/index.js";
import { keccak256 } from "../crypto/index.js";
import { recoverAddress } from "../transaction/index.js";
import {
assertArgument, concat, encodeRlp, toBeArray
} from "../utils/index.js";
import type { Addressable } from "../address/index.js";
import type { SignatureLike } from "../crypto/index.js";
import type { BigNumberish, Numeric } from "../utils/index.js";
export interface AuthorizationRequest {
address: string | Addressable;
nonce?: Numeric;
chainId?: BigNumberish;
}
/**
* Computes the [[link-eip-7702]] authorization digest to sign.
*/
export function hashAuthorization(auth: AuthorizationRequest): string {
assertArgument(typeof(auth.address) === "string", "invalid address for hashAuthorization", "auth.address", auth);
return keccak256(concat([
"0x05", encodeRlp([
(auth.chainId != null) ? toBeArray(auth.chainId): "0x",
getAddress(auth.address),
(auth.nonce != null) ? toBeArray(auth.nonce): "0x",
])
]));
}
/**
* Return the address of the private key that produced
* the signature %%sig%% during signing for %%message%%.
*/
export function verifyAuthorization(auth: AuthorizationRequest, sig: SignatureLike): string {
return recoverAddress(hashAuthorization(auth), sig);
}

17
dev/env/node_modules/ethers/src.ts/hash/id.ts generated vendored Executable file
View File

@@ -0,0 +1,17 @@
import { keccak256 } from "../crypto/index.js";
import { toUtf8Bytes } from "../utils/index.js";
/**
* A simple hashing function which operates on UTF-8 strings to
* compute an 32-byte identifier.
*
* This simply computes the [UTF-8 bytes](toUtf8Bytes) and computes
* the [[keccak256]].
*
* @example:
* id("hello world")
* //_result:
*/
export function id(value: string): string {
return keccak256(toUtf8Bytes(value));
}

18
dev/env/node_modules/ethers/src.ts/hash/index.ts generated vendored Executable file
View File

@@ -0,0 +1,18 @@
/**
* Utilities for common tasks involving hashing. Also see
* [cryptographic hashing](about-crypto-hashing).
*
* @_section: api/hashing:Hashing Utilities [about-hashing]
*/
export { hashAuthorization, verifyAuthorization } from "./authorization.js";
export { id } from "./id.js"
export { ensNormalize, isValidName, namehash, dnsEncode } from "./namehash.js";
export { hashMessage, verifyMessage } from "./message.js";
export {
solidityPacked, solidityPackedKeccak256, solidityPackedSha256
} from "./solidity.js";
export { TypedDataEncoder, verifyTypedData } from "./typed-data.js";
export type { AuthorizationRequest } from "./authorization.js";
export type { TypedDataDomain, TypedDataField } from "./typed-data.js";

51
dev/env/node_modules/ethers/src.ts/hash/message.ts generated vendored Executable file
View File

@@ -0,0 +1,51 @@
import { keccak256 } from "../crypto/index.js";
import { MessagePrefix } from "../constants/index.js";
import { recoverAddress } from "../transaction/index.js";
import { concat, toUtf8Bytes } from "../utils/index.js";
import type { SignatureLike } from "../crypto/index.js";
/**
* Computes the [[link-eip-191]] personal-sign message digest to sign.
*
* This prefixes the message with [[MessagePrefix]] and the decimal length
* of %%message%% and computes the [[keccak256]] digest.
*
* If %%message%% is a string, it is converted to its UTF-8 bytes
* first. To compute the digest of a [[DataHexString]], it must be converted
* to [bytes](getBytes).
*
* @example:
* hashMessage("Hello World")
* //_result:
*
* // Hashes the SIX (6) string characters, i.e.
* // [ "0", "x", "4", "2", "4", "3" ]
* hashMessage("0x4243")
* //_result:
*
* // Hashes the TWO (2) bytes [ 0x42, 0x43 ]...
* hashMessage(getBytes("0x4243"))
* //_result:
*
* // ...which is equal to using data
* hashMessage(new Uint8Array([ 0x42, 0x43 ]))
* //_result:
*
*/
export function hashMessage(message: Uint8Array | string): string {
if (typeof(message) === "string") { message = toUtf8Bytes(message); }
return keccak256(concat([
toUtf8Bytes(MessagePrefix),
toUtf8Bytes(String(message.length)),
message
]));
}
/**
* Return the address of the private key that produced
* the signature %%sig%% during signing for %%message%%.
*/
export function verifyMessage(message: Uint8Array | string, sig: SignatureLike): string {
const digest = hashMessage(message);
return recoverAddress(digest, sig);
}

101
dev/env/node_modules/ethers/src.ts/hash/namehash.ts generated vendored Executable file
View File

@@ -0,0 +1,101 @@
import { keccak256 } from "../crypto/index.js";
import {
concat, hexlify, assertArgument, toUtf8Bytes
} from "../utils/index.js";
import { ens_normalize } from "@adraffy/ens-normalize";
const Zeros = new Uint8Array(32);
Zeros.fill(0);
function checkComponent(comp: Uint8Array): Uint8Array {
assertArgument(comp.length !== 0, "invalid ENS name; empty component", "comp", comp)
return comp;
}
function ensNameSplit(name: string): Array<Uint8Array> {
const bytes = toUtf8Bytes(ensNormalize(name));
const comps: Array<Uint8Array> = [ ];
if (name.length === 0) { return comps; }
let last = 0;
for (let i = 0; i < bytes.length; i++) {
const d = bytes[i];
// A separator (i.e. "."); copy this component
if (d === 0x2e) {
comps.push(checkComponent(bytes.slice(last, i)));
last = i + 1;
}
}
// There was a stray separator at the end of the name
assertArgument(last < bytes.length, "invalid ENS name; empty component", "name", name);
comps.push(checkComponent(bytes.slice(last)));
return comps;
}
/**
* Returns the ENS %%name%% normalized.
*/
export function ensNormalize(name: string): string {
try {
if (name.length === 0) { throw new Error("empty label"); }
return ens_normalize(name);
} catch (error: any) {
assertArgument(false, `invalid ENS name (${ error.message })`, "name", name);
}
}
/**
* Returns ``true`` if %%name%% is a valid ENS name.
*/
export function isValidName(name: string): name is string {
try {
return (ensNameSplit(name).length !== 0);
} catch (error) { }
return false;
}
/**
* Returns the [[link-namehash]] for %%name%%.
*/
export function namehash(name: string): string {
assertArgument(typeof(name) === "string", "invalid ENS name; not a string", "name", name);
assertArgument(name.length, `invalid ENS name (empty label)`, "name", name);
let result: string | Uint8Array = Zeros;
const comps = ensNameSplit(name);
while (comps.length) {
result = keccak256(concat([ result, keccak256(<Uint8Array>(comps.pop()))] ));
}
return hexlify(result);
}
/**
* Returns the DNS encoded %%name%%.
*
* This is used for various parts of ENS name resolution, such
* as the wildcard resolution.
*/
export function dnsEncode(name: string, _maxLength?: number): string {
const length = (_maxLength != null) ? _maxLength: 63;
assertArgument(length <= 255, "DNS encoded label cannot exceed 255", "length", length);
return hexlify(concat(ensNameSplit(name).map((comp) => {
assertArgument(comp.length <= length, `label ${ JSON.stringify(name) } exceeds ${ length } bytes`, "name", name);
const bytes = new Uint8Array(comp.length + 1);
bytes.set(comp, 1);
bytes[0] = bytes.length - 1;
return bytes;
}))) + "00";
}

117
dev/env/node_modules/ethers/src.ts/hash/solidity.ts generated vendored Executable file
View File

@@ -0,0 +1,117 @@
import { getAddress } from "../address/index.js";
import {
keccak256 as _keccak256, sha256 as _sha256
} from "../crypto/index.js";
import {
concat, dataLength, getBytes, hexlify, toBeArray, toTwos, toUtf8Bytes, zeroPadBytes, zeroPadValue,
assertArgument
} from "../utils/index.js";
const regexBytes = new RegExp("^bytes([0-9]+)$");
const regexNumber = new RegExp("^(u?int)([0-9]*)$");
const regexArray = new RegExp("^(.*)\\[([0-9]*)\\]$");
function _pack(type: string, value: any, isArray?: boolean): Uint8Array {
switch(type) {
case "address":
if (isArray) { return getBytes(zeroPadValue(value, 32)); }
return getBytes(getAddress(value));
case "string":
return toUtf8Bytes(value);
case "bytes":
return getBytes(value);
case "bool":
value = (!!value ? "0x01": "0x00");
if (isArray) { return getBytes(zeroPadValue(value, 32)); }
return getBytes(value);
}
let match = type.match(regexNumber);
if (match) {
let signed = (match[1] === "int");
let size = parseInt(match[2] || "256")
assertArgument((!match[2] || match[2] === String(size)) && (size % 8 === 0) && size !== 0 && size <= 256, "invalid number type", "type", type);
if (isArray) { size = 256; }
if (signed) { value = toTwos(value, size); }
return getBytes(zeroPadValue(toBeArray(value), size / 8));
}
match = type.match(regexBytes);
if (match) {
const size = parseInt(match[1]);
assertArgument(String(size) === match[1] && size !== 0 && size <= 32, "invalid bytes type", "type", type);
assertArgument(dataLength(value) === size, `invalid value for ${ type }`, "value", value);
if (isArray) { return getBytes(zeroPadBytes(value, 32)); }
return value;
}
match = type.match(regexArray);
if (match && Array.isArray(value)) {
const baseType = match[1];
const count = parseInt(match[2] || String(value.length));
assertArgument(count === value.length, `invalid array length for ${ type }`, "value", value);
const result: Array<Uint8Array> = [];
value.forEach(function(value) {
result.push(_pack(baseType, value, true));
});
return getBytes(concat(result));
}
assertArgument(false, "invalid type", "type", type)
}
// @TODO: Array Enum
/**
* Computes the [[link-solc-packed]] representation of %%values%%
* respectively to their %%types%%.
*
* @example:
* addr = "0x8ba1f109551bd432803012645ac136ddd64dba72"
* solidityPacked([ "address", "uint" ], [ addr, 45 ]);
* //_result:
*/
export function solidityPacked(types: ReadonlyArray<string>, values: ReadonlyArray<any>): string {
assertArgument(types.length === values.length, "wrong number of values; expected ${ types.length }", "values", values);
const tight: Array<Uint8Array> = [];
types.forEach(function(type, index) {
tight.push(_pack(type, values[index]));
});
return hexlify(concat(tight));
}
/**
* Computes the [[link-solc-packed]] [[keccak256]] hash of %%values%%
* respectively to their %%types%%.
*
* @example:
* addr = "0x8ba1f109551bd432803012645ac136ddd64dba72"
* solidityPackedKeccak256([ "address", "uint" ], [ addr, 45 ]);
* //_result:
*/
export function solidityPackedKeccak256(types: ReadonlyArray<string>, values: ReadonlyArray<any>): string {
return _keccak256(solidityPacked(types, values));
}
/**
* Computes the [[link-solc-packed]] [[sha256]] hash of %%values%%
* respectively to their %%types%%.
*
* @example:
* addr = "0x8ba1f109551bd432803012645ac136ddd64dba72"
* solidityPackedSha256([ "address", "uint" ], [ addr, 45 ]);
* //_result:
*/
export function solidityPackedSha256(types: ReadonlyArray<string>, values: ReadonlyArray<any>): string {
return _sha256(solidityPacked(types, values));
}

658
dev/env/node_modules/ethers/src.ts/hash/typed-data.ts generated vendored Executable file
View File

@@ -0,0 +1,658 @@
//import { TypedDataDomain, TypedDataField } from "@ethersproject/providerabstract-signer";
import { getAddress } from "../address/index.js";
import { keccak256 } from "../crypto/index.js";
import { recoverAddress } from "../transaction/index.js";
import {
concat, defineProperties, getBigInt, getBytes, hexlify, isHexString, mask, toBeHex, toQuantity, toTwos, zeroPadValue,
assertArgument
} from "../utils/index.js";
import { id } from "./id.js";
import type { SignatureLike } from "../crypto/index.js";
import type { BigNumberish, BytesLike } from "../utils/index.js";
const padding = new Uint8Array(32);
padding.fill(0);
const BN__1 = BigInt(-1);
const BN_0 = BigInt(0);
const BN_1 = BigInt(1);
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 {
/**
* The human-readable name of the signing domain.
*/
name?: null | string;
/**
* The major version of the signing domain.
*/
version?: null | string;
/**
* The chain ID of the signing domain.
*/
chainId?: null | BigNumberish;
/**
* The the address of the contract that will verify the signature.
*/
verifyingContract?: null | string;
/**
* A salt used for purposes decided by the specific domain.
*/
salt?: null | BytesLike;
};
/**
* A specific field of a structured [[link-eip-712]] type.
*/
export interface TypedDataField {
/**
* The field name.
*/
name: string;
/**
* The type of the field.
*/
type: string;
};
function hexPadRight(value: BytesLike): string {
const bytes = getBytes(value);
const padOffset = bytes.length % 32
if (padOffset) {
return concat([ bytes, padding.slice(padOffset) ]);
}
return hexlify(bytes);
}
const hexTrue = toBeHex(BN_1, 32);
const hexFalse = toBeHex(BN_0, 32);
const domainFieldTypes: Record<string, string> = {
name: "string",
version: "string",
chainId: "uint256",
verifyingContract: "address",
salt: "bytes32"
};
const domainFieldNames: Array<string> = [
"name", "version", "chainId", "verifyingContract", "salt"
];
function checkString(key: string): (value: any) => string {
return function (value: any){
assertArgument(typeof(value) === "string", `invalid domain value for ${ JSON.stringify(key) }`, `domain.${ key }`, value);
return value;
}
}
const domainChecks: Record<string, (value: any) => any> = {
name: checkString("name"),
version: checkString("version"),
chainId: function(_value: any) {
const value = getBigInt(_value, "domain.chainId");
assertArgument(value >= 0, "invalid chain ID", "domain.chainId", _value);
if (Number.isSafeInteger(value)) { return Number(value); }
return toQuantity(value);
},
verifyingContract: function(value: any) {
try {
return getAddress(value).toLowerCase();
} catch (error) { }
assertArgument(false, `invalid domain value "verifyingContract"`, "domain.verifyingContract", value);
},
salt: function(value: any) {
const bytes = getBytes(value, "domain.salt");
assertArgument(bytes.length === 32, `invalid domain value "salt"`, "domain.salt", value);
return hexlify(bytes);
}
}
function getBaseEncoder(type: string): null | ((value: any) => string) {
// intXX and uintXX
{
const match = type.match(/^(u?)int(\d+)$/);
if (match) {
const signed = (match[1] === "");
const width = parseInt(match[2]);
assertArgument(width % 8 === 0 && width !== 0 && width <= 256 && match[2] === String(width), "invalid numeric width", "type", type);
const boundsUpper = mask(BN_MAX_UINT256, signed ? (width - 1): width);
const boundsLower = signed ? ((boundsUpper + BN_1) * BN__1): BN_0;
return function(_value: BigNumberish) {
const value = getBigInt(_value, "value");
assertArgument(value >= boundsLower && value <= boundsUpper, `value out-of-bounds for ${ type }`, "value", value);
return toBeHex(signed ? toTwos(value, 256): value, 32);
};
}
}
// bytesXX
{
const match = type.match(/^bytes(\d+)$/);
if (match) {
const width = parseInt(match[1]);
assertArgument(width !== 0 && width <= 32 && match[1] === String(width), "invalid bytes width", "type", type);
return function(value: BytesLike) {
const bytes = getBytes(value);
assertArgument(bytes.length === width, `invalid length for ${ type }`, "value", value);
return hexPadRight(value);
};
}
}
switch (type) {
case "address": return function(value: string) {
return zeroPadValue(getAddress(value), 32);
};
case "bool": return function(value: boolean) {
return ((!value) ? hexFalse: hexTrue);
};
case "bytes": return function(value: BytesLike) {
return keccak256(value);
};
case "string": return function(value: string) {
return id(value);
};
}
return null;
}
function encodeType(name: string, fields: Array<TypedDataField>): string {
return `${ name }(${ fields.map(({ name, type }) => (type + " " + name)).join(",") })`;
}
type ArrayResult = {
base: string; // The base type
index?: string; // the full Index (if any)
array?: { // The Array... (if index)
base: string; // ...base type (same as above)
prefix: string; // ...sans the final Index
count: number; // ...the final Index (-1 for dynamic)
}
};
// foo[][3] => { base: "foo", index: "[][3]", array: {
// base: "foo", prefix: "foo[]", count: 3 } }
function splitArray(type: string): ArrayResult {
const match = type.match(/^([^\x5b]*)((\x5b\d*\x5d)*)(\x5b(\d*)\x5d)$/);
if (match) {
return {
base: match[1],
index: (match[2] + match[4]),
array: {
base: match[1],
prefix: (match[1] + match[2]),
count: (match[5] ? parseInt(match[5]): -1),
}
};
}
return { base: type };
}
/**
* 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 {
/**
* 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 #types: string;
/**
* The types.
*/
get types(): Record<string, Array<TypedDataField>> {
return JSON.parse(this.#types);
}
readonly #fullTypes: Map<string, 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>>) {
this.#fullTypes = new Map();
this.#encoderCache = new Map();
// Link struct types to their direct child structs
const links: Map<string, Set<string>> = new Map();
// Link structs to structs which contain them as a child
const parents: Map<string, Array<string>> = new Map();
// Link all subtypes within a given struct
const subtypes: Map<string, Set<string>> = new Map();
const types: Record<string, Array<TypedDataField>> = { };
Object.keys(_types).forEach((type) => {
types[type] = _types[type].map(({ name, type }) => {
// Normalize the base type (unless name conflict)
let { base, index } = splitArray(type);
if (base === "int" && !_types["int"]) { base = "int256"; }
if (base === "uint" && !_types["uint"]) { base = "uint256"; }
return { name, type: (base + (index || "")) };
});
links.set(type, new Set());
parents.set(type, [ ]);
subtypes.set(type, new Set());
});
this.#types = JSON.stringify(types);
for (const name in types) {
const uniqueNames: Set<string> = new Set();
for (const field of types[name]) {
// Check each field has a unique name
assertArgument(!uniqueNames.has(field.name), `duplicate variable name ${ JSON.stringify(field.name) } in ${ JSON.stringify(name) }`, "types", _types);
uniqueNames.add(field.name);
// Get the base type (drop any array specifiers)
const baseType = splitArray(field.type).base;
assertArgument(baseType !== name, `circular type reference to ${ JSON.stringify(baseType) }`, "types", _types);
// Is this a base encoding type?
const encoder = getBaseEncoder(baseType);
if (encoder) { continue; }
assertArgument(parents.has(baseType), `unknown type ${ JSON.stringify(baseType) }`, "types", _types);
// Add linkage
(parents.get(baseType) as Array<string>).push(name);
(links.get(name) as Set<string>).add(baseType);
}
}
// Deduce the primary type
const primaryTypes = Array.from(parents.keys()).filter((n) => ((parents.get(n) as Array<string>).length === 0));
assertArgument(primaryTypes.length !== 0, "missing primary type", "types", _types);
assertArgument(primaryTypes.length === 1, `ambiguous primary types or unused types: ${ primaryTypes.map((t) => (JSON.stringify(t))).join(", ") }`, "types", _types);
defineProperties<TypedDataEncoder>(this, { primaryType: primaryTypes[0] });
// Check for circular type references
function checkCircular(type: string, found: Set<string>) {
assertArgument(!found.has(type), `circular type reference to ${ JSON.stringify(type) }`, "types", _types);
found.add(type);
for (const child of (links.get(type) as Set<string>)) {
if (!parents.has(child)) { continue; }
// Recursively check children
checkCircular(child, found);
// Mark all ancestors as having this decendant
for (const subtype of found) {
(subtypes.get(subtype) as Set<string>).add(child);
}
}
found.delete(type);
}
checkCircular(this.primaryType, new Set());
// Compute each fully describe type
for (const [ name, set ] of subtypes) {
const st = Array.from(set);
st.sort();
this.#fullTypes.set(name, encodeType(name, types[name]) + st.map((t) => encodeType(t, types[t])).join(""));
}
}
/**
* Returnthe encoder for the specific %%type%%.
*/
getEncoder(type: string): (value: any) => string {
let encoder = this.#encoderCache.get(type);
if (!encoder) {
encoder = this.#getEncoder(type);
this.#encoderCache.set(type, encoder);
}
return encoder;
}
#getEncoder(type: string): (value: any) => string {
// Basic encoder type (address, bool, uint256, etc)
{
const encoder = getBaseEncoder(type);
if (encoder) { return encoder; }
}
// Array
const array = splitArray(type).array;
if (array) {
const subtype = array.prefix;
const subEncoder = this.getEncoder(subtype);
return (value: Array<any>) => {
assertArgument(array.count === -1 || array.count === value.length, `array length mismatch; expected length ${ array.count }`, "value", value);
let result = value.map(subEncoder);
if (this.#fullTypes.has(subtype)) {
result = result.map(keccak256);
}
return keccak256(concat(result));
};
}
// Struct
const fields = this.types[type];
if (fields) {
const encodedType = id(this.#fullTypes.get(type) as string);
return (value: Record<string, any>) => {
const values = fields.map(({ name, type }) => {
const result = this.getEncoder(type)(value[name]);
if (this.#fullTypes.has(type)) { return keccak256(result); }
return result;
});
values.unshift(encodedType);
return concat(values);
}
}
assertArgument(false, `unknown type: ${ type }`, "type", type);
}
/**
* Return the full type for %%name%%.
*/
encodeType(name: string): string {
const result = this.#fullTypes.get(name);
assertArgument(result, `unknown type: ${ JSON.stringify(name) }`, "name", name);
return result;
}
/**
* Return the encoded %%value%% for the %%type%%.
*/
encodeData(type: string, value: any): string {
return this.getEncoder(type)(value);
}
/**
* Returns the hash of %%value%% for the type of %%name%%.
*/
hashStruct(name: string, value: Record<string, any>): string {
return keccak256(this.encodeData(name, value));
}
/**
* Return the fulled encoded %%value%% for the [[types]].
*/
encode(value: Record<string, any>): string {
return this.encodeData(this.primaryType, value);
}
/**
* Return the hash of the fully encoded %%value%% for the [[types]].
*/
hash(value: Record<string, any>): string {
return this.hashStruct(this.primaryType, value);
}
/**
* @_ignore:
*/
_visit(type: string, value: any, callback: (type: string, data: any) => any): any {
// Basic encoder type (address, bool, uint256, etc)
{
const encoder = getBaseEncoder(type);
if (encoder) { return callback(type, value); }
}
// Array
const array = splitArray(type).array;
if (array) {
assertArgument(array.count === -1 || array.count === value.length, `array length mismatch; expected length ${ array.count }`, "value", value);
return value.map((v: any) => this._visit(array.prefix, v, callback));
}
// Struct
const fields = this.types[type];
if (fields) {
return fields.reduce((accum, { name, type }) => {
accum[name] = this._visit(type, value[name], callback);
return accum;
}, <Record<string, any>>{});
}
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 {
return this._visit(this.primaryType, value, callback);
}
/**
* Create a new **TypedDataEncoder** for %%types%%.
*/
static from(types: Record<string, Array<TypedDataField>>): TypedDataEncoder {
return new TypedDataEncoder(types);
}
/**
* Return the primary type for %%types%%.
*/
static getPrimaryType(types: Record<string, Array<TypedDataField>>): string {
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 {
return TypedDataEncoder.from(types).hashStruct(name, value);
}
/**
* Return the domain hash for %%domain%%.
*/
static hashDomain(domain: TypedDataDomain): string {
const domainFields: Array<TypedDataField> = [ ];
for (const name in domain) {
if ((<Record<string, any>>domain)[name] == null) { continue; }
const type = domainFieldTypes[name];
assertArgument(type, `invalid typed-data domain key: ${ JSON.stringify(name) }`, "domain", domain);
domainFields.push({ name, type });
}
domainFields.sort((a, b) => {
return domainFieldNames.indexOf(a.name) - domainFieldNames.indexOf(b.name);
});
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 {
return concat([
"0x1901",
TypedDataEncoder.hashDomain(domain),
TypedDataEncoder.from(types).hash(value)
]);
}
/**
* 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 {
return keccak256(TypedDataEncoder.encode(domain, types, value));
}
// 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 }> {
// Make a copy to isolate it from the object passed in
domain = Object.assign({ }, domain);
// Allow passing null to ignore value
for (const key in domain) {
if ((<Record<string, any>>domain)[key] == null) {
delete (<Record<string, any>>domain)[key];
}
}
// Look up all ENS names
const ensCache: Record<string, string> = { };
// Do we need to look up the domain's verifyingContract?
if (domain.verifyingContract && !isHexString(domain.verifyingContract, 20)) {
ensCache[domain.verifyingContract] = "0x";
}
// We are going to use the encoder to visit all the base values
const encoder = TypedDataEncoder.from(types);
// Get a list of all the addresses
encoder.visit(value, (type: string, value: any) => {
if (type === "address" && !isHexString(value, 20)) {
ensCache[value] = "0x";
}
return value;
});
// Lookup each name
for (const name in ensCache) {
ensCache[name] = await resolveName(name);
}
// Replace the domain verifyingContract if needed
if (domain.verifyingContract && ensCache[domain.verifyingContract]) {
domain.verifyingContract = ensCache[domain.verifyingContract];
}
// Replace all ENS names with their address
value = encoder.visit(value, (type: string, value: any) => {
if (type === "address" && ensCache[value]) { return ensCache[value]; }
return 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 {
// Validate the domain fields
TypedDataEncoder.hashDomain(domain);
// Derive the EIP712Domain Struct reference type
const domainValues: Record<string, any> = { };
const domainTypes: Array<{ name: string, type:string }> = [ ];
domainFieldNames.forEach((name) => {
const value = (<any>domain)[name];
if (value == null) { return; }
domainValues[name] = domainChecks[name](value);
domainTypes.push({ name, type: domainFieldTypes[name] });
});
const encoder = TypedDataEncoder.from(types);
// Get the normalized types
types = encoder.types;
const typesWithDomain = Object.assign({ }, types);
assertArgument(typesWithDomain.EIP712Domain == null, "types must not contain EIP712Domain type", "types.EIP712Domain", types);
typesWithDomain.EIP712Domain = domainTypes;
// Validate the data structures and types
encoder.encode(value);
return {
types: typesWithDomain,
domain: domainValues,
primaryType: encoder.primaryType,
message: encoder.visit(value, (type: string, value: any) => {
// bytes
if (type.match(/^bytes(\d*)/)) {
return hexlify(getBytes(value));
}
// uint or int
if (type.match(/^u?int/)) {
return getBigInt(value).toString();
}
switch (type) {
case "address":
return value.toLowerCase();
case "bool":
return !!value;
case "string":
assertArgument(typeof(value) === "string", "invalid string", "value", value);
return value;
}
assertArgument(false, "unsupported type", "type", type);
})
};
}
}
/**
* Compute the address used to sign the typed data for the %%signature%%.
*/
export function verifyTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>, signature: SignatureLike): string {
return recoverAddress(TypedDataEncoder.hash(domain, types, value), signature);
}

12
dev/env/node_modules/ethers/src.ts/index.ts generated vendored Executable file
View File

@@ -0,0 +1,12 @@
/**
* The Application Programming Interface (API) is the collection of
* functions, classes and types offered by the Ethers library.
*
* @_section: api:Application Programming Interface [about-api]
* @_navTitle: API
*/
import * as ethers from "./ethers.js";
export { ethers };
export * from "./ethers.js";

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,314 @@
/**
* Generally the [[Wallet]] and [[JsonRpcSigner]] and their sub-classes
* are sufficient for most developers, but this is provided to
* fascilitate more complex Signers.
*
* @_section: api/providers/abstract-signer: Subclassing Signer [abstract-signer]
*/
import { resolveAddress } from "../address/index.js";
import { Transaction } from "../transaction/index.js";
import {
defineProperties, getBigInt, resolveProperties,
assert, assertArgument
} from "../utils/index.js";
import { copyRequest } from "./provider.js";
import type {
AuthorizationRequest, TypedDataDomain, TypedDataField
} from "../hash/index.js";
import type { Authorization, TransactionLike } from "../transaction/index.js";
import type {
BlockTag, Provider, TransactionRequest, TransactionResponse
} from "./provider.js";
import type { Signer } from "./signer.js";
function checkProvider(signer: AbstractSigner, operation: string): Provider {
if (signer.provider) { return signer.provider; }
assert(false, "missing provider", "UNSUPPORTED_OPERATION", { operation });
}
async function populate(signer: AbstractSigner, tx: TransactionRequest): Promise<TransactionLike<string>> {
let pop: any = copyRequest(tx);
if (pop.to != null) { pop.to = resolveAddress(pop.to, signer); }
if (pop.from != null) {
const from = pop.from;
pop.from = Promise.all([
signer.getAddress(),
resolveAddress(from, signer)
]).then(([ address, from ]) => {
assertArgument(address.toLowerCase() === from.toLowerCase(),
"transaction from mismatch", "tx.from", from);
return address;
});
} else {
pop.from = signer.getAddress();
}
return await resolveProperties(pop);
}
/**
* 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 {
/**
* The provider this signer is connected to.
*/
readonly provider!: P;
/**
* Creates a new Signer connected to %%provider%%.
*/
constructor(provider?: P) {
defineProperties<AbstractSigner>(this, { provider: (provider || null) });
}
/**
* Resolves to the Signer address.
*/
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;
async getNonce(blockTag?: BlockTag): Promise<number> {
return checkProvider(this, "getTransactionCount").getTransactionCount(await this.getAddress(), blockTag);
}
async populateCall(tx: TransactionRequest): Promise<TransactionLike<string>> {
const pop = await populate(this, tx);
return pop;
}
async populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>> {
const provider = checkProvider(this, "populateTransaction");
const pop = await populate(this, tx);
if (pop.nonce == null) {
pop.nonce = await this.getNonce("pending");
}
if (pop.gasLimit == null) {
pop.gasLimit = await this.estimateGas(pop);
}
// Populate the chain ID
const network = await (<Provider>(this.provider)).getNetwork();
if (pop.chainId != null) {
const chainId = getBigInt(pop.chainId);
assertArgument(chainId === network.chainId, "transaction chainId mismatch", "tx.chainId", tx.chainId);
} else {
pop.chainId = network.chainId;
}
// Do not allow mixing pre-eip-1559 and eip-1559 properties
const hasEip1559 = (pop.maxFeePerGas != null || pop.maxPriorityFeePerGas != null);
if (pop.gasPrice != null && (pop.type === 2 || hasEip1559)) {
assertArgument(false, "eip-1559 transaction do not support gasPrice", "tx", tx);
} else if ((pop.type === 0 || pop.type === 1) && hasEip1559) {
assertArgument(false, "pre-eip-1559 transaction do not support maxFeePerGas/maxPriorityFeePerGas", "tx", tx);
}
if ((pop.type === 2 || pop.type == null) && (pop.maxFeePerGas != null && pop.maxPriorityFeePerGas != null)) {
// Fully-formed EIP-1559 transaction (skip getFeeData)
pop.type = 2;
} else if (pop.type === 0 || pop.type === 1) {
// Explicit Legacy or EIP-2930 transaction
// We need to get fee data to determine things
const feeData = await provider.getFeeData();
assert(feeData.gasPrice != null, "network does not support gasPrice", "UNSUPPORTED_OPERATION", {
operation: "getGasPrice" });
// Populate missing gasPrice
if (pop.gasPrice == null) { pop.gasPrice = feeData.gasPrice; }
} else {
// We need to get fee data to determine things
const feeData = await provider.getFeeData();
if (pop.type == null) {
// We need to auto-detect the intended type of this transaction...
if (feeData.maxFeePerGas != null && feeData.maxPriorityFeePerGas != null) {
// The network supports EIP-1559!
// Upgrade transaction from null to eip-1559
if (pop.authorizationList && pop.authorizationList.length) {
pop.type = 4;
} else {
pop.type = 2;
}
if (pop.gasPrice != null) {
// Using legacy gasPrice property on an eip-1559 network,
// so use gasPrice as both fee properties
const gasPrice = pop.gasPrice;
delete pop.gasPrice;
pop.maxFeePerGas = gasPrice;
pop.maxPriorityFeePerGas = gasPrice;
} else {
// Populate missing fee data
if (pop.maxFeePerGas == null) {
pop.maxFeePerGas = feeData.maxFeePerGas;
}
if (pop.maxPriorityFeePerGas == null) {
pop.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
}
}
} else if (feeData.gasPrice != null) {
// Network doesn't support EIP-1559...
// ...but they are trying to use EIP-1559 properties
assert(!hasEip1559, "network does not support EIP-1559", "UNSUPPORTED_OPERATION", {
operation: "populateTransaction" });
// Populate missing fee data
if (pop.gasPrice == null) {
pop.gasPrice = feeData.gasPrice;
}
// Explicitly set untyped transaction to legacy
// @TODO: Maybe this shold allow type 1?
pop.type = 0;
} else {
// getFeeData has failed us.
assert(false, "failed to get consistent fee data", "UNSUPPORTED_OPERATION", {
operation: "signer.getFeeData" });
}
} else if (pop.type === 2 || pop.type === 3 || pop.type === 4) {
// Explicitly using EIP-1559 or EIP-4844
// Populate missing fee data
if (pop.maxFeePerGas == null) {
pop.maxFeePerGas = feeData.maxFeePerGas;
}
if (pop.maxPriorityFeePerGas == null) {
pop.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas;
}
}
}
//@TOOD: Don't await all over the place; save them up for
// the end for better batching
return await resolveProperties(pop);
}
async populateAuthorization(_auth: AuthorizationRequest): Promise<AuthorizationRequest> {
const auth = Object.assign({ }, _auth);
// Add a chain ID if not explicitly set to 0
if (auth.chainId == null) {
auth.chainId = (await checkProvider(this, "getNetwork").getNetwork()).chainId;
}
// @TODO: Take chain ID into account when populating noce?
if (auth.nonce == null) { auth.nonce = await this.getNonce(); }
return auth;
}
async estimateGas(tx: TransactionRequest): Promise<bigint> {
return checkProvider(this, "estimateGas").estimateGas(await this.populateCall(tx));
}
async call(tx: TransactionRequest): Promise<string> {
return checkProvider(this, "call").call(await this.populateCall(tx));
}
async resolveName(name: string): Promise<null | string> {
const provider = checkProvider(this, "resolveName");
return await provider.resolveName(name);
}
async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse> {
const provider = checkProvider(this, "sendTransaction");
const pop = await this.populateTransaction(tx);
delete pop.from;
const txObj = Transaction.from(pop);
return await provider.broadcastTransaction(await this.signTransaction(txObj));
}
// @TODO: in v7 move this to be abstract
authorize(authorization: AuthorizationRequest): Promise<Authorization> {
assert(false, "authorization not implemented for this signer",
"UNSUPPORTED_OPERATION", { operation: "authorize" });
}
abstract signTransaction(tx: TransactionRequest): Promise<string>;
abstract signMessage(message: string | Uint8Array): Promise<string>;
abstract signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
}
/**
* A **VoidSigner** is a class designed 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 {
/**
* The signer address.
*/
readonly address!: string;
/**
* Creates a new **VoidSigner** with %%address%% attached to
* %%provider%%.
*/
constructor(address: string, provider?: null | Provider) {
super(provider);
defineProperties<VoidSigner>(this, { address });
}
async getAddress(): Promise<string> { return this.address; }
connect(provider: null | Provider): VoidSigner {
return new VoidSigner(this.address, provider);
}
#throwUnsupported(suffix: string, operation: string): never {
assert(false, `VoidSigner cannot sign ${ suffix }`, "UNSUPPORTED_OPERATION", { operation });
}
async signTransaction(tx: TransactionRequest): Promise<string> {
this.#throwUnsupported("transactions", "signTransaction");
}
async signMessage(message: string | Uint8Array): Promise<string> {
this.#throwUnsupported("messages", "signMessage");
}
async signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
this.#throwUnsupported("typed-data", "signTypedData");
}
}

49
dev/env/node_modules/ethers/src.ts/providers/community.ts generated vendored Executable file
View File

@@ -0,0 +1,49 @@
/**
* There are many awesome community services that provide Ethereum
* nodes both for developers just starting out and for large-scale
* communities.
*
* @_section: api/providers/thirdparty: Community Providers [thirdparty]
*/
/**
* Providers which offer community credentials should extend this
* to notify any interested consumers whether community credentials
* are in-use.
*/
export interface CommunityResourcable {
/**
* Returns true if the instance is connected using the community
* credentials.
*/
isCommunityResource(): boolean;
}
// Show the throttle message only once per service
const shown: Set<string> = new Set();
/**
* Displays a warning in the console when the community resource is
* being used too heavily by the app, recommending the developer
* acquire their own credentials instead of using the community
* credentials.
*
* The notification will only occur once per service.
*/
export function showThrottleMessage(service: string): void {
if (shown.has(service)) { return; }
shown.add(service);
console.log("========= NOTICE =========")
console.log(`Request-Rate Exceeded for ${ service } (this message will not be repeated)`);
console.log("");
console.log("The default API keys for each service are provided as a highly-throttled,");
console.log("community resource for low-traffic projects and early prototyping.");
console.log("");
console.log("While your application will continue to function, we highly recommended");
console.log("signing up for your own API keys to improve performance, increase your");
console.log("request rate/limit and enable other perks, such as metrics and advanced APIs.");
console.log("");
console.log("For more details: https:/\/docs.ethers.org/api-keys/");
console.log("==========================");
}

42
dev/env/node_modules/ethers/src.ts/providers/contracts.ts generated vendored Executable file
View File

@@ -0,0 +1,42 @@
import type {
Provider, TransactionRequest, TransactionResponse
} from "./provider.js";
/**
* 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 {
/**
* 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;
/**
* Required to estimate gas.
*/
estimateGas?: (tx: TransactionRequest) => Promise<bigint>;
/**
* Required for pure, view or static calls to contracts.
*/
call?: (tx: TransactionRequest) => Promise<string>;
/**
* Required to support ENS names
*/
resolveName?: (name: string) => Promise<null | string>;
/**
* Required for state mutating calls
*/
sendTransaction?: (tx: TransactionRequest) => Promise<TransactionResponse>;
}

View File

@@ -0,0 +1,202 @@
import { assert } from "../utils/index.js";
import { AnkrProvider } from "./provider-ankr.js";
import { AlchemyProvider } from "./provider-alchemy.js";
//import { BlockscoutProvider } from "./provider-blockscout.js";
import { ChainstackProvider } from "./provider-chainstack.js";
import { CloudflareProvider } from "./provider-cloudflare.js";
import { EtherscanProvider } from "./provider-etherscan.js";
import { InfuraProvider } from "./provider-infura.js";
//import { PocketProvider } from "./provider-pocket.js";
import { QuickNodeProvider } from "./provider-quicknode.js";
import { FallbackProvider } from "./provider-fallback.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import { Network } from "./network.js";
import { WebSocketProvider } from "./provider-websocket.js";
import type { AbstractProvider } from "./abstract-provider.js";
import type { Networkish } from "./network.js";
import { WebSocketLike } from "./provider-websocket.js";
function isWebSocketLike(value: any): value is WebSocketLike {
return (value && typeof(value.send) === "function" &&
typeof(value.close) === "function");
}
const Testnets = "goerli kovan sepolia classicKotti optimism-goerli arbitrum-goerli matic-mumbai bnbt".split(" ");
/**
* Returns a default provider for %%network%%.
*
* If %%network%% is a [[WebSocketLike]] or string that begins with
* ``"ws:"`` or ``"wss:"``, a [[WebSocketProvider]] is returned backed
* by that WebSocket or URL.
*
* If %%network%% is a string that begins with ``"HTTP:"`` or ``"HTTPS:"``,
* a [[JsonRpcProvider]] is returned connected to that URL.
*
* Otherwise, a default provider is created backed by well-known public
* Web3 backends (such as [[link-infura]]) using community-provided API
* keys.
*
* The %%options%% allows specifying custom API keys per backend (setting
* an API key to ``"-"`` will omit that provider) and ``options.exclusive``
* can be set to either a backend name or and array of backend names, which
* will whitelist **only** those backends.
*
* Current backend strings supported are:
* - ``"alchemy"``
* - ``"ankr"``
* - ``"cloudflare"``
* - ``"chainstack"``
* - ``"etherscan"``
* - ``"infura"``
* - ``"publicPolygon"``
* - ``"quicknode"``
*
* @example:
* // Connect to a local Geth node
* provider = getDefaultProvider("http://localhost:8545/");
*
* // Connect to Ethereum mainnet with any current and future
* // third-party services available
* provider = getDefaultProvider("mainnet");
*
* // Connect to Polygon, but only allow Etherscan and
* // INFURA and use "MY_API_KEY" in calls to Etherscan.
* provider = getDefaultProvider("matic", {
* etherscan: "MY_API_KEY",
* exclusive: [ "etherscan", "infura" ]
* });
*/
export function getDefaultProvider(network?: string | Networkish | WebSocketLike, options?: any): AbstractProvider {
if (options == null) { options = { }; }
const allowService = (name: string) => {
if (options[name] === "-") { return false; }
if (typeof(options.exclusive) === "string") {
return (name === options.exclusive);
}
if (Array.isArray(options.exclusive)) {
return (options.exclusive.indexOf(name) !== -1);
}
return true;
};
if (typeof(network) === "string" && network.match(/^https?:/)) {
return new JsonRpcProvider(network);
}
if (typeof(network) === "string" && network.match(/^wss?:/) || isWebSocketLike(network)) {
return new WebSocketProvider(network);
}
// Get the network and name, if possible
let staticNetwork: null | Network = null;
try {
staticNetwork = Network.from(network);
} catch (error) { }
const providers: Array<AbstractProvider> = [ ];
if (allowService("publicPolygon") && staticNetwork) {
if (staticNetwork.name === "matic") {
providers.push(new JsonRpcProvider("https:/\/polygon-rpc.com/", staticNetwork, { staticNetwork }));
} else if (staticNetwork.name === "matic-amoy") {
providers.push(new JsonRpcProvider("https:/\/rpc-amoy.polygon.technology/", staticNetwork, { staticNetwork }));
}
}
if (allowService("alchemy")) {
try {
providers.push(new AlchemyProvider(network, options.alchemy));
} catch (error) { }
}
if (allowService("ankr") && options.ankr != null) {
try {
providers.push(new AnkrProvider(network, options.ankr));
} catch (error) { }
}
/* Temporarily remove until custom error issue is fixed
if (allowService("blockscout")) {
try {
providers.push(new BlockscoutProvider(network, options.blockscout));
} catch (error) { }
}
*/
if (allowService("chainstack")) {
try {
providers.push(new ChainstackProvider(network, options.chainstack));
} catch (error) { }
}
if (allowService("cloudflare")) {
try {
providers.push(new CloudflareProvider(network));
} catch (error) { }
}
if (allowService("etherscan")) {
try {
providers.push(new EtherscanProvider(network, options.etherscan));
} catch (error) { }
}
if (allowService("infura")) {
try {
let projectId = options.infura;
let projectSecret: undefined | string = undefined;
if (typeof(projectId) === "object") {
projectSecret = projectId.projectSecret;
projectId = projectId.projectId;
}
providers.push(new InfuraProvider(network, projectId, projectSecret));
} catch (error) { }
}
/*
if (options.pocket !== "-") {
try {
let appId = options.pocket;
let secretKey: undefined | string = undefined;
let loadBalancer: undefined | boolean = undefined;
if (typeof(appId) === "object") {
loadBalancer = !!appId.loadBalancer;
secretKey = appId.secretKey;
appId = appId.appId;
}
providers.push(new PocketProvider(network, appId, secretKey, loadBalancer));
} catch (error) { console.log(error); }
}
*/
if (allowService("quicknode")) {
try {
let token = options.quicknode;
providers.push(new QuickNodeProvider(network, token));
} catch (error) { }
}
assert(providers.length, "unsupported default network", "UNSUPPORTED_OPERATION", {
operation: "getDefaultProvider"
});
// No need for a FallbackProvider
if (providers.length === 1) { return providers[0]; }
// We use the floor because public third-party providers can be unreliable,
// so a low number of providers with a large quorum will fail too often
let quorum = Math.floor(providers.length / 2);
if (quorum > 2) { quorum = 2; }
// Testnets don't need as strong a security gaurantee and speed is
// more useful during testing
if (staticNetwork && Testnets.indexOf(staticNetwork.name) !== -1) { quorum = 1; }
// Provided override qorum takes priority
if (options && options.quorum) { quorum = options.quorum; }
return new FallbackProvider(providers, undefined, { quorum });
}

606
dev/env/node_modules/ethers/src.ts/providers/ens-resolver.ts generated vendored Executable file
View File

@@ -0,0 +1,606 @@
/**
* 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]
*/
import { getAddress } from "../address/index.js";
import { ZeroAddress } from "../constants/index.js";
import { Contract } from "../contract/index.js";
import { dnsEncode, namehash } from "../hash/index.js";
import {
hexlify, isHexString, toBeHex,
defineProperties, encodeBase58,
assert, assertArgument, isError,
FetchRequest
} from "../utils/index.js";
import type { FunctionFragment } from "../abi/index.js";
import type { BytesLike } from "../utils/index.js";
import type { AbstractProvider, AbstractProviderPlugin } from "./abstract-provider.js";
import type { EnsPlugin } from "./plugins-network.js";
import type { Provider } from "./provider.js";
// @TODO: This should use the fetch-data:ipfs gateway
// Trim off the ipfs:// prefix and return the default gateway URL
function getIpfsLink(link: string): string {
if (link.match(/^ipfs:\/\/ipfs\//i)) {
link = link.substring(12);
} else if (link.match(/^ipfs:\/\//i)) {
link = link.substring(7);
} else {
assertArgument(false, "unsupported IPFS format", "link", link);
}
return `https:/\/gateway.ipfs.io/ipfs/${ link }`;
}
/**
* The type of data found during a steip during avatar resolution.
*/
export type AvatarLinkageType = "name" | "avatar" | "!avatar" | "url" | "data" | "ipfs" |
"erc721" | "erc1155" | "!erc721-caip" | "!erc1155-caip" |
"!owner" | "owner" | "!balance" | "balance" |
"metadata-url-base" | "metadata-url-expanded" | "metadata-url" | "!metadata-url" |
"!metadata" | "metadata" |
"!imageUrl" | "imageUrl-ipfs" | "imageUrl" | "!imageUrl-ipfs";
/**
* An individual record for each step during avatar resolution.
*/
export interface AvatarLinkage {
/**
* The type of linkage.
*/
type: AvatarLinkageType;
/**
* The linkage value.
*/
value: string;
};
/**
* When resolving an avatar for an ENS name, there are many
* steps involved, fetching metadata, validating results, et cetera.
*
* Some applications may wish to analyse this data, or use this data
* to diagnose promblems, so an **AvatarResult** provides details of
* each completed step during avatar resolution.
*/
export interface AvatarResult {
/**
* How the [[url]] was arrived at, resolving the many steps required
* for an avatar URL.
*/
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;
};
/**
* A provider plugin super-class for processing multicoin address types.
*/
export abstract class MulticoinProviderPlugin implements AbstractProviderPlugin {
/**
* The name.
*/
readonly name!: string;
/**
* Creates a new **MulticoinProviderPluing** for %%name%%.
*/
constructor(name: string) {
defineProperties<MulticoinProviderPlugin>(this, { name });
}
connect(proivder: Provider): MulticoinProviderPlugin {
return this;
}
/**
* Returns ``true`` if %%coinType%% is supported by this plugin.
*/
supportsCoinType(coinType: number): boolean {
return false;
}
/**
* Resolves to the encoded %%address%% for %%coinType%%.
*/
async encodeAddress(coinType: number, address: string): Promise<string> {
throw new Error("unsupported coin");
}
/**
* Resolves to the decoded %%data%% for %%coinType%%.
*/
async decodeAddress(coinType: number, data: BytesLike): Promise<string> {
throw new Error("unsupported coin");
}
}
const BasicMulticoinPluginId = "org.ethers.plugins.provider.BasicMulticoin";
/**
* A **BasicMulticoinProviderPlugin** provides service for common
* coin types, which do not require additional libraries to encode or
* decode.
*/
export class BasicMulticoinProviderPlugin extends MulticoinProviderPlugin {
/**
* Creates a new **BasicMulticoinProviderPlugin**.
*/
constructor() {
super(BasicMulticoinPluginId);
}
}
const matcherIpfs = new RegExp("^(ipfs):/\/(.*)$", "i");
const matchers = [
new RegExp("^(https):/\/(.*)$", "i"),
new RegExp("^(data):(.*)$", "i"),
matcherIpfs,
new RegExp("^eip155:[0-9]+/(erc[0-9]+):(.*)$", "i"),
];
/**
* A connected object to a resolved ENS name resolver, which can be
* used to query additional details.
*/
export class EnsResolver {
/**
* The connected provider.
*/
provider!: AbstractProvider;
/**
* The address of the resolver.
*/
address!: string;
/**
* The name this resolver was resolved against.
*/
name!: string;
// For EIP-2544 names, the ancestor that provided the resolver
#supports2544: null | Promise<boolean>;
#resolver: Contract;
constructor(provider: AbstractProvider, address: string, name: string) {
defineProperties<EnsResolver>(this, { provider, address, name });
this.#supports2544 = null;
this.#resolver = new Contract(address, [
"function supportsInterface(bytes4) view returns (bool)",
"function resolve(bytes, bytes) view returns (bytes)",
"function addr(bytes32) view returns (address)",
"function addr(bytes32, uint) view returns (bytes)",
"function text(bytes32, string) view returns (string)",
"function contenthash(bytes32) view returns (bytes)",
], provider);
}
/**
* Resolves to true if the resolver supports wildcard resolution.
*/
async supportsWildcard(): Promise<boolean> {
if (this.#supports2544 == null) {
this.#supports2544 = (async () => {
try {
return await this.#resolver.supportsInterface("0x9061b923");
} catch (error) {
// Wildcard resolvers must understand supportsInterface
// and return true.
if (isError(error, "CALL_EXCEPTION")) { return false; }
// Let future attempts try again...
this.#supports2544 = null;
throw error;
}
})();
}
return await this.#supports2544;
}
async #fetch(funcName: string, params?: Array<any>): Promise<null | any> {
params = (params || []).slice();
const iface = this.#resolver.interface;
// The first parameters is always the nodehash
params.unshift(namehash(this.name))
let fragment: null | FunctionFragment = null;
if (await this.supportsWildcard()) {
fragment = iface.getFunction(funcName);
assert(fragment, "missing fragment", "UNKNOWN_ERROR", {
info: { funcName }
});
params = [
dnsEncode(this.name, 255),
iface.encodeFunctionData(fragment, params)
];
funcName = "resolve(bytes,bytes)";
}
params.push({
enableCcipRead: true
});
try {
const result = await this.#resolver[funcName](...params);
if (fragment) {
return iface.decodeFunctionResult(fragment, result)[0];
}
return result;
} catch (error: any) {
if (!isError(error, "CALL_EXCEPTION")) { throw error; }
}
return null;
}
/**
* Resolves to the address for %%coinType%% or null if the
* provided %%coinType%% has not been configured.
*/
async getAddress(coinType?: number): Promise<null | string> {
if (coinType == null) { coinType = 60; }
if (coinType === 60) {
try {
const result = await this.#fetch("addr(bytes32)");
// No address
if (result == null || result === ZeroAddress) { return null; }
return result;
} catch (error: any) {
if (isError(error, "CALL_EXCEPTION")) { return null; }
throw error;
}
}
// Try decoding its EVM canonical chain as an EVM chain address first
if (coinType >= 0 && coinType < 0x80000000) {
let ethCoinType = coinType + 0x80000000;
const data = await this.#fetch("addr(bytes32,uint)", [ ethCoinType ]);
if (isHexString(data, 20)) { return getAddress(data); }
}
let coinPlugin: null | MulticoinProviderPlugin = null;
for (const plugin of this.provider.plugins) {
if (!(plugin instanceof MulticoinProviderPlugin)) { continue; }
if (plugin.supportsCoinType(coinType)) {
coinPlugin = plugin;
break;
}
}
if (coinPlugin == null) { return null; }
// keccak256("addr(bytes32,uint256")
const data = await this.#fetch("addr(bytes32,uint)", [ coinType ]);
// No address
if (data == null || data === "0x") { return null; }
// Compute the address
const address = await coinPlugin.decodeAddress(coinType, data);
if (address != null) { return address; }
assert(false, `invalid coin data`, "UNSUPPORTED_OPERATION", {
operation: `getAddress(${ coinType })`,
info: { coinType, data }
});
}
/**
* Resolves to the EIP-634 text record for %%key%%, or ``null``
* if unconfigured.
*/
async getText(key: string): Promise<null | string> {
const data = await this.#fetch("text(bytes32,string)", [ key ]);
if (data == null || data === "0x") { return null; }
return data;
}
/**
* Rsolves to the content-hash or ``null`` if unconfigured.
*/
async getContentHash(): Promise<null | string> {
// keccak256("contenthash()")
const data = await this.#fetch("contenthash(bytes32)");
// No contenthash
if (data == null || data === "0x") { return null; }
// IPFS (CID: 1, Type: 70=DAG-PB, 72=libp2p-key)
const ipfs = data.match(/^0x(e3010170|e5010172)(([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f]*))$/);
if (ipfs) {
const scheme = (ipfs[1] === "e3010170") ? "ipfs": "ipns";
const length = parseInt(ipfs[4], 16);
if (ipfs[5].length === length * 2) {
return `${ scheme }:/\/${ encodeBase58("0x" + ipfs[2])}`;
}
}
// Swarm (CID: 1, Type: swarm-manifest; hash/length hard-coded to keccak256/32)
const swarm = data.match(/^0xe40101fa011b20([0-9a-f]*)$/)
if (swarm && swarm[1].length === 64) {
return `bzz:/\/${ swarm[1] }`;
}
assert(false, `invalid or unsupported content hash data`, "UNSUPPORTED_OPERATION", {
operation: "getContentHash()",
info: { data }
});
}
/**
* Resolves to the avatar url or ``null`` if the avatar is either
* unconfigured or incorrectly configured (e.g. references an NFT
* not owned by the address).
*
* If diagnosing issues with configurations, the [[_getAvatar]]
* method may be useful.
*/
async getAvatar(): Promise<null | string> {
const avatar = await this._getAvatar();
return avatar.url;
}
/**
* When resolving an avatar, there are many steps involved, such
* fetching metadata and possibly validating ownership of an
* NFT.
*
* This method can be used to examine each step and the value it
* was working from.
*/
async _getAvatar(): Promise<AvatarResult> {
const linkage: Array<AvatarLinkage> = [ { type: "name", value: this.name } ];
try {
// test data for ricmoo.eth
//const avatar = "eip155:1/erc721:0x265385c7f4132228A0d54EB1A9e7460b91c0cC68/29233";
const avatar = await this.getText("avatar");
if (avatar == null) {
linkage.push({ type: "!avatar", value: "" });
return { url: null, linkage };
}
linkage.push({ type: "avatar", value: avatar });
for (let i = 0; i < matchers.length; i++) {
const match = avatar.match(matchers[i]);
if (match == null) { continue; }
const scheme = match[1].toLowerCase();
switch (scheme) {
case "https":
case "data":
linkage.push({ type: "url", value: avatar });
return { linkage, url: avatar };
case "ipfs": {
const url = getIpfsLink(avatar);
linkage.push({ type: "ipfs", value: avatar });
linkage.push({ type: "url", value: url });
return { linkage, url };
}
case "erc721":
case "erc1155": {
// Depending on the ERC type, use tokenURI(uint256) or url(uint256)
const selector = (scheme === "erc721") ? "tokenURI(uint256)": "uri(uint256)";
linkage.push({ type: scheme, value: avatar });
// The owner of this name
const owner = await this.getAddress();
if (owner == null) {
linkage.push({ type: "!owner", value: "" });
return { url: null, linkage };
}
const comps = (match[2] || "").split("/");
if (comps.length !== 2) {
linkage.push({ type: <any>`!${ scheme }caip`, value: (match[2] || "") });
return { url: null, linkage };
}
const tokenId = comps[1];
const contract = new Contract(comps[0], [
// ERC-721
"function tokenURI(uint) view returns (string)",
"function ownerOf(uint) view returns (address)",
// ERC-1155
"function uri(uint) view returns (string)",
"function balanceOf(address, uint256) view returns (uint)"
], this.provider);
// Check that this account owns the token
if (scheme === "erc721") {
const tokenOwner = await contract.ownerOf(tokenId);
if (owner !== tokenOwner) {
linkage.push({ type: "!owner", value: tokenOwner });
return { url: null, linkage };
}
linkage.push({ type: "owner", value: tokenOwner });
} else if (scheme === "erc1155") {
const balance = await contract.balanceOf(owner, tokenId);
if (!balance) {
linkage.push({ type: "!balance", value: "0" });
return { url: null, linkage };
}
linkage.push({ type: "balance", value: balance.toString() });
}
// Call the token contract for the metadata URL
let metadataUrl = await contract[selector](tokenId);
if (metadataUrl == null || metadataUrl === "0x") {
linkage.push({ type: "!metadata-url", value: "" });
return { url: null, linkage };
}
linkage.push({ type: "metadata-url-base", value: metadataUrl });
// ERC-1155 allows a generic {id} in the URL
if (scheme === "erc1155") {
metadataUrl = metadataUrl.replace("{id}", toBeHex(tokenId, 32).substring(2));
linkage.push({ type: "metadata-url-expanded", value: metadataUrl });
}
// Transform IPFS metadata links
if (metadataUrl.match(/^ipfs:/i)) {
metadataUrl = getIpfsLink(metadataUrl);
}
linkage.push({ type: "metadata-url", value: metadataUrl });
// Get the token metadata
let metadata: any = { };
const response = await (new FetchRequest(metadataUrl)).send();
response.assertOk();
try {
metadata = response.bodyJson;
} catch (error) {
try {
linkage.push({ type: "!metadata", value: response.bodyText });
} catch (error) {
const bytes = response.body;
if (bytes) {
linkage.push({ type: "!metadata", value: hexlify(bytes) });
}
return { url: null, linkage };
}
return { url: null, linkage };
}
if (!metadata) {
linkage.push({ type: "!metadata", value: "" });
return { url: null, linkage };
}
linkage.push({ type: "metadata", value: JSON.stringify(metadata) });
// Pull the image URL out
let imageUrl = metadata.image;
if (typeof(imageUrl) !== "string") {
linkage.push({ type: "!imageUrl", value: "" });
return { url: null, linkage };
}
if (imageUrl.match(/^(https:\/\/|data:)/i)) {
// Allow
} else {
// Transform IPFS link to gateway
const ipfs = imageUrl.match(matcherIpfs);
if (ipfs == null) {
linkage.push({ type: "!imageUrl-ipfs", value: imageUrl });
return { url: null, linkage };
}
linkage.push({ type: "imageUrl-ipfs", value: imageUrl });
imageUrl = getIpfsLink(imageUrl);
}
linkage.push({ type: "url", value: imageUrl });
return { linkage, url: imageUrl };
}
}
}
} catch (error) { }
return { linkage, url: null };
}
static async getEnsAddress(provider: Provider): Promise<string> {
const network = await provider.getNetwork();
const ensPlugin = network.getPlugin<EnsPlugin>("org.ethers.plugins.network.Ens");
// No ENS...
assert(ensPlugin, "network does not support ENS", "UNSUPPORTED_OPERATION", {
operation: "getEnsAddress", info: { network } });
return ensPlugin.address;
}
static async #getResolver(provider: Provider, name: string): Promise<null | string> {
const ensAddr = await EnsResolver.getEnsAddress(provider);
try {
const contract = new Contract(ensAddr, [
"function resolver(bytes32) view returns (address)"
], provider);
const addr = await contract.resolver(namehash(name), {
enableCcipRead: true
});
if (addr === ZeroAddress) { return null; }
return addr;
} catch (error) {
// ENS registry cannot throw errors on resolver(bytes32),
// so probably a link error
throw error;
}
return null;
}
/**
* Resolve to the ENS resolver for %%name%% using %%provider%% or
* ``null`` if unconfigured.
*/
static async fromName(provider: AbstractProvider, name: string): Promise<null | EnsResolver> {
let currentName = name;
while (true) {
if (currentName === "" || currentName === ".") { return null; }
// Optimization since the eth node cannot change and does
// not have a wildcard resolver
if (name !== "eth" && currentName === "eth") { return null; }
// Check the current node for a resolver
const addr = await EnsResolver.#getResolver(provider, currentName);
// Found a resolver!
if (addr != null) {
const resolver = new EnsResolver(provider, addr, name);
// Legacy resolver found, using EIP-2544 so it isn't safe to use
if (currentName !== name && !(await resolver.supportsWildcard())) { return null; }
return resolver;
}
// Get the parent node
currentName = currentName.split(".").slice(1).join(".");
}
}
}

335
dev/env/node_modules/ethers/src.ts/providers/format.ts generated vendored Executable file
View File

@@ -0,0 +1,335 @@
/**
* @_ignore
*/
import { getAddress, getCreateAddress } from "../address/index.js";
import { Signature } from "../crypto/index.js"
import { accessListify } from "../transaction/index.js";
import {
getBigInt, getNumber, hexlify, isHexString, zeroPadValue,
assert, assertArgument
} from "../utils/index.js";
import type { SignatureLike } from "../crypto/index.js"
import type {
BlockParams, LogParams,
TransactionReceiptParams, TransactionResponseParams,
} from "./formatting.js";
const BN_0 = BigInt(0);
export type FormatFunc = (value: any) => any;
export function allowNull(format: FormatFunc, nullValue?: any): FormatFunc {
return (function(value: any) {
if (value == null) { return nullValue; }
return format(value);
});
}
export function arrayOf(format: FormatFunc, allowNull?: boolean): FormatFunc {
return ((array: any) => {
if (allowNull && array == null) { return null; }
if (!Array.isArray(array)) { throw new Error("not an array"); }
return array.map((i) => format(i));
});
}
// Requires an object which matches a fleet of other formatters
// Any FormatFunc may return `undefined` to have the value omitted
// from the result object. Calls preserve `this`.
export function object(format: Record<string, FormatFunc>, altNames?: Record<string, Array<string>>): FormatFunc {
return ((value: any) => {
const result: any = { };
for (const key in format) {
let srcKey = key;
if (altNames && key in altNames && !(srcKey in value)) {
for (const altKey of altNames[key]) {
if (altKey in value) {
srcKey = altKey;
break;
}
}
}
try {
const nv = format[key](value[srcKey]);
if (nv !== undefined) { result[key] = nv; }
} catch (error) {
const message = (error instanceof Error) ? error.message: "not-an-error";
assert(false, `invalid value for value.${ key } (${ message })`, "BAD_DATA", { value })
}
}
return result;
});
}
export function formatBoolean(value: any): boolean {
switch (value) {
case true: case "true":
return true;
case false: case "false":
return false;
}
assertArgument(false, `invalid boolean; ${ JSON.stringify(value) }`, "value", value);
}
export function formatData(value: string): string {
assertArgument(isHexString(value, true), "invalid data", "value", value);
return value;
}
export function formatHash(value: any): string {
assertArgument(isHexString(value, 32), "invalid hash", "value", value);
return value;
}
export function formatUint256(value: any): string {
if (!isHexString(value)) {
throw new Error("invalid uint256");
}
return zeroPadValue(value, 32);
}
const _formatLog = object({
address: getAddress,
blockHash: formatHash,
blockNumber: getNumber,
data: formatData,
index: getNumber,
removed: allowNull(formatBoolean, false),
topics: arrayOf(formatHash),
transactionHash: formatHash,
transactionIndex: getNumber,
}, {
index: [ "logIndex" ]
});
export function formatLog(value: any): LogParams {
return _formatLog(value);
}
const _formatBlock = object({
hash: allowNull(formatHash),
parentHash: formatHash,
parentBeaconBlockRoot: allowNull(formatHash, null),
number: getNumber,
timestamp: getNumber,
nonce: allowNull(formatData),
difficulty: getBigInt,
gasLimit: getBigInt,
gasUsed: getBigInt,
stateRoot: allowNull(formatHash, null),
receiptsRoot: allowNull(formatHash, null),
blobGasUsed: allowNull(getBigInt, null),
excessBlobGas: allowNull(getBigInt, null),
miner: allowNull(getAddress),
prevRandao: allowNull(formatHash, null),
extraData: formatData,
baseFeePerGas: allowNull(getBigInt)
}, {
prevRandao: [ "mixHash" ]
});
export function formatBlock(value: any): BlockParams {
const result = _formatBlock(value);
result.transactions = value.transactions.map((tx: string | TransactionResponseParams) => {
if (typeof(tx) === "string") { return tx; }
return formatTransactionResponse(tx);
});
return result;
}
const _formatReceiptLog = object({
transactionIndex: getNumber,
blockNumber: getNumber,
transactionHash: formatHash,
address: getAddress,
topics: arrayOf(formatHash),
data: formatData,
index: getNumber,
blockHash: formatHash,
}, {
index: [ "logIndex" ]
});
export function formatReceiptLog(value: any): LogParams {
return _formatReceiptLog(value);
}
const _formatTransactionReceipt = object({
to: allowNull(getAddress, null),
from: allowNull(getAddress, null),
contractAddress: allowNull(getAddress, null),
// should be allowNull(hash), but broken-EIP-658 support is handled in receipt
index: getNumber,
root: allowNull(hexlify),
gasUsed: getBigInt,
blobGasUsed: allowNull(getBigInt, null),
logsBloom: allowNull(formatData),
blockHash: formatHash,
hash: formatHash,
logs: arrayOf(formatReceiptLog),
blockNumber: getNumber,
//confirmations: allowNull(getNumber, null),
cumulativeGasUsed: getBigInt,
effectiveGasPrice: allowNull(getBigInt),
blobGasPrice: allowNull(getBigInt, null),
status: allowNull(getNumber),
type: allowNull(getNumber, 0)
}, {
effectiveGasPrice: [ "gasPrice" ],
hash: [ "transactionHash" ],
index: [ "transactionIndex" ],
});
export function formatTransactionReceipt(value: any): TransactionReceiptParams {
return _formatTransactionReceipt(value);
}
export function formatTransactionResponse(value: any): TransactionResponseParams {
// Some clients (TestRPC) do strange things like return 0x0 for the
// 0 address; correct this to be a real address
if (value.to && getBigInt(value.to) === BN_0) {
value.to = "0x0000000000000000000000000000000000000000";
}
const result = object({
hash: formatHash,
// Some nodes do not return this, usually test nodes (like Ganache)
index: allowNull(getNumber, undefined),
type: (value: any) => {
if (value === "0x" || value == null) { return 0; }
return getNumber(value);
},
accessList: allowNull(accessListify, null),
blobVersionedHashes: allowNull(arrayOf(formatHash, true), null),
authorizationList: allowNull(arrayOf((v: any) => {
let sig: SignatureLike;
if (v.signature) {
sig = v.signature;
} else {
let yParity = v.yParity;
if (yParity === "0x1b") {
yParity = 0;
} else if (yParity === "0x1c") {
yParity = 1;
}
sig = Object.assign({ }, v, { yParity });
}
return {
address: getAddress(v.address),
chainId: getBigInt(v.chainId),
nonce: getBigInt(v.nonce),
signature: Signature.from(sig)
};
}, false), null),
blockHash: allowNull(formatHash, null),
blockNumber: allowNull(getNumber, null),
transactionIndex: allowNull(getNumber, null),
from: getAddress,
// either (gasPrice) or (maxPriorityFeePerGas + maxFeePerGas) must be set
gasPrice: allowNull(getBigInt),
maxPriorityFeePerGas: allowNull(getBigInt),
maxFeePerGas: allowNull(getBigInt),
maxFeePerBlobGas: allowNull(getBigInt, null),
gasLimit: getBigInt,
to: allowNull(getAddress, null),
value: getBigInt,
nonce: getNumber,
data: formatData,
creates: allowNull(getAddress, null),
chainId: allowNull(getBigInt, null)
}, {
data: [ "input" ],
gasLimit: [ "gas" ],
index: [ "transactionIndex" ]
})(value);
// If to and creates are empty, populate the creates from the value
if (result.to == null && result.creates == null) {
result.creates = getCreateAddress(result);
}
// @TODO: Check fee data
// Add an access list to supported transaction types
if ((value.type === 1 || value.type === 2) && value.accessList == null) {
result.accessList = [ ];
}
// Compute the signature
if (value.signature) {
result.signature = Signature.from(value.signature);
} else {
result.signature = Signature.from(value);
}
// Some backends omit ChainId on legacy transactions, but we can compute it
if (result.chainId == null) {
const chainId = result.signature.legacyChainId;
if (chainId != null) { result.chainId = chainId; }
}
// @TODO: check chainID
/*
if (value.chainId != null) {
let chainId = value.chainId;
if (isHexString(chainId)) {
chainId = BigNumber.from(chainId).toNumber();
}
result.chainId = chainId;
} else {
let chainId = value.networkId;
// geth-etc returns chainId
if (chainId == null && result.v == null) {
chainId = value.chainId;
}
if (isHexString(chainId)) {
chainId = BigNumber.from(chainId).toNumber();
}
if (typeof(chainId) !== "number" && result.v != null) {
chainId = (result.v - 35) / 2;
if (chainId < 0) { chainId = 0; }
chainId = parseInt(chainId);
}
if (typeof(chainId) !== "number") { chainId = 0; }
result.chainId = chainId;
}
*/
// 0x0000... should actually be null
if (result.blockHash && getBigInt(result.blockHash) === BN_0) {
result.blockHash = null;
}
return result;
}

418
dev/env/node_modules/ethers/src.ts/providers/formatting.ts generated vendored Executable file
View File

@@ -0,0 +1,418 @@
/**
* About provider formatting?
*
* @_section: api/providers/formatting:Formatting [provider-formatting]
*/
import type { Signature } from "../crypto/index.js";
import type { Authorization, AccessList } from "../transaction/index.js";
//////////////////////
// Block
/**
* a **BlockParams** encodes the minimal required properties for a
* formatted block.
*/
export interface BlockParams {
/**
* The block hash.
*/
hash?: null | string;
/**
* The block number.
*/
number: number;
/**
* The timestamp for this block, which is the number of seconds
* since epoch that this block was included.
*/
timestamp: number;
/**
* The hash of the previous block in the blockchain. The genesis block
* has the parentHash of the [[ZeroHash]].
*/
parentHash: string;
/**
* The hash tree root of the parent beacon block for the given
* execution block. See [[link-eip-4788]].
*/
parentBeaconBlockRoot?: null | string;
/**
* A random sequence provided during the mining process for
* proof-of-work networks.
*/
nonce: string;
/**
* For proof-of-work networks, the difficulty target is used to
* adjust the difficulty in mining to ensure an expected block rate.
*/
difficulty: bigint;
/**
* The maximum amount of gas a block can consume.
*/
gasLimit: bigint;
/**
* The amount of gas a block consumed.
*/
gasUsed: bigint;
/**
* The total amount of BLOb gas consumed by transactions within
* the block. See [[link-eip4844].
*/
blobGasUsed?: null | bigint;
/**
* The running total of BLOb gas consumed in excess of the target
* prior to the block. See [[link-eip-4844]].
*/
excessBlobGas?: null | bigint;
/**
* The miner (or author) of a block.
*/
miner: string;
/**
* The latest RANDAO mix of the post beacon state of
* the previous block.
*/
prevRandao?: null | string;
/**
* Additional data the miner choose to include.
*/
extraData: string;
/**
* The protocol-defined base fee per gas in an [[link-eip-1559]]
* block.
*/
baseFeePerGas: null | bigint;
/**
* The root hash for the global state after applying changes
* in this block.
*/
stateRoot?: null | string;
/**
* The hash of the transaction receipts trie.
*/
receiptsRoot?: null | string;
/**
* The list of transactions in the block.
*/
transactions: ReadonlyArray<string | TransactionResponseParams>;
};
//////////////////////
// Log
/**
* a **LogParams** encodes the minimal required properties for a
* formatted log.
*/
export interface LogParams {
/**
* The transaction hash for the transaxction the log occurred in.
*/
transactionHash: string;
/**
* The block hash of the block that included the transaction for this
* log.
*/
blockHash: string;
/**
* The block number of the block that included the transaction for this
* log.
*/
blockNumber: number;
/**
* Whether this log was removed due to the transaction it was included
* in being removed dur to an orphaned block.
*/
removed: boolean;
/**
* The address of the contract that emitted this log.
*/
address: string;
/**
* The data emitted with this log.
*/
data: string;
/**
* The topics emitted with this log.
*/
topics: ReadonlyArray<string>;
/**
* The index of this log.
*/
index: number;
/**
* The transaction index of this log.
*/
transactionIndex: number;
}
//////////////////////
// Transaction Receipt
/**
* a **TransactionReceiptParams** encodes the minimal required properties
* for a formatted transaction receipt.
*/
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;
/**
* The sender of the transaction.
*/
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;
/**
* The transaction hash.
*/
hash: string;
/**
* The transaction index.
*/
index: number;
/**
* The block hash of the block that included this transaction.
*/
blockHash: string;
/**
* The block number of the block that included this transaction.
*/
blockNumber: number;
/**
* The bloom filter for the logs emitted during execution of this
* transaction.
*/
logsBloom: string;
/**
* The logs emitted during the execution of this transaction.
*/
logs: ReadonlyArray<LogParams>;
/**
* The amount of gas consumed executing this transaction.
*/
gasUsed: bigint;
/**
* The amount of BLOb gas used. See [[link-eip-4844]].
*/
blobGasUsed?: null | bigint;
/**
* The total amount of gas consumed during the entire block up to
* and including this transaction.
*/
cumulativeGasUsed: bigint;
/**
* The actual gas price per gas charged for this transaction.
*/
gasPrice?: null | bigint;
/**
* The actual BLOb gas price that was charged. See [[link-eip-4844]].
*/
blobGasPrice?: null | bigint;
/**
* The actual gas price per gas charged for this transaction.
*/
effectiveGasPrice?: null | bigint;
/**
* The [[link-eip-2718]] envelope type.
*/
type: number;
//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;
/**
* The root of this transaction in a pre-bazatium block. In
* post-byzantium blocks this is null.
*/
root: null | string;
}
/*
export interface LegacyTransactionReceipt {
byzantium: false;
status: null;
root: string;
}
export interface ByzantiumTransactionReceipt {
byzantium: true;
status: number;
root: null;
}
*/
//////////////////////
// Transaction Response
/**
* a **TransactionResponseParams** encodes the minimal required properties
* for a formatted transaction response.
*/
export interface TransactionResponseParams {
/**
* The block number of the block that included this transaction.
*/
blockNumber: null | number;
/**
* The block hash of the block that included this transaction.
*/
blockHash: null | string;
/**
* The transaction hash.
*/
hash: string;
/**
* The transaction index.
*/
index: number;
/**
* The [[link-eip-2718]] transaction type.
*/
type: number;
/**
* The target of the transaction. If ``null``, the ``data`` is initcode
* and this transaction is a deployment transaction.
*/
to: null | string;
/**
* The sender of the transaction.
*/
from: string;
/**
* The nonce of the transaction, used for replay protection.
*/
nonce: number;
/**
* The maximum amount of gas this transaction is authorized to consume.
*/
gasLimit: bigint;
/**
* For legacy transactions, this is the gas price per gas to pay.
*/
gasPrice: bigint;
/**
* For [[link-eip-1559]] transactions, this is the maximum priority
* fee to allow a producer to claim.
*/
maxPriorityFeePerGas: null | bigint;
/**
* For [[link-eip-1559]] transactions, this is the maximum fee that
* will be paid.
*/
maxFeePerGas: null | bigint;
/**
* For [[link-eip-4844]] transactions, this is the maximum fee that
* will be paid per BLOb.
*/
maxFeePerBlobGas?: null | bigint;
/**
* The transaction data.
*/
data: string;
/**
* The transaction value (in wei).
*/
value: bigint;
/**
* The chain ID this transaction is valid on.
*/
chainId: bigint;
/**
* The signature of the transaction.
*/
signature: Signature;
/**
* The transaction access list.
*/
accessList: null | AccessList;
/**
* The [[link-eip-4844]] BLOb versioned hashes.
*/
blobVersionedHashes?: null | Array<string>; // @TODO: drop the "?"? (v7)
/**
* The [[link-eip-7702]] authorizations (if any).
*/
authorizationList: null | Array<Authorization>;
};

135
dev/env/node_modules/ethers/src.ts/providers/index.ts generated vendored Executable file
View File

@@ -0,0 +1,135 @@
/**
* 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]
*/
export {
AbstractProvider, UnmanagedSubscriber
} from "./abstract-provider.js";
export {
AbstractSigner,
VoidSigner,
} from "./abstract-signer.js";
export {
showThrottleMessage
} from "./community.js";
export { getDefaultProvider } from "./default-provider.js";
export {
EnsResolver,
MulticoinProviderPlugin
} from "./ens-resolver.js";
export { Network } from "./network.js";
export { NonceManager } from "./signer-noncemanager.js";
export {
NetworkPlugin,
GasCostPlugin,
EnsPlugin,
FeeDataNetworkPlugin,
FetchUrlFeeDataNetworkPlugin,
} from "./plugins-network.js";
export {
Block,
FeeData,
Log,
TransactionReceipt,
TransactionResponse,
copyRequest,
//resolveTransactionRequest,
} from "./provider.js";
export { FallbackProvider } from "./provider-fallback.js";
export { JsonRpcApiProvider, JsonRpcProvider, JsonRpcSigner } from "./provider-jsonrpc.js"
export { BrowserProvider } from "./provider-browser.js";
export { AlchemyProvider } from "./provider-alchemy.js";
export { BlockscoutProvider } from "./provider-blockscout.js";
export { AnkrProvider } from "./provider-ankr.js";
export { CloudflareProvider } from "./provider-cloudflare.js";
export { ChainstackProvider } from "./provider-chainstack.js";
export { EtherscanProvider, EtherscanPlugin } from "./provider-etherscan.js";
export { InfuraProvider, InfuraWebSocketProvider } from "./provider-infura.js";
export { PocketProvider } from "./provider-pocket.js";
export { QuickNodeProvider } from "./provider-quicknode.js";
import { IpcSocketProvider } from "./provider-ipcsocket.js"; /*-browser*/
export { IpcSocketProvider };
export { SocketProvider } from "./provider-socket.js";
export { WebSocketProvider } from "./provider-websocket.js";
export {
SocketSubscriber, SocketBlockSubscriber, SocketPendingSubscriber,
SocketEventSubscriber
} from "./provider-socket.js";
export type {
AbstractProviderOptions, Subscription, Subscriber,
AbstractProviderPlugin,
PerformActionFilter, PerformActionTransaction, PerformActionRequest,
} from "./abstract-provider.js"
export type { ContractRunner } from "./contracts.js";
export type {
BlockParams, LogParams, TransactionReceiptParams,
TransactionResponseParams,
} from "./formatting.js";
export type {
CommunityResourcable
} from "./community.js";
/*
export type {
AvatarLinkageType, AvatarLinkage, AvatarResult
} from "./ens-resolver.js";
*/
export type { Networkish } from "./network.js";
export type { GasCostParameters } from "./plugins-network.js";
export type {
BlockTag,
TransactionRequest, PreparedTransactionRequest,
EventFilter, Filter, FilterByBlockHash, OrphanFilter, ProviderEvent,
TopicFilter,
Provider,
MinedBlock, MinedTransactionResponse
} from "./provider.js";
export type {
BrowserDiscoverOptions, BrowserProviderOptions, DebugEventBrowserProvider,
Eip1193Provider, Eip6963ProviderInfo
} from "./provider-browser.js";
export type { FallbackProviderOptions } from "./provider-fallback.js";
export type {
JsonRpcPayload, JsonRpcResult, JsonRpcError,
JsonRpcApiProviderOptions,
JsonRpcTransactionRequest,
} from "./provider-jsonrpc.js";
export type {
WebSocketCreator, WebSocketLike
} from "./provider-websocket.js";
export type { Signer } from "./signer.js";

438
dev/env/node_modules/ethers/src.ts/providers/network.ts generated vendored Executable file
View File

@@ -0,0 +1,438 @@
/**
* A **Network** encapsulates the various properties required to
* interact with a specific chain.
*
* @_subsection: api/providers:Networks [networks]
*/
import { accessListify } from "../transaction/index.js";
import { getBigInt, assert, assertArgument } from "../utils/index.js";
import {
EnsPlugin, FetchUrlFeeDataNetworkPlugin, GasCostPlugin
} from "./plugins-network.js";
import type { BigNumberish } from "../utils/index.js";
import type { TransactionLike } from "../transaction/index.js";
import type { NetworkPlugin } from "./plugins-network.js";
/**
* A Networkish can be used to allude to a Network, by specifing:
* - a [[Network]] object
* - a well-known (or registered) network name
* - a well-known (or registered) chain ID
* - an object with sufficient details to describe a network
*/
export type Networkish = Network | number | bigint | string | {
name?: string,
chainId?: number,
//layerOneConnection?: Provider,
ensAddress?: string,
ensNetwork?: number
};
/* * * *
// Networks which operation against an L2 can use this plugin to
// specify how to access L1, for the purpose of resolving ENS,
// for example.
export class LayerOneConnectionPlugin extends NetworkPlugin {
readonly provider!: Provider;
// @TODO: Rename to ChainAccess and allow for connecting to any chain
constructor(provider: Provider) {
super("org.ethers.plugins.layer-one-connection");
defineProperties<LayerOneConnectionPlugin>(this, { provider });
}
clone(): LayerOneConnectionPlugin {
return new LayerOneConnectionPlugin(this.provider);
}
}
*/
const Networks: Map<string | bigint, () => Network> = new Map();
/**
* A **Network** provides access to a chain's properties and allows
* for plug-ins to extend functionality.
*/
export class Network {
#name: string;
#chainId: bigint;
#plugins: Map<string, NetworkPlugin>;
/**
* Creates a new **Network** for %%name%% and %%chainId%%.
*/
constructor(name: string, chainId: BigNumberish) {
this.#name = name;
this.#chainId = getBigInt(chainId);
this.#plugins = new Map();
}
/**
* Returns a JSON-compatible representation of a Network.
*/
toJSON(): any {
return { name: this.name, chainId: String(this.chainId) };
}
/**
* The network common name.
*
* This is the canonical name, as networks migh have multiple
* names.
*/
get name(): string { return this.#name; }
set name(value: string) { this.#name = value; }
/**
* The network chain ID.
*/
get chainId(): bigint { return this.#chainId; }
set chainId(value: BigNumberish) { this.#chainId = getBigInt(value, "chainId"); }
/**
* Returns true if %%other%% matches this network. Any chain ID
* must match, and if no chain ID is present, the name must match.
*
* This method does not currently check for additional properties,
* such as ENS address or plug-in compatibility.
*/
matches(other: Networkish): boolean {
if (other == null) { return false; }
if (typeof(other) === "string") {
try {
return (this.chainId === getBigInt(other));
} catch (error) { }
return (this.name === other);
}
if (typeof(other) === "number" || typeof(other) === "bigint") {
try {
return (this.chainId === getBigInt(other));
} catch (error) { }
return false;
}
if (typeof(other) === "object") {
if (other.chainId != null) {
try {
return (this.chainId === getBigInt(other.chainId));
} catch (error) { }
return false;
}
if (other.name != null) {
return (this.name === other.name);
}
return false;
}
return false;
}
/**
* Returns the list of plugins currently attached to this Network.
*/
get plugins(): Array<NetworkPlugin> {
return Array.from(this.#plugins.values());
}
/**
* Attach a new %%plugin%% to this Network. The network name
* must be unique, excluding any fragment.
*/
attachPlugin(plugin: NetworkPlugin): this {
if (this.#plugins.get(plugin.name)) {
throw new Error(`cannot replace existing plugin: ${ plugin.name } `);
}
this.#plugins.set(plugin.name, plugin.clone());
return this;
}
/**
* Return the plugin, if any, matching %%name%% exactly. Plugins
* with fragments will not be returned unless %%name%% includes
* a fragment.
*/
getPlugin<T extends NetworkPlugin = NetworkPlugin>(name: string): null | T {
return <T>(this.#plugins.get(name)) || null;
}
/**
* Gets a list of all plugins that match %%name%%, with otr without
* a fragment.
*/
getPlugins<T extends NetworkPlugin = NetworkPlugin>(basename: string): Array<T> {
return <Array<T>>(this.plugins.filter((p) => (p.name.split("#")[0] === basename)));
}
/**
* Create a copy of this Network.
*/
clone(): Network {
const clone = new Network(this.name, this.chainId);
this.plugins.forEach((plugin) => {
clone.attachPlugin(plugin.clone());
});
return clone;
}
/**
* Compute the intrinsic gas required for a transaction.
*
* A GasCostPlugin can be attached to override the default
* values.
*/
computeIntrinsicGas(tx: TransactionLike): number {
const costs = this.getPlugin<GasCostPlugin>("org.ethers.plugins.network.GasCost") || (new GasCostPlugin());
let gas = costs.txBase;
if (tx.to == null) { gas += costs.txCreate; }
if (tx.data) {
for (let i = 2; i < tx.data.length; i += 2) {
if (tx.data.substring(i, i + 2) === "00") {
gas += costs.txDataZero;
} else {
gas += costs.txDataNonzero;
}
}
}
if (tx.accessList) {
const accessList = accessListify(tx.accessList);
for (const addr in accessList) {
gas += costs.txAccessListAddress + costs.txAccessListStorageKey * accessList[addr].storageKeys.length;
}
}
return gas;
}
/**
* Returns a new Network for the %%network%% name or chainId.
*/
static from(network?: Networkish): Network {
injectCommonNetworks();
// Default network
if (network == null) { return Network.from("mainnet"); }
// Canonical name or chain ID
if (typeof(network) === "number") { network = BigInt(network); }
if (typeof(network) === "string" || typeof(network) === "bigint") {
const networkFunc = Networks.get(network);
if (networkFunc) { return networkFunc(); }
if (typeof(network) === "bigint") {
return new Network("unknown", network);
}
assertArgument(false, "unknown network", "network", network);
}
// Clonable with network-like abilities
if (typeof((<Network>network).clone) === "function") {
const clone = (<Network>network).clone();
//if (typeof(network.name) !== "string" || typeof(network.chainId) !== "number") {
//}
return clone;
}
// Networkish
if (typeof(network) === "object") {
assertArgument(typeof(network.name) === "string" && typeof(network.chainId) === "number",
"invalid network object name or chainId", "network", network);
const custom = new Network(<string>(network.name), <number>(network.chainId));
if ((<any>network).ensAddress || (<any>network).ensNetwork != null) {
custom.attachPlugin(new EnsPlugin((<any>network).ensAddress, (<any>network).ensNetwork));
}
//if ((<any>network).layerOneConnection) {
// custom.attachPlugin(new LayerOneConnectionPlugin((<any>network).layerOneConnection));
//}
return custom;
}
assertArgument(false, "invalid network", "network", network);
}
/**
* Register %%nameOrChainId%% with a function which returns
* an instance of a Network representing that chain.
*/
static register(nameOrChainId: string | number | bigint, networkFunc: () => Network): void {
if (typeof(nameOrChainId) === "number") { nameOrChainId = BigInt(nameOrChainId); }
const existing = Networks.get(nameOrChainId);
if (existing) {
assertArgument(false, `conflicting network for ${ JSON.stringify(existing.name) }`, "nameOrChainId", nameOrChainId);
}
Networks.set(nameOrChainId, networkFunc);
}
}
type Options = {
ensNetwork?: number;
altNames?: Array<string>;
plugins?: Array<NetworkPlugin>;
};
// We don't want to bring in formatUnits because it is backed by
// FixedNumber and we want to keep Networks tiny. The values
// included by the Gas Stations are also IEEE 754 with lots of
// rounding issues and exceed the strict checks formatUnits has.
function parseUnits(_value: number | string, decimals: number): bigint {
const value = String(_value);
if (!value.match(/^[0-9.]+$/)) {
throw new Error(`invalid gwei value: ${ _value }`);
}
// Break into [ whole, fraction ]
const comps = value.split(".");
if (comps.length === 1) { comps.push(""); }
// More than 1 decimal point or too many fractional positions
if (comps.length !== 2) {
throw new Error(`invalid gwei value: ${ _value }`);
}
// Pad the fraction to 9 decimalplaces
while (comps[1].length < decimals) { comps[1] += "0"; }
// Too many decimals and some non-zero ending, take the ceiling
if (comps[1].length > 9) {
let frac = BigInt(comps[1].substring(0, 9));
if (!comps[1].substring(9).match(/^0+$/)) { frac++; }
comps[1] = frac.toString();
}
return BigInt(comps[0] + comps[1]);
}
// Used by Polygon to use a gas station for fee data
function getGasStationPlugin(url: string) {
return new FetchUrlFeeDataNetworkPlugin(url, async (fetchFeeData, provider, request) => {
// Prevent Cloudflare from blocking our request in node.js
request.setHeader("User-Agent", "ethers");
let response;
try {
const [ _response, _feeData ] = await Promise.all([
request.send(), fetchFeeData()
]);
response = _response;
const payload = response.bodyJson.standard;
const feeData = {
gasPrice: _feeData.gasPrice,
maxFeePerGas: parseUnits(payload.maxFee, 9),
maxPriorityFeePerGas: parseUnits(payload.maxPriorityFee, 9),
};
return feeData;
} catch (error: any) {
assert(false, `error encountered with polygon gas station (${ JSON.stringify(request.url) })`, "SERVER_ERROR", { request, response, error });
}
});
}
// See: https://chainlist.org
let injected = false;
function injectCommonNetworks(): void {
if (injected) { return; }
injected = true;
/// Register popular Ethereum networks
function registerEth(name: string, chainId: number, options: Options): void {
const func = function() {
const network = new Network(name, chainId);
// We use 0 to disable ENS
if (options.ensNetwork != null) {
network.attachPlugin(new EnsPlugin(null, options.ensNetwork));
}
network.attachPlugin(new GasCostPlugin());
(options.plugins || []).forEach((plugin) => {
network.attachPlugin(plugin);
});
return network;
};
// Register the network by name and chain ID
Network.register(name, func);
Network.register(chainId, func);
if (options.altNames) {
options.altNames.forEach((name) => {
Network.register(name, func);
});
}
}
registerEth("mainnet", 1, { ensNetwork: 1, altNames: [ "homestead" ] });
registerEth("ropsten", 3, { ensNetwork: 3 });
registerEth("rinkeby", 4, { ensNetwork: 4 });
registerEth("goerli", 5, { ensNetwork: 5 });
registerEth("kovan", 42, { ensNetwork: 42 });
registerEth("sepolia", 11155111, { ensNetwork: 11155111 });
registerEth("holesky", 17000, { ensNetwork: 17000 });
registerEth("classic", 61, { });
registerEth("classicKotti", 6, { });
registerEth("arbitrum", 42161, {
ensNetwork: 1,
});
registerEth("arbitrum-goerli", 421613, { });
registerEth("arbitrum-sepolia", 421614, { });
registerEth("base", 8453, { ensNetwork: 1 });
registerEth("base-goerli", 84531, { });
registerEth("base-sepolia", 84532, { });
registerEth("bnb", 56, { ensNetwork: 1 });
registerEth("bnbt", 97, { });
registerEth("filecoin", 314, { });
registerEth("filecoin-calibration", 314159, { });
registerEth("linea", 59144, { ensNetwork: 1 });
registerEth("linea-goerli", 59140, { });
registerEth("linea-sepolia", 59141, { });
registerEth("matic", 137, {
ensNetwork: 1,
plugins: [
getGasStationPlugin("https:/\/gasstation.polygon.technology/v2")
]
});
registerEth("matic-amoy", 80002, { });
registerEth("matic-mumbai", 80001, {
altNames: [ "maticMumbai", "maticmum" ], // @TODO: Future remove these alts
plugins: [
getGasStationPlugin("https:/\/gasstation-testnet.polygon.technology/v2")
]
});
registerEth("optimism", 10, {
ensNetwork: 1,
plugins: [ ]
});
registerEth("optimism-goerli", 420, { });
registerEth("optimism-sepolia", 11155420, { });
registerEth("xdai", 100, { ensNetwork: 1 });
}

8
dev/env/node_modules/ethers/src.ts/providers/pagination.ts generated vendored Executable file
View File

@@ -0,0 +1,8 @@
export interface PaginationResult<R> extends Array<R> {
next(): Promise<PaginationResult<R>>;
// The total number of results available or null if unknown
totalResults: null | number;
done: boolean;
}

View File

@@ -0,0 +1,35 @@
import { AbstractProviderPlugin } from "./abstract-provider.js";
import { defineProperties } from "../utils/index.js";
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
export const PluginIdFallbackProvider = "org.ethers.plugins.provider.QualifiedPlugin";
export class CheckQualifiedPlugin implements AbstractProviderPlugin {
declare name: string;
constructor() {
defineProperties<CheckQualifiedPlugin>(this, { name: PluginIdFallbackProvider });
}
connect(provider: AbstractProvider): CheckQualifiedPlugin {
return this;
}
// Retruns true if this value should be considered qualified for
// inclusion in the quorum.
isQualified(action: PerformActionRequest, result: any): boolean {
return true;
}
}
export class PossiblyPrunedTransactionPlugin extends CheckQualifiedPlugin {
isQualified(action: PerformActionRequest, result: any): boolean {
if (action.method === "getTransaction" || action.method === "getTransactionReceipt") {
if (result == null) { return false; }
}
return super.isQualified(action, result);
}
}

View File

@@ -0,0 +1,281 @@
import { defineProperties } from "../utils/properties.js";
import { assertArgument } from "../utils/index.js";
import type { FeeData, Provider } from "./provider.js";
import type { FetchRequest } from "../utils/fetch.js";
const EnsAddress = "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e";
/**
* A **NetworkPlugin** provides additional functionality on a [[Network]].
*/
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;
/**
* Creates a new **NetworkPlugin**.
*/
constructor(name: string) {
defineProperties<NetworkPlugin>(this, { name });
}
/**
* Creates a copy of this plugin.
*/
clone(): NetworkPlugin {
return new NetworkPlugin(this.name);
}
// validate(network: Network): NetworkPlugin {
// return this;
// }
}
/**
* The gas cost parameters for a [[GasCostPlugin]].
*/
export type GasCostParameters = {
/**
* The transactions base fee.
*/
txBase?: number;
/**
* The fee for creating a new account.
*/
txCreate?: number;
/**
* The fee per zero-byte in the data.
*/
txDataZero?: number;
/**
* The fee per non-zero-byte in the data.
*/
txDataNonzero?: number;
/**
* The fee per storage key in the [[link-eip-2930]] access list.
*/
txAccessListStorageKey?: number;
/**
* The fee per address in the [[link-eip-2930]] access list.
*/
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 {
/**
* 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;
/**
* The transactions base fee.
*/
readonly txBase!: number;
/**
* The fee for creating a new account.
*/
readonly txCreate!: number;
/**
* The fee per zero-byte in the data.
*/
readonly txDataZero!: number;
/**
* The fee per non-zero-byte in the data.
*/
readonly txDataNonzero!: number;
/**
* The fee per storage key in the [[link-eip-2930]] access list.
*/
readonly txAccessListStorageKey!: number;
/**
* The fee per address in the [[link-eip-2930]] access list.
*/
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) {
if (effectiveBlock == null) { effectiveBlock = 0; }
super(`org.ethers.network.plugins.GasCost#${ (effectiveBlock || 0) }`);
const props: Record<string, number> = { effectiveBlock };
function set(name: keyof GasCostParameters, nullish: number): void {
let value = (costs || { })[name];
if (value == null) { value = nullish; }
assertArgument(typeof(value) === "number", `invalud value for ${ name }`, "costs", costs);
props[name] = value;
}
set("txBase", 21000);
set("txCreate", 32000);
set("txDataZero", 4);
set("txDataNonzero", 16);
set("txAccessListStorageKey", 1900);
set("txAccessListAddress", 2400);
defineProperties<GasCostPlugin>(this, props);
}
clone(): GasCostPlugin {
return new GasCostPlugin(this.effectiveBlock, this);
}
}
/**
* 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 {
/**
* The ENS Registrty Contract address.
*/
readonly address!: string;
/**
* The chain ID that the ENS contract lives on.
*/
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) {
super("org.ethers.plugins.network.Ens");
defineProperties<EnsPlugin>(this, {
address: (address || EnsAddress),
targetNetwork: ((targetNetwork == null) ? 1: targetNetwork)
});
}
clone(): EnsPlugin {
return new EnsPlugin(this.address, this.targetNetwork);
}
}
/**
* 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 {
readonly #feeDataFunc: (provider: Provider) => Promise<FeeData>;
/**
* The fee data function provided to the constructor.
*/
get feeDataFunc(): (provider: Provider) => Promise<FeeData> {
return this.#feeDataFunc;
}
/**
* Creates a new **FeeDataNetworkPlugin**.
*/
constructor(feeDataFunc: (provider: Provider) => Promise<FeeData>) {
super("org.ethers.plugins.network.FeeData");
this.#feeDataFunc = feeDataFunc;
}
/**
* Resolves to the fee data.
*/
async getFeeData(provider: Provider): Promise<FeeData> {
return await this.#feeDataFunc(provider);
}
clone(): FeeDataNetworkPlugin {
return new FeeDataNetworkPlugin(this.#feeDataFunc);
}
}
export class FetchUrlFeeDataNetworkPlugin extends NetworkPlugin {
readonly #url: string;
readonly #processFunc: (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }>;
/**
* The URL to initialize the FetchRequest with in %%processFunc%%.
*/
get url(): string { return this.#url; }
/**
* The callback to use when computing the FeeData.
*/
get processFunc(): (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }> { return this.#processFunc; }
/**
* Creates a new **FetchUrlFeeDataNetworkPlugin** which will
* be used when computing the fee data for the network.
*/
constructor(url: string, processFunc: (f: () => Promise<FeeData>, p: Provider, r: FetchRequest) => Promise<{ gasPrice?: null | bigint, maxFeePerGas?: null | bigint, maxPriorityFeePerGas?: null | bigint }>) {
super("org.ethers.plugins.network.FetchUrlFeeDataPlugin");
this.#url = url;
this.#processFunc = processFunc;
}
// We are immutable, so we can serve as our own clone
clone(): FetchUrlFeeDataNetworkPlugin { return this; }
}
/*
export class CustomBlockNetworkPlugin extends NetworkPlugin {
readonly #blockFunc: (provider: Provider, block: BlockParams<string>) => Block<string>;
readonly #blockWithTxsFunc: (provider: Provider, block: BlockParams<TransactionResponseParams>) => Block<TransactionResponse>;
constructor(blockFunc: (provider: Provider, block: BlockParams<string>) => Block<string>, blockWithTxsFunc: (provider: Provider, block: BlockParams<TransactionResponseParams>) => Block<TransactionResponse>) {
super("org.ethers.network-plugins.custom-block");
this.#blockFunc = blockFunc;
this.#blockWithTxsFunc = blockWithTxsFunc;
}
async getBlock(provider: Provider, block: BlockParams<string>): Promise<Block<string>> {
return await this.#blockFunc(provider, block);
}
async getBlockions(provider: Provider, block: BlockParams<TransactionResponseParams>): Promise<Block<TransactionResponse>> {
return await this.#blockWithTxsFunc(provider, block);
}
clone(): CustomBlockNetworkPlugin {
return new CustomBlockNetworkPlugin(this.#blockFunc, this.#blockWithTxsFunc);
}
}
*/

View File

@@ -0,0 +1,166 @@
/**
* [[link-alchemy]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Sepolia Testnet (``sepolia``)
* - Arbitrum (``arbitrum``)
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
* - Base (``base``)
* - Base Goerlia Testnet (``base-goerli``)
* - Base Sepolia Testnet (``base-sepolia``)
* - Optimism (``optimism``)
* - Optimism Goerli Testnet (``optimism-goerli``)
* - Optimism Sepolia Testnet (``optimism-sepolia``)
* - Polygon (``matic``)
* - Polygon Amoy Testnet (``matic-amoy``)
* - Polygon Mumbai Testnet (``matic-mumbai``)
*
* @_subsection: api/providers/thirdparty:Alchemy [providers-alchemy]
*/
import {
defineProperties, resolveProperties, assert, assertArgument,
FetchRequest
} from "../utils/index.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
const defaultApiKey = "_gg7wSSi0KMBsdKnGVfHDueq6xMB9EkC"
function getHost(name: string): string {
switch(name) {
case "mainnet":
return "eth-mainnet.g.alchemy.com";
case "goerli":
return "eth-goerli.g.alchemy.com";
case "sepolia":
return "eth-sepolia.g.alchemy.com";
case "arbitrum":
return "arb-mainnet.g.alchemy.com";
case "arbitrum-goerli":
return "arb-goerli.g.alchemy.com";
case "arbitrum-sepolia":
return "arb-sepolia.g.alchemy.com";
case "base":
return "base-mainnet.g.alchemy.com";
case "base-goerli":
return "base-goerli.g.alchemy.com";
case "base-sepolia":
return "base-sepolia.g.alchemy.com";
case "matic":
return "polygon-mainnet.g.alchemy.com";
case "matic-amoy":
return "polygon-amoy.g.alchemy.com";
case "matic-mumbai":
return "polygon-mumbai.g.alchemy.com";
case "optimism":
return "opt-mainnet.g.alchemy.com";
case "optimism-goerli":
return "opt-goerli.g.alchemy.com";
case "optimism-sepolia":
return "opt-sepolia.g.alchemy.com";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **AlchemyProvider** connects to the [[link-alchemy]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-alchemy-signup).
*
* @_docloc: api/providers/thirdparty
*/
export class AlchemyProvider extends JsonRpcProvider implements CommunityResourcable {
readonly apiKey!: string;
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = defaultApiKey; }
const request = AlchemyProvider.getRequest(network, apiKey);
super(request, network, { staticNetwork: network });
defineProperties<AlchemyProvider>(this, { apiKey });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new AlchemyProvider(chainId, this.apiKey);
} catch (error) { }
return super._getProvider(chainId);
}
async _perform(req: PerformActionRequest): Promise<any> {
// https://docs.alchemy.com/reference/trace-transaction
if (req.method === "getTransactionResult") {
const { trace, tx } = await resolveProperties({
trace: this.send("trace_transaction", [ req.hash ]),
tx: this.getTransaction(req.hash)
});
if (trace == null || tx == null) { return null; }
let data: undefined | string;
let error = false;
try {
data = trace[0].result.output;
error = (trace[0].error === "Reverted");
} catch (error) { }
if (data) {
assert(!error, "an error occurred during transaction executions", "CALL_EXCEPTION", {
action: "getTransactionResult",
data,
reason: null,
transaction: tx,
invocation: null,
revert: null // @TODO
});
return data;
}
assert(false, "could not parse trace result", "BAD_DATA", { value: trace });
}
return await super._perform(req);
}
isCommunityResource(): boolean {
return (this.apiKey === defaultApiKey);
}
static getRequest(network: Network, apiKey?: string): FetchRequest {
if (apiKey == null) { apiKey = defaultApiKey; }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v2/${ apiKey }`);
request.allowGzip = true;
if (apiKey === defaultApiKey) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("alchemy");
return true;
}
}
return request;
}
}

159
dev/env/node_modules/ethers/src.ts/providers/provider-ankr.ts generated vendored Executable file
View File

@@ -0,0 +1,159 @@
/**
* [[link-ankr]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Sepolia Testnet (``sepolia``)
* - Arbitrum (``arbitrum``)
* - Base (``base``)
* - Base Goerlia Testnet (``base-goerli``)
* - Base Sepolia Testnet (``base-sepolia``)
* - BNB (``bnb``)
* - BNB Testnet (``bnbt``)
* - Filecoin (``filecoin``)
* - Filecoin Calibration Testnet (``filecoin-calibration``)
* - Optimism (``optimism``)
* - Optimism Goerli Testnet (``optimism-goerli``)
* - Optimism Sepolia Testnet (``optimism-sepolia``)
* - Polygon (``matic``)
* - Polygon Mumbai Testnet (``matic-mumbai``)
*
* @_subsection: api/providers/thirdparty:Ankr [providers-ankr]
*/
import {
defineProperties, FetchRequest, assertArgument
} from "../utils/index.js";
import { AbstractProvider } from "./abstract-provider.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
import type { JsonRpcError, JsonRpcPayload } from "./provider-jsonrpc.js";
const defaultApiKey = "9f7d929b018cdffb338517efa06f58359e86ff1ffd350bc889738523659e7972";
function getHost(name: string): string {
switch (name) {
case "mainnet":
return "rpc.ankr.com/eth";
case "goerli":
return "rpc.ankr.com/eth_goerli";
case "sepolia":
return "rpc.ankr.com/eth_sepolia";
case "arbitrum":
return "rpc.ankr.com/arbitrum";
case "base":
return "rpc.ankr.com/base";
case "base-goerli":
return "rpc.ankr.com/base_goerli";
case "base-sepolia":
return "rpc.ankr.com/base_sepolia";
case "bnb":
return "rpc.ankr.com/bsc";
case "bnbt":
return "rpc.ankr.com/bsc_testnet_chapel";
case "filecoin":
return "rpc.ankr.com/filecoin";
case "filecoin-calibration":
return "rpc.ankr.com/filecoin_testnet";
case "matic":
return "rpc.ankr.com/polygon";
case "matic-mumbai":
return "rpc.ankr.com/polygon_mumbai";
case "optimism":
return "rpc.ankr.com/optimism";
case "optimism-goerli":
return "rpc.ankr.com/optimism_testnet";
case "optimism-sepolia":
return "rpc.ankr.com/optimism_sepolia";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **AnkrProvider** connects to the [[link-ankr]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-ankr-signup).
*/
export class AnkrProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The API key for the Ankr connection.
*/
readonly apiKey!: string;
/**
* Create a new **AnkrProvider**.
*
* By default connecting to ``mainnet`` with a highly throttled
* API key.
*/
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = defaultApiKey; }
// Ankr does not support filterId, so we force polling
const options = { polling: true, staticNetwork: network };
const request = AnkrProvider.getRequest(network, apiKey);
super(request, network, options);
defineProperties<AnkrProvider>(this, { apiKey });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new AnkrProvider(chainId, this.apiKey);
} catch (error) { }
return super._getProvider(chainId);
}
/**
* Returns a prepared request for connecting to %%network%% with
* %%apiKey%%.
*/
static getRequest(network: Network, apiKey?: null | string): FetchRequest {
if (apiKey == null) { apiKey = defaultApiKey; }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ apiKey }`);
request.allowGzip = true;
if (apiKey === defaultApiKey) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("AnkrProvider");
return true;
};
}
return request;
}
getRpcError(payload: JsonRpcPayload, error: JsonRpcError): Error {
if (payload.method === "eth_sendRawTransaction") {
if (error && error.error && error.error.message === "INTERNAL_ERROR: could not replace existing tx") {
error.error.message = "replacement transaction underpriced";
}
}
return super.getRpcError(payload, error);
}
isCommunityResource(): boolean {
return (this.apiKey === defaultApiKey);
}
}

View File

@@ -0,0 +1,167 @@
/**
* [[link-blockscout]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Sepolia Testnet (``sepolia``)
* - Holesky Testnet (``holesky``)
* - Ethereum Classic (``classic``)
* - Arbitrum (``arbitrum``)
* - Base (``base``)
* - Base Sepolia Testnet (``base-sepolia``)
* - Gnosis (``xdai``)
* - Optimism (``optimism``)
* - Optimism Sepolia Testnet (``optimism-sepolia``)
* - Polygon (``matic``)
*
* @_subsection: api/providers/thirdparty:Blockscout [providers-blockscout]
*/
import {
assertArgument, defineProperties, FetchRequest, isHexString
} from "../utils/index.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { AbstractProvider, PerformActionRequest } from "./abstract-provider.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
import type { JsonRpcPayload, JsonRpcError } from "./provider-jsonrpc.js";
function getUrl(name: string): string {
switch(name) {
case "mainnet":
return "https:/\/eth.blockscout.com/api/eth-rpc";
case "sepolia":
return "https:/\/eth-sepolia.blockscout.com/api/eth-rpc";
case "holesky":
return "https:/\/eth-holesky.blockscout.com/api/eth-rpc";
case "classic":
return "https:/\/etc.blockscout.com/api/eth-rpc";
case "arbitrum":
return "https:/\/arbitrum.blockscout.com/api/eth-rpc";
case "base":
return "https:/\/base.blockscout.com/api/eth-rpc";
case "base-sepolia":
return "https:/\/base-sepolia.blockscout.com/api/eth-rpc";
case "matic":
return "https:/\/polygon.blockscout.com/api/eth-rpc";
case "optimism":
return "https:/\/optimism.blockscout.com/api/eth-rpc";
case "optimism-sepolia":
return "https:/\/optimism-sepolia.blockscout.com/api/eth-rpc";
case "xdai":
return "https:/\/gnosis.blockscout.com/api/eth-rpc";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **BlockscoutProvider** connects to the [[link-blockscout]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-blockscout).
*/
export class BlockscoutProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The API key.
*/
readonly apiKey!: null | string;
/**
* Creates a new **BlockscoutProvider**.
*/
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = null; }
const request = BlockscoutProvider.getRequest(network);
super(request, network, { staticNetwork: network });
defineProperties<BlockscoutProvider>(this, { apiKey });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new BlockscoutProvider(chainId, this.apiKey);
} catch (error) { }
return super._getProvider(chainId);
}
isCommunityResource(): boolean {
return (this.apiKey === null);
}
getRpcRequest(req: PerformActionRequest): null | { method: string, args: Array<any> } {
// Blockscout enforces the TAG argument for estimateGas
const resp = super.getRpcRequest(req);
if (resp && resp.method === "eth_estimateGas" && resp.args.length == 1) {
resp.args = resp.args.slice();
resp.args.push("latest");
}
return resp;
}
getRpcError(payload: JsonRpcPayload, _error: JsonRpcError): Error {
const error = _error ? _error.error: null;
// Blockscout currently drops the VM result and replaces it with a
// human-readable string, so we need to make it machine-readable.
if (error && error.code === -32015 && !isHexString(error.data || "", true)) {
const panicCodes = <Record<string, string>>{
"assert(false)": "01",
"arithmetic underflow or overflow": "11",
"division or modulo by zero": "12",
"out-of-bounds array access; popping on an empty array": "31",
"out-of-bounds access of an array or bytesN": "32"
};
let panicCode = "";
if (error.message === "VM execution error.") {
// eth_call passes this message
panicCode = panicCodes[error.data] || "";
} else if (panicCodes[error.message || ""]) {
panicCode = panicCodes[error.message || ""];
}
if (panicCode) {
error.message += ` (reverted: ${ error.data })`;
error.data = "0x4e487b7100000000000000000000000000000000000000000000000000000000000000" + panicCode;
}
} else if (error && error.code === -32000) {
if (error.message === "wrong transaction nonce") {
error.message += " (nonce too low)";
}
}
return super.getRpcError(payload, _error);
}
/**
* Returns a prepared request for connecting to %%network%%
* with %%apiKey%%.
*/
static getRequest(network: Network): FetchRequest {
const request = new FetchRequest(getUrl(network.name));
request.allowGzip = true;
return request;
}
}

View File

@@ -0,0 +1,334 @@
import { assertArgument, makeError } from "../utils/index.js";
import { JsonRpcApiPollingProvider } from "./provider-jsonrpc.js";
import type {
JsonRpcApiProviderOptions,
JsonRpcError, JsonRpcPayload, JsonRpcResult,
JsonRpcSigner
} from "./provider-jsonrpc.js";
import type { Network, 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 {
/**
* See [[link-eip-1193]] for details on this method.
*/
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 = {
action: "sendEip1193Payload",
payload: { method: string, params: Array<any> }
} | {
action: "receiveEip1193Result",
result: any
} | {
action: "receiveEip1193Error",
error: Error
};
/**
* Provider info provided by the [[link-eip-6963]] discovery mechanism.
*/
export interface Eip6963ProviderInfo {
uuid: string;
name: string;
icon: string;
rdns: string;
}
interface Eip6963ProviderDetail {
info: Eip6963ProviderInfo;
provider: Eip1193Provider;
}
interface Eip6963Announcement {
type: "eip6963:announceProvider";
detail: Eip6963ProviderDetail
}
export type BrowserProviderOptions = {
polling?: boolean;
staticNetwork?: null | boolean | Network;
cacheTimeout?: number;
pollingInterval?: number;
providerInfo?: Eip6963ProviderInfo;
};
/**
* Specifies how [[link-eip-6963]] discovery should proceed.
*
* See: [[BrowserProvider-discover]]
*/
export interface BrowserDiscoverOptions {
/**
* Override provider detection with this provider.
*/
provider?: Eip1193Provider;
/**
* Duration to wait to detect providers. (default: 300ms)
*/
timeout?: number;
/**
* Return the first detected provider. Otherwise wait for %%timeout%%
* and allowing filtering before selecting the desired provider.
*/
anyProvider?: boolean;
/**
* Use the provided window context. Useful in non-standard
* environments or to hijack where a provider comes from.
*/
window?: any;
/**
* Explicitly choose which provider to used once scanning is complete.
*/
filter?: (found: Array<Eip6963ProviderInfo>) => null | BrowserProvider |
Eip6963ProviderInfo;
}
/**
* 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 {
#request: (method: string, params: Array<any> | Record<string, any>) => Promise<any>;
#providerInfo: null | Eip6963ProviderInfo;
/**
* Connect to the %%ethereum%% provider, optionally forcing the
* %%network%%.
*/
constructor(ethereum: Eip1193Provider, network?: Networkish, _options?: BrowserProviderOptions) {
// Copy the options
const options: JsonRpcApiProviderOptions = Object.assign({ },
((_options != null) ? _options: { }),
{ batchMaxCount: 1 });
assertArgument(ethereum && ethereum.request, "invalid EIP-1193 provider", "ethereum", ethereum);
super(network, options);
this.#providerInfo = null;
if (_options && _options.providerInfo) {
this.#providerInfo = _options.providerInfo;
}
this.#request = async (method: string, params: Array<any> | Record<string, any>) => {
const payload = { method, params };
this.emit("debug", { action: "sendEip1193Request", payload });
try {
const result = await ethereum.request(payload);
this.emit("debug", { action: "receiveEip1193Result", result });
return result;
} catch (e: any) {
const error = new Error(e.message);
(<any>error).code = e.code;
(<any>error).data = e.data;
(<any>error).payload = payload;
this.emit("debug", { action: "receiveEip1193Error", error });
throw error;
}
};
}
get providerInfo(): null | Eip6963ProviderInfo {
return this.#providerInfo;
}
async send(method: string, params: Array<any> | Record<string, any>): Promise<any> {
await this._start();
return await super.send(method, params);
}
async _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>> {
assertArgument(!Array.isArray(payload), "EIP-1193 does not support batch request", "payload", payload);
try {
const result = await this.#request(payload.method, payload.params || [ ]);
return [ { id: payload.id, result } ];
} catch (e: any) {
return [ {
id: payload.id,
error: { code: e.code, data: e.data, message: e.message }
} ];
}
}
getRpcError(payload: JsonRpcPayload, error: JsonRpcError): Error {
error = JSON.parse(JSON.stringify(error));
// EIP-1193 gives us some machine-readable error codes, so rewrite
// them into Ethers standard errors.
switch (error.error.code || -1) {
case 4001:
error.error.message = `ethers-user-denied: ${ error.error.message }`;
break;
case 4200:
error.error.message = `ethers-unsupported: ${ error.error.message }`;
break;
}
return super.getRpcError(payload, error);
}
/**
* Resolves to ``true`` if the provider manages the %%address%%.
*/
async hasSigner(address: number | string): Promise<boolean> {
if (address == null) { address = 0; }
const accounts = await this.send("eth_accounts", [ ]);
if (typeof(address) === "number") {
return (accounts.length > address);
}
address = address.toLowerCase();
return accounts.filter((a: string) => (a.toLowerCase() === address)).length !== 0;
}
async getSigner(address?: number | string): Promise<JsonRpcSigner> {
if (address == null) { address = 0; }
if (!(await this.hasSigner(address))) {
try {
await this.#request("eth_requestAccounts", [ ]);
} catch (error: any) {
const payload = error.payload;
throw this.getRpcError(payload, { id: payload.id, error });
}
}
return await super.getSigner(address);
}
/**
* Discover and connect to a Provider in the Browser using the
* [[link-eip-6963]] discovery mechanism. If no providers are
* present, ``null`` is resolved.
*/
static async discover(options?: BrowserDiscoverOptions): Promise<null | BrowserProvider> {
if (options == null) { options = { }; }
if (options.provider) {
return new BrowserProvider(options.provider);
}
const context = options.window ? options.window:
(typeof(window) !== "undefined") ? window: null;
if (context == null) { return null; }
const anyProvider = options.anyProvider;
if (anyProvider && context.ethereum) {
return new BrowserProvider(context.ethereum);
}
if (!("addEventListener" in context && "dispatchEvent" in context
&& "removeEventListener" in context)) {
return null;
}
const timeout = options.timeout ? options.timeout: 300;
if (timeout === 0) { return null; }
return await (new Promise((resolve, reject) => {
let found: Array<Eip6963ProviderDetail> = [ ];
const addProvider = (event: Eip6963Announcement) => {
found.push(event.detail);
if (anyProvider) { finalize(); }
};
const finalize = () => {
clearTimeout(timer);
if (found.length) {
// If filtering is provided:
if (options && options.filter) {
// Call filter, with a copies of found provider infos
const filtered = options.filter(found.map(i =>
Object.assign({ }, (i.info))));
if (filtered == null) {
// No provider selected
resolve(null);
} else if (filtered instanceof BrowserProvider) {
// Custom provider created
resolve(filtered);
} else {
// Find the matching provider
let match: null | Eip6963ProviderDetail = null;
if (filtered.uuid) {
const matches = found.filter(f =>
(filtered.uuid === f.info.uuid));
// @TODO: What should happen if multiple values
// for the same UUID?
match = matches[0];
}
if (match) {
const { provider, info } = match;
resolve(new BrowserProvider(provider, undefined, {
providerInfo: info
}));
} else {
reject(makeError("filter returned unknown info", "UNSUPPORTED_OPERATION", {
value: filtered
}));
}
}
} else {
// Pick the first found provider
const { provider, info } = found[0];
resolve(new BrowserProvider(provider, undefined, {
providerInfo: info
}));
}
} else {
// Nothing found
resolve(null);
}
context.removeEventListener(<any>"eip6963:announceProvider",
addProvider);
};
const timer = setTimeout(() => { finalize(); }, timeout);
context.addEventListener(<any>"eip6963:announceProvider",
addProvider);
context.dispatchEvent(new Event("eip6963:requestProvider"));
}));
}
}

View File

@@ -0,0 +1,113 @@
/**
* [[link-chainstack]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Arbitrum (``arbitrum``)
* - BNB Smart Chain Mainnet (``bnb``)
* - Polygon (``matic``)
*
* @_subsection: api/providers/thirdparty:Chainstack [providers-chainstack]
*/
import {
defineProperties, FetchRequest, assertArgument
} from "../utils/index.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { AbstractProvider } from "./abstract-provider.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
function getApiKey(name: string): string {
switch (name) {
case "mainnet": return "39f1d67cedf8b7831010a665328c9197";
case "arbitrum": return "0550c209db33c3abf4cc927e1e18cea1"
case "bnb": return "98b5a77e531614387366f6fc5da097f8";
case "matic": return "cd9d4d70377471aa7c142ec4a4205249";
}
assertArgument(false, "unsupported network", "network", name);
}
function getHost(name: string): string {
switch(name) {
case "mainnet":
return "ethereum-mainnet.core.chainstack.com";
case "arbitrum":
return "arbitrum-mainnet.core.chainstack.com";
case "bnb":
return "bsc-mainnet.core.chainstack.com";
case "matic":
return "polygon-mainnet.core.chainstack.com";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **ChainstackProvider** connects to the [[link-chainstack]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-chainstack).
*/
export class ChainstackProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The API key for the Chainstack connection.
*/
readonly apiKey!: string;
/**
* Creates a new **ChainstackProvider**.
*/
constructor(_network?: Networkish, apiKey?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (apiKey == null) { apiKey = getApiKey(network.name); }
const request = ChainstackProvider.getRequest(network, apiKey);
super(request, network, { staticNetwork: network });
defineProperties<ChainstackProvider>(this, { apiKey });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new ChainstackProvider(chainId, this.apiKey);
} catch (error) { }
return super._getProvider(chainId);
}
isCommunityResource(): boolean {
return (this.apiKey === getApiKey(this._network.name));
}
/**
* Returns a prepared request for connecting to %%network%%
* with %%apiKey%% and %%projectSecret%%.
*/
static getRequest(network: Network, apiKey?: null | string): FetchRequest {
if (apiKey == null) { apiKey = getApiKey(network.name); }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ apiKey }`);
request.allowGzip = true;
if (apiKey === getApiKey(network.name)) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("ChainstackProvider");
return true;
};
}
return request;
}
}

View File

@@ -0,0 +1,24 @@
/**
* About Cloudflare
*
* @_subsection: api/providers/thirdparty:Cloudflare [providers-cloudflare]
*/
import { assertArgument } from "../utils/index.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
/**
* About Cloudflare...
*/
export class CloudflareProvider extends JsonRpcProvider {
constructor(_network?: Networkish) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
assertArgument(network.name === "mainnet", "unsupported network", "network", _network);
super("https:/\/cloudflare-eth.com/", network, { staticNetwork: network });
}
}

View File

@@ -0,0 +1,687 @@
/**
* [[link-etherscan]] provides a third-party service for connecting to
* various blockchains over a combination of JSON-RPC and custom API
* endpoints.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Sepolia Testnet (``sepolia``)
* - Holesky Testnet (``holesky``)
* - Arbitrum (``arbitrum``)
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
* - Base (``base``)
* - Base Sepolia Testnet (``base-sepolia``)
* - BNB Smart Chain Mainnet (``bnb``)
* - BNB Smart Chain Testnet (``bnbt``)
* - Optimism (``optimism``)
* - Optimism Goerli Testnet (``optimism-goerli``)
* - Polygon (``matic``)
* - Polygon Mumbai Testnet (``matic-mumbai``)
* - Polygon Amoy Testnet (``matic-amoy``)
*
* @_subsection api/providers/thirdparty:Etherscan [providers-etherscan]
*/
import { AbiCoder } from "../abi/index.js";
import { Contract } from "../contract/index.js";
import { accessListify, Transaction } from "../transaction/index.js";
import {
defineProperties,
hexlify, toQuantity,
FetchRequest,
assert, assertArgument, isError,
// parseUnits,
toUtf8String
} from "../utils/index.js";
import { AbstractProvider } from "./abstract-provider.js";
import { Network } from "./network.js";
import { NetworkPlugin } from "./plugins-network.js";
import { showThrottleMessage } from "./community.js";
import { PerformActionRequest } from "./abstract-provider.js";
import type { Networkish } from "./network.js";
//import type { } from "./pagination";
import type { TransactionRequest } from "./provider.js";
// See: https://docs.etherscan.io/supported-chains
const Supported = (
"1 11155111 17000 560048 2741 11124 33111 33139 42170 " +
"42161 421614 43114 43113 8453 84532 80069 80094 199 1029 81457 " +
"168587773 56 97 42220 11142220 252 2523 100 999 737373 747474 " +
"59144 59141 5000 5003 43521 143 10143 1287 1284 1285 10 " +
"11155420 204 5611 80002 137 534352 534351 1329 1328 146 14601 " +
"988 2201 1923 1924 167013 167000 130 1301 480 4801 51 50 324 300"
).split(/ /g);
const THROTTLE = 2000;
function isPromise<T = any>(value: any): value is Promise<T> {
return (value && typeof(value.then) === "function");
}
/**
* When subscribing to the ``"debug"`` event on an Etherscan-based
* provider, the events receive a **DebugEventEtherscanProvider**
* payload.
*
* @_docloc: api/providers/thirdparty:Etherscan
*/
export type DebugEventEtherscanProvider = {
action: "sendRequest",
id: number,
url: string,
payload: Record<string, any>
} | {
action: "receiveRequest",
id: number,
result: any
} | {
action: "receiveError",
id: number,
error: any
};
const EtherscanPluginId = "org.ethers.plugins.provider.Etherscan";
/**
* A Network can include an **EtherscanPlugin** to provide
* a custom base URL.
*
* @_docloc: api/providers/thirdparty:Etherscan
*/
export class EtherscanPlugin extends NetworkPlugin {
/**
* The Etherscan API base URL.
*/
readonly baseUrl!: string;
/**
* Creates a new **EtherscanProvider** which will use
* %%baseUrl%%.
*/
constructor(baseUrl: string) {
super(EtherscanPluginId);
defineProperties<EtherscanPlugin>(this, { baseUrl });
}
clone(): EtherscanPlugin {
return new EtherscanPlugin(this.baseUrl);
}
}
const skipKeys = [ "enableCcipRead" ];
let nextId = 1;
/**
* The **EtherscanBaseProvider** is the super-class of
* [[EtherscanProvider]], which should generally be used instead.
*
* Since the **EtherscanProvider** includes additional code for
* [[Contract]] access, in //rare cases// that contracts are not
* used, this class can reduce code size.
*
* @_docloc: api/providers/thirdparty:Etherscan
*/
export class EtherscanProvider extends AbstractProvider {
/**
* The connected network.
*/
readonly network!: Network;
/**
* The API key or null if using the community provided bandwidth.
*/
readonly apiKey!: null | string;
readonly #plugin: null | EtherscanPlugin;
/**
* Creates a new **EtherscanBaseProvider**.
*/
constructor(_network?: Networkish, _apiKey?: string) {
const apiKey = (_apiKey != null) ? _apiKey: null;
super();
const network = Network.from(_network);
assertArgument(Supported.indexOf(`${ network.chainId }`) >= 0,
"unsupported network", "network", network);
this.#plugin = network.getPlugin<EtherscanPlugin>(EtherscanPluginId);
defineProperties<EtherscanProvider>(this, { apiKey, network });
}
/**
* Returns the base URL.
*
* If an [[EtherscanPlugin]] is configured on the
* [[EtherscanBaseProvider_network]], returns the plugin's
* baseUrl.
*
* Deprecated; for Etherscan v2 the base is no longer a simply
* host, but instead a URL including a chainId parameter. Changing
* this to return a URL prefix could break some libraries, so it
* is left intact but will be removed in the future as it is unused.
*/
getBaseUrl(): string {
if (this.#plugin) { return this.#plugin.baseUrl; }
switch(this.network.name) {
case "mainnet":
return "https:/\/api.etherscan.io";
case "goerli":
return "https:/\/api-goerli.etherscan.io";
case "sepolia":
return "https:/\/api-sepolia.etherscan.io";
case "holesky":
return "https:/\/api-holesky.etherscan.io";
case "arbitrum":
return "https:/\/api.arbiscan.io";
case "arbitrum-goerli":
return "https:/\/api-goerli.arbiscan.io";
case "base":
return "https:/\/api.basescan.org";
case "base-sepolia":
return "https:/\/api-sepolia.basescan.org";
case "bnb":
return "https:/\/api.bscscan.com";
case "bnbt":
return "https:/\/api-testnet.bscscan.com";
case "matic":
return "https:/\/api.polygonscan.com";
case "matic-amoy":
return "https:/\/api-amoy.polygonscan.com";
case "matic-mumbai":
return "https:/\/api-testnet.polygonscan.com";
case "optimism":
return "https:/\/api-optimistic.etherscan.io";
case "optimism-goerli":
return "https:/\/api-goerli-optimistic.etherscan.io";
default:
}
assertArgument(false, "unsupported network", "network", this.network);
}
/**
* Returns the URL for the %%module%% and %%params%%.
*/
getUrl(module: string, params: Record<string, string>): string {
let query = Object.keys(params).reduce((accum, key) => {
const value = params[key];
if (value != null) {
accum += `&${ key }=${ value }`
}
return accum
}, "");
if (this.apiKey) { query += `&apikey=${ this.apiKey }`; }
return `https:/\/api.etherscan.io/v2/api?chainid=${ this.network.chainId }&module=${ module }${ query }`;
}
/**
* Returns the URL for using POST requests.
*/
getPostUrl(): string {
return `https:/\/api.etherscan.io/v2/api?chainid=${ this.network.chainId }`;
}
/**
* Returns the parameters for using POST requests.
*/
getPostData(module: string, params: Record<string, any>): Record<string, any> {
params.module = module;
params.apikey = this.apiKey;
params.chainid = this.network.chainId;
return params;
}
async detectNetwork(): Promise<Network> {
return this.network;
}
/**
* Resolves to the result of calling %%module%% with %%params%%.
*
* If %%post%%, the request is made as a POST request.
*/
async fetch(module: string, params: Record<string, any>, post?: boolean): Promise<any> {
const id = nextId++;
const url = (post ? this.getPostUrl(): this.getUrl(module, params));
const payload = (post ? this.getPostData(module, params): null);
this.emit("debug", { action: "sendRequest", id, url, payload: payload });
const request = new FetchRequest(url);
request.setThrottleParams({ slotInterval: 1000 });
request.retryFunc = (req, resp, attempt: number) => {
if (this.isCommunityResource()) {
showThrottleMessage("Etherscan");
}
return Promise.resolve(true);
};
request.processFunc = async (request, response) => {
const result = response.hasBody() ? JSON.parse(toUtf8String(response.body)): { };
const throttle = ((typeof(result.result) === "string") ? result.result: "").toLowerCase().indexOf("rate limit") >= 0;
if (module === "proxy") {
// This JSON response indicates we are being throttled
if (result && result.status == 0 && result.message == "NOTOK" && throttle) {
this.emit("debug", { action: "receiveError", id, reason: "proxy-NOTOK", error: result });
response.throwThrottleError(result.result, THROTTLE);
}
} else {
if (throttle) {
this.emit("debug", { action: "receiveError", id, reason: "null result", error: result.result });
response.throwThrottleError(result.result, THROTTLE);
}
}
return response;
};
if (payload) {
request.setHeader("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
request.body = Object.keys(payload).map((k) => `${ k }=${ payload[k] }`).join("&");
}
const response = await request.send();
try {
response.assertOk();
} catch (error) {
this.emit("debug", { action: "receiveError", id, error, reason: "assertOk" });
assert(false, "response error", "SERVER_ERROR", { request, response });
}
if (!response.hasBody()) {
this.emit("debug", { action: "receiveError", id, error: "missing body", reason: "null body" });
assert(false, "missing response", "SERVER_ERROR", { request, response });
}
const result = JSON.parse(toUtf8String(response.body));
if (module === "proxy") {
if (result.jsonrpc != "2.0") {
this.emit("debug", { action: "receiveError", id, result, reason: "invalid JSON-RPC" });
assert(false, "invalid JSON-RPC response (missing jsonrpc='2.0')", "SERVER_ERROR", { request, response, info: { result } });
}
if (result.error) {
this.emit("debug", { action: "receiveError", id, result, reason: "JSON-RPC error" });
assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
}
this.emit("debug", { action: "receiveRequest", id, result });
return result.result;
} else {
// getLogs, getHistory have weird success responses
if (result.status == 0 && (result.message === "No records found" || result.message === "No transactions found")) {
this.emit("debug", { action: "receiveRequest", id, result });
return result.result;
}
if (result.status != 1 || (typeof(result.message) === "string" && !result.message.match(/^OK/))) {
this.emit("debug", { action: "receiveError", id, result });
assert(false, "error response", "SERVER_ERROR", { request, response, info: { result } });
}
this.emit("debug", { action: "receiveRequest", id, result });
return result.result;
}
}
/**
* Returns %%transaction%% normalized for the Etherscan API.
*/
_getTransactionPostData(transaction: TransactionRequest): Record<string, string> {
const result: Record<string, string> = { };
for (let key in transaction) {
if (skipKeys.indexOf(key) >= 0) { continue; }
if ((<any>transaction)[key] == null) { continue; }
let value = (<any>transaction)[key];
if (key === "type" && value === 0) { continue; }
if (key === "blockTag" && value === "latest") { continue; }
// Quantity-types require no leading zero, unless 0
if ((<any>{ type: true, gasLimit: true, gasPrice: true, maxFeePerGs: true, maxPriorityFeePerGas: true, nonce: true, value: true })[key]) {
value = toQuantity(value);
} else if (key === "accessList") {
value = "[" + accessListify(value).map((set) => {
return `{address:"${ set.address }",storageKeys:["${ set.storageKeys.join('","') }"]}`;
}).join(",") + "]";
} else if (key === "blobVersionedHashes") {
if (value.length === 0) { continue; }
// @TODO: update this once the API supports blobs
assert(false, "Etherscan API does not support blobVersionedHashes", "UNSUPPORTED_OPERATION", {
operation: "_getTransactionPostData",
info: { transaction }
});
} else {
value = hexlify(value);
}
result[key] = value;
}
return result;
}
/**
* Throws the normalized Etherscan error.
*/
_checkError(req: PerformActionRequest, error: Error, transaction: any): never {
// Pull any message out if, possible
let message = "";
if (isError(error, "SERVER_ERROR")) {
// Check for an error emitted by a proxy call
try {
message = (<any>error).info.result.error.message;
} catch (e) { }
if (!message) {
try {
message = (<any>error).info.message;
} catch (e) { }
}
}
if (req.method === "estimateGas") {
if (!message.match(/revert/i) && message.match(/insufficient funds/i)) {
assert(false, "insufficient funds", "INSUFFICIENT_FUNDS", {
transaction: req.transaction
});
}
}
if (req.method === "call" || req.method === "estimateGas") {
if (message.match(/execution reverted/i)) {
let data = "";
try {
data = (<any>error).info.result.error.data;
} catch (error) { }
const e = AbiCoder.getBuiltinCallException(req.method, <any>req.transaction, data);
e.info = { request: req, error }
throw e;
}
}
if (message) {
if (req.method === "broadcastTransaction") {
const transaction = Transaction.from(req.signedTransaction);
if (message.match(/replacement/i) && message.match(/underpriced/i)) {
assert(false, "replacement fee too low", "REPLACEMENT_UNDERPRICED", {
transaction
});
}
if (message.match(/insufficient funds/)) {
assert(false, "insufficient funds for intrinsic transaction cost", "INSUFFICIENT_FUNDS", {
transaction
});
}
if (message.match(/same hash was already imported|transaction nonce is too low|nonce too low/)) {
assert(false, "nonce has already been used", "NONCE_EXPIRED", {
transaction
});
}
}
}
// Something we could not process
throw error;
}
async _detectNetwork(): Promise<Network> {
return this.network;
}
async _perform(req: PerformActionRequest): Promise<any> {
switch (req.method) {
case "chainId":
return this.network.chainId;
case "getBlockNumber":
return this.fetch("proxy", { action: "eth_blockNumber" });
case "getGasPrice":
return this.fetch("proxy", { action: "eth_gasPrice" });
case "getPriorityFee":
// This is temporary until Etherscan completes support
if (this.network.name === "mainnet") {
return "1000000000";
} else if (this.network.name === "optimism") {
return "1000000";
} else {
throw new Error("fallback onto the AbstractProvider default");
}
/* Working with Etherscan to get this added:
try {
const test = await this.fetch("proxy", {
action: "eth_maxPriorityFeePerGas"
});
console.log(test);
return test;
} catch (e) {
console.log("DEBUG", e);
throw e;
}
*/
/* This might be safe; but due to rounding neither myself
or Etherscan are necessarily comfortable with this. :)
try {
const result = await this.fetch("gastracker", { action: "gasoracle" });
console.log(result);
const gasPrice = parseUnits(result.SafeGasPrice, "gwei");
const baseFee = parseUnits(result.suggestBaseFee, "gwei");
const priorityFee = gasPrice - baseFee;
if (priorityFee < 0) { throw new Error("negative priority fee; defer to abstract provider default"); }
return priorityFee;
} catch (error) {
console.log("DEBUG", error);
throw error;
}
*/
case "getBalance":
// Returns base-10 result
return this.fetch("account", {
action: "balance",
address: req.address,
tag: req.blockTag
});
case "getTransactionCount":
return this.fetch("proxy", {
action: "eth_getTransactionCount",
address: req.address,
tag: req.blockTag
});
case "getCode":
return this.fetch("proxy", {
action: "eth_getCode",
address: req.address,
tag: req.blockTag
});
case "getStorage":
return this.fetch("proxy", {
action: "eth_getStorageAt",
address: req.address,
position: req.position,
tag: req.blockTag
});
case "broadcastTransaction":
return this.fetch("proxy", {
action: "eth_sendRawTransaction",
hex: req.signedTransaction
}, true).catch((error) => {
return this._checkError(req, <Error>error, req.signedTransaction);
});
case "getBlock":
if ("blockTag" in req) {
return this.fetch("proxy", {
action: "eth_getBlockByNumber",
tag: req.blockTag,
boolean: (req.includeTransactions ? "true": "false")
});
}
assert(false, "getBlock by blockHash not supported by Etherscan", "UNSUPPORTED_OPERATION", {
operation: "getBlock(blockHash)"
});
case "getTransaction":
return this.fetch("proxy", {
action: "eth_getTransactionByHash",
txhash: req.hash
});
case "getTransactionReceipt":
return this.fetch("proxy", {
action: "eth_getTransactionReceipt",
txhash: req.hash
});
case "call": {
if (req.blockTag !== "latest") {
throw new Error("EtherscanProvider does not support blockTag for call");
}
const postData = this._getTransactionPostData(req.transaction);
postData.module = "proxy";
postData.action = "eth_call";
try {
return await this.fetch("proxy", postData, true);
} catch (error) {
return this._checkError(req, <Error>error, req.transaction);
}
}
case "estimateGas": {
const postData = this._getTransactionPostData(req.transaction);
postData.module = "proxy";
postData.action = "eth_estimateGas";
try {
return await this.fetch("proxy", postData, true);
} catch (error) {
return this._checkError(req, <Error>error, req.transaction);
}
}
/*
case "getLogs": {
// Needs to complain if more than one address is passed in
const args: Record<string, any> = { action: "getLogs" }
if (params.filter.fromBlock) {
args.fromBlock = checkLogTag(params.filter.fromBlock);
}
if (params.filter.toBlock) {
args.toBlock = checkLogTag(params.filter.toBlock);
}
if (params.filter.address) {
args.address = params.filter.address;
}
// @TODO: We can handle slightly more complicated logs using the logs API
if (params.filter.topics && params.filter.topics.length > 0) {
if (params.filter.topics.length > 1) {
logger.throwError("unsupported topic count", Logger.Errors.UNSUPPORTED_OPERATION, { topics: params.filter.topics });
}
if (params.filter.topics.length === 1) {
const topic0 = params.filter.topics[0];
if (typeof(topic0) !== "string" || topic0.length !== 66) {
logger.throwError("unsupported topic format", Logger.Errors.UNSUPPORTED_OPERATION, { topic0: topic0 });
}
args.topic0 = topic0;
}
}
const logs: Array<any> = await this.fetch("logs", args);
// Cache txHash => blockHash
let blocks: { [tag: string]: string } = {};
// Add any missing blockHash to the logs
for (let i = 0; i < logs.length; i++) {
const log = logs[i];
if (log.blockHash != null) { continue; }
if (blocks[log.blockNumber] == null) {
const block = await this.getBlock(log.blockNumber);
if (block) {
blocks[log.blockNumber] = block.hash;
}
}
log.blockHash = blocks[log.blockNumber];
}
return logs;
}
*/
default:
break;
}
return super._perform(req);
}
async getNetwork(): Promise<Network> {
return this.network;
}
/**
* Resolves to the current price of ether.
*
* This returns ``0`` on any network other than ``mainnet``.
*/
async getEtherPrice(): Promise<number> {
if (this.network.name !== "mainnet") { return 0.0; }
return parseFloat((await this.fetch("stats", { action: "ethprice" })).ethusd);
}
/**
* Resolves to a [Contract]] for %%address%%, using the
* Etherscan API to retreive the Contract ABI.
*/
async getContract(_address: string): Promise<null | Contract> {
let address = this._getAddress(_address);
if (isPromise(address)) { address = await address; }
try {
const resp = await this.fetch("contract", {
action: "getabi", address });
const abi = JSON.parse(resp);
return new Contract(address, abi, this);
} catch (error) {
return null;
}
}
isCommunityResource(): boolean {
return (this.apiKey == null);
}
}

View File

@@ -0,0 +1,801 @@
/**
* A **FallbackProvider** provides resilience, security and performance
* in a way that is customizable and configurable.
*
* @_section: api/providers/fallback-provider:Fallback Provider [about-fallback-provider]
*/
import {
assert, assertArgument, getBigInt, getNumber, isError
} from "../utils/index.js";
import { AbstractProvider } from "./abstract-provider.js";
import { Network } from "./network.js"
import type { PerformActionRequest } from "./abstract-provider.js";
import type { Networkish } from "./network.js"
const BN_1 = BigInt("1");
const BN_2 = BigInt("2");
function shuffle<T = any>(array: Array<T>): void {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
}
function stall(duration: number): Promise<void> {
return new Promise((resolve) => { setTimeout(resolve, duration); });
}
function getTime(): number { return (new Date()).getTime(); }
function stringify(value: any): string {
return JSON.stringify(value, (key, value) => {
if (typeof(value) === "bigint") {
return { type: "bigint", value: value.toString() };
}
return value;
});
}
/**
* A configuration entry for how to use a [[Provider]].
*/
export interface FallbackProviderConfig {
/**
* The provider.
*/
provider: AbstractProvider;
/**
* 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;
/**
* The priority. Lower priority providers are dispatched first.
*/
priority?: number;
/**
* The amount of weight a provider is given against the quorum.
*/
weight?: number;
};
const defaultConfig = { stallTimeout: 400, priority: 1, weight: 1 };
// We track a bunch of extra stuff that might help debug problems or
// optimize infrastructure later on.
/**
* The statistics and state maintained for a [[Provider]].
*/
export interface FallbackProviderState extends Required<FallbackProviderConfig> {
/**
* The most recent blockNumber this provider has reported (-2 if none).
*/
blockNumber: number;
/**
* The number of total requests ever sent to this provider.
*/
requests: number;
/**
* The number of responses that errored.
*/
errorResponses: number;
/**
* The number of responses that occured after the result resolved.
*/
lateResponses: number;
/**
* How many times syncing was required to catch up the expected block.
*/
outOfSync: number;
/**
* The number of requests which reported unsupported operation.
*/
unsupportedEvents: number;
/**
* A rolling average (5% current duration) for response time.
*/
rollingDuration: number;
/**
* The ratio of quorum-agreed results to total.
*/
score: number;
}
interface Config extends FallbackProviderState {
_updateNumber: null | Promise<any>;
_network: null | Network;
_totalTime: number;
_lastFatalError: null | Error;
_lastFatalErrorTimestamp: number;
}
const defaultState = {
blockNumber: -2, requests: 0, lateResponses: 0, errorResponses: 0,
outOfSync: -1, unsupportedEvents: 0, rollingDuration: 0, score: 0,
_network: null, _updateNumber: null, _totalTime: 0,
_lastFatalError: null, _lastFatalErrorTimestamp: 0
};
async function waitForSync(config: Config, blockNumber: number): Promise<void> {
while (config.blockNumber < 0 || config.blockNumber < blockNumber) {
if (!config._updateNumber) {
config._updateNumber = (async () => {
try {
const blockNumber = await config.provider.getBlockNumber();
if (blockNumber > config.blockNumber) {
config.blockNumber = blockNumber;
}
} catch (error: any) {
config.blockNumber = -2;
config._lastFatalError = error;
config._lastFatalErrorTimestamp = getTime();
}
config._updateNumber = null;
})();
}
await config._updateNumber;
config.outOfSync++;
if (config._lastFatalError) { break; }
}
}
/**
* Additional options to configure a [[FallbackProvider]].
*/
export type FallbackProviderOptions = {
// How many providers must agree on a value before reporting
// back the response
quorum?: number;
// How many providers must have reported the same event
// for it to be emitted (currently unimplmented)
eventQuorum?: number;
// How many providers to dispatch each event to simultaneously.
// Set this to 0 to use getLog polling, which implies eventQuorum
// is equal to quorum. (currently unimplemented)
eventWorkers?: number;
cacheTimeout?: number;
pollingInterval?: number;
};
type RunnerResult = { result: any } | { error: Error };
type RunnerState = {
config: Config;
staller: null | Promise<void>;
didBump: boolean;
perform: null | Promise<any>;
result: null | RunnerResult;
}
function _normalize(value: any): string {
if (value == null) { return "null"; }
if (Array.isArray(value)) {
return "[" + (value.map(_normalize)).join(",") + "]";
}
if (typeof(value) === "object" && typeof(value.toJSON) === "function") {
return _normalize(value.toJSON());
}
switch (typeof(value)) {
case "boolean": case "symbol":
return value.toString();
case "bigint": case "number":
return BigInt(value).toString();
case "string":
return JSON.stringify(value);
case "object": {
const keys = Object.keys(value);
keys.sort();
return "{" + keys.map((k) => `${ JSON.stringify(k) }:${ _normalize(value[k]) }`).join(",") + "}";
}
}
console.log("Could not serialize", value);
throw new Error("Hmm...");
}
function normalizeResult(method: string, value: RunnerResult): { tag: string, value: any } {
if ("error" in value) {
const error = value.error;
let tag: string;
if (isError(error, "CALL_EXCEPTION")) {
tag = _normalize(Object.assign({ }, error, {
shortMessage: undefined, reason: undefined, info: undefined
}));
} else {
tag = _normalize(error)
}
return { tag, value: error };
}
const result = value.result;
return { tag: _normalize(result), value: result };
}
type TallyResult = {
tag: string;
value: any;
weight: number;
};
// This strategy picks the highest weight result, as long as the weight is
// equal to or greater than quorum
function checkQuorum(quorum: number, results: Array<TallyResult>): any | Error {
const tally: Map<string, { value: any, weight: number }> = new Map();
for (const { value, tag, weight } of results) {
const t = tally.get(tag) || { value, weight: 0 };
t.weight += weight;
tally.set(tag, t);
}
let best: null | { value: any, weight: number } = null;
for (const r of tally.values()) {
if (r.weight >= quorum && (!best || r.weight > best.weight)) {
best = r;
}
}
if (best) { return best.value; }
return undefined;
}
function getMedian(quorum: number, results: Array<TallyResult>): undefined | bigint | Error {
let resultWeight = 0;
const errorMap: Map<string, { weight: number, value: Error }> = new Map();
let bestError: null | { weight: number, value: Error } = null;
const values: Array<bigint> = [ ];
for (const { value, tag, weight } of results) {
if (value instanceof Error) {
const e = errorMap.get(tag) || { value, weight: 0 };
e.weight += weight;
errorMap.set(tag, e);
if (bestError == null || e.weight > bestError.weight) { bestError = e; }
} else {
values.push(BigInt(value));
resultWeight += weight;
}
}
if (resultWeight < quorum) {
// We have quorum for an error
if (bestError && bestError.weight >= quorum) { return bestError.value; }
// We do not have quorum for a result
return undefined;
}
// Get the sorted values
values.sort((a, b) => ((a < b) ? -1: (b > a) ? 1: 0));
const mid = Math.floor(values.length / 2);
// Odd-length; take the middle value
if (values.length % 2) { return values[mid]; }
// Even length; take the ceiling of the mean of the center two values
return (values[mid - 1] + values[mid] + BN_1) / BN_2;
}
function getAnyResult(quorum: number, results: Array<TallyResult>): undefined | any | Error {
// If any value or error meets quorum, that is our preferred result
const result = checkQuorum(quorum, results);
if (result !== undefined) { return result; }
// Otherwise, do we have any result?
for (const r of results) {
if (r.value) { return r.value; }
}
// Nope!
return undefined;
}
function getFuzzyMode(quorum: number, results: Array<TallyResult>): undefined | number {
if (quorum === 1) { return getNumber(<bigint>getMedian(quorum, results), "%internal"); }
const tally: Map<number, { result: number, weight: number }> = new Map();
const add = (result: number, weight: number) => {
const t = tally.get(result) || { result, weight: 0 };
t.weight += weight;
tally.set(result, t);
};
for (const { weight, value } of results) {
const r = getNumber(value);
add(r - 1, weight);
add(r, weight);
add(r + 1, weight);
}
let bestWeight = 0;
let bestResult: undefined | number = undefined;
for (const { weight, result } of tally.values()) {
// Use this result, if this result meets quorum and has either:
// - a better weight
// - or equal weight, but the result is larger
if (weight >= quorum && (weight > bestWeight || (bestResult != null && weight === bestWeight && result > bestResult))) {
bestWeight = weight;
bestResult = result;
}
}
return bestResult;
}
/**
* A **FallbackProvider** manages several [[Providers]] providing
* resilience 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 {
/**
* The number of backends that must agree on a value before it is
* accpeted.
*/
readonly quorum: number;
/**
* @_ignore:
*/
readonly eventQuorum: number;
/**
* @_ignore:
*/
readonly eventWorkers: number;
readonly #configs: Array<Config>;
#height: number;
#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, options?: FallbackProviderOptions) {
super(network, options);
this.#configs = providers.map((p) => {
if (p instanceof AbstractProvider) {
return Object.assign({ provider: p }, defaultConfig, defaultState );
} else {
return Object.assign({ }, defaultConfig, p, defaultState );
}
});
this.#height = -2;
this.#initialSyncPromise = null;
if (options && options.quorum != null) {
this.quorum = options.quorum;
} else {
this.quorum = Math.ceil(this.#configs.reduce((accum, config) => {
accum += config.weight;
return accum;
}, 0) / 2);
}
this.eventQuorum = 1;
this.eventWorkers = 1;
assertArgument(this.quorum <= this.#configs.reduce((a, c) => (a + c.weight), 0),
"quorum exceed provider weight", "quorum", this.quorum);
}
get providerConfigs(): Array<FallbackProviderState> {
return this.#configs.map((c) => {
const result: any = Object.assign({ }, c);
for (const key in result) {
if (key[0] === "_") { delete result[key]; }
}
return result;
});
}
async _detectNetwork(): Promise<Network> {
return Network.from(getBigInt(await this._perform({ method: "chainId" })));
}
// @TODO: Add support to select providers to be the event subscriber
//_getSubscriber(sub: Subscription): Subscriber {
// throw new Error("@TODO");
//}
/**
* Transforms a %%req%% into the correct method call on %%provider%%.
*/
async _translatePerform(provider: AbstractProvider, req: PerformActionRequest): Promise<any> {
switch (req.method) {
case "broadcastTransaction":
return await provider.broadcastTransaction(req.signedTransaction);
case "call":
return await provider.call(Object.assign({ }, req.transaction, { blockTag: req.blockTag }));
case "chainId":
return (await provider.getNetwork()).chainId;
case "estimateGas":
return await provider.estimateGas(req.transaction);
case "getBalance":
return await provider.getBalance(req.address, req.blockTag);
case "getBlock": {
const block = ("blockHash" in req) ? req.blockHash: req.blockTag;
return await provider.getBlock(block, req.includeTransactions);
}
case "getBlockNumber":
return await provider.getBlockNumber();
case "getCode":
return await provider.getCode(req.address, req.blockTag);
case "getGasPrice":
return (await provider.getFeeData()).gasPrice;
case "getPriorityFee":
return (await provider.getFeeData()).maxPriorityFeePerGas;
case "getLogs":
return await provider.getLogs(req.filter);
case "getStorage":
return await provider.getStorage(req.address, req.position, req.blockTag);
case "getTransaction":
return await provider.getTransaction(req.hash);
case "getTransactionCount":
return await provider.getTransactionCount(req.address, req.blockTag);
case "getTransactionReceipt":
return await provider.getTransactionReceipt(req.hash);
case "getTransactionResult":
return await provider.getTransactionResult(req.hash);
}
}
// Grab the next (random) config that is not already part of
// the running set
#getNextConfig(running: Set<RunnerState>): null | Config {
// @TODO: Maybe do a check here to favour (heavily) providers that
// do not require waitForSync and disfavour providers that
// seem down-ish or are behaving slowly
const configs = Array.from(running).map((r) => r.config)
// Shuffle the states, sorted by priority
const allConfigs = this.#configs.slice();
shuffle(allConfigs);
allConfigs.sort((a, b) => (a.priority - b.priority));
for (const config of allConfigs) {
if (config._lastFatalError) { continue; }
if (configs.indexOf(config) === -1) { return config; }
}
return null;
}
// Adds a new runner (if available) to running.
#addRunner(running: Set<RunnerState>, req: PerformActionRequest): null | RunnerState {
const config = this.#getNextConfig(running);
// No runners available
if (config == null) { return null; }
// Create a new runner
const runner: RunnerState = {
config, result: null, didBump: false,
perform: null, staller: null
};
const now = getTime();
// Start performing this operation
runner.perform = (async () => {
try {
config.requests++;
const result = await this._translatePerform(config.provider, req);
runner.result = { result };
} catch (error: any) {
config.errorResponses++;
runner.result = { error };
}
const dt = (getTime() - now);
config._totalTime += dt;
config.rollingDuration = 0.95 * config.rollingDuration + 0.05 * dt;
runner.perform = null;
})();
// Start a staller; when this times out, it's time to force
// kicking off another runner because we are taking too long
runner.staller = (async () => {
await stall(config.stallTimeout);
runner.staller = null;
})();
running.add(runner);
return runner;
}
// Initializes the blockNumber and network for each runner and
// blocks until initialized
async #initialSync(): Promise<void> {
let initialSync = this.#initialSyncPromise;
if (!initialSync) {
const promises: Array<Promise<any>> = [ ];
this.#configs.forEach((config) => {
promises.push((async () => {
await waitForSync(config, 0);
if (!config._lastFatalError) {
config._network = await config.provider.getNetwork();
}
})());
});
this.#initialSyncPromise = initialSync = (async () => {
// Wait for all providers to have a block number and network
await Promise.all(promises);
// Check all the networks match
let chainId: null | bigint = null;
for (const config of this.#configs) {
if (config._lastFatalError) { continue; }
const network = <Network>(config._network);
if (chainId == null) {
chainId = network.chainId;
} else if (network.chainId !== chainId) {
assert(false, "cannot mix providers on different networks", "UNSUPPORTED_OPERATION", {
operation: "new FallbackProvider"
});
}
}
})();
}
await initialSync
}
async #checkQuorum(running: Set<RunnerState>, req: PerformActionRequest): Promise<any> {
// Get all the result objects
const results: Array<TallyResult> = [ ];
for (const runner of running) {
if (runner.result != null) {
const { tag, value } = normalizeResult(req.method, runner.result);
results.push({ tag, value, weight: runner.config.weight });
}
}
// Are there enough results to event meet quorum?
if (results.reduce((a, r) => (a + r.weight), 0) < this.quorum) {
return undefined;
}
switch (req.method) {
case "getBlockNumber": {
// We need to get the bootstrap block height
if (this.#height === -2) {
this.#height = Math.ceil(getNumber(<bigint>getMedian(this.quorum, this.#configs.filter((c) => (!c._lastFatalError)).map((c) => ({
value: c.blockNumber,
tag: getNumber(c.blockNumber).toString(),
weight: c.weight
})))));
}
// Find the mode across all the providers, allowing for
// a little drift between block heights
const mode = getFuzzyMode(this.quorum, results);
if (mode === undefined) { return undefined; }
if (mode > this.#height) { this.#height = mode; }
return this.#height;
}
case "getGasPrice":
case "getPriorityFee":
case "estimateGas":
return getMedian(this.quorum, results);
case "getBlock":
// Pending blocks are in the mempool and already
// quite untrustworthy; just grab anything
if ("blockTag" in req && req.blockTag === "pending") {
return getAnyResult(this.quorum, results);
}
return checkQuorum(this.quorum, results);
case "call":
case "chainId":
case "getBalance":
case "getTransactionCount":
case "getCode":
case "getStorage":
case "getTransaction":
case "getTransactionReceipt":
case "getLogs":
return checkQuorum(this.quorum, results);
case "broadcastTransaction":
return getAnyResult(this.quorum, results);
}
assert(false, "unsupported method", "UNSUPPORTED_OPERATION", {
operation: `_perform(${ stringify((<any>req).method) })`
});
}
async #waitForQuorum(running: Set<RunnerState>, req: PerformActionRequest): Promise<any> {
if (running.size === 0) { throw new Error("no runners?!"); }
// Any promises that are interesting to watch for; an expired stall
// or a successful perform
const interesting: Array<Promise<void>> = [ ];
let newRunners = 0;
for (const runner of running) {
// No responses, yet; keep an eye on it
if (runner.perform) {
interesting.push(runner.perform);
}
// Still stalling...
if (runner.staller) {
interesting.push(runner.staller);
continue;
}
// This runner has already triggered another runner
if (runner.didBump) { continue; }
// Got a response (result or error) or stalled; kick off another runner
runner.didBump = true;
newRunners++;
}
// Check if we have reached quorum on a result (or error)
const value = await this.#checkQuorum(running, req);
if (value !== undefined) {
if (value instanceof Error) { throw value; }
return value;
}
// Add any new runners, because a staller timed out or a result
// or error response came in.
for (let i = 0; i < newRunners; i++) {
this.#addRunner(running, req);
}
// All providers have returned, and we have no result
assert(interesting.length > 0, "quorum not met", "SERVER_ERROR", {
request: "%sub-requests",
info: { request: req, results: Array.from(running).map((r) => stringify(r.result)) }
});
// Wait for someone to either complete its perform or stall out
await Promise.race(interesting);
// This is recursive, but at worst case the depth is 2x the
// number of providers (each has a perform and a staller)
return await this.#waitForQuorum(running, req);
}
async _perform<T = any>(req: PerformActionRequest): Promise<T> {
// Broadcasting a transaction is rare (ish) and already incurs
// a cost on the user, so spamming is safe-ish. Just send it to
// every backend.
if (req.method === "broadcastTransaction") {
// Once any broadcast provides a positive result, use it. No
// need to wait for anyone else
const results: Array<null | TallyResult> = this.#configs.map((c) => null);
const broadcasts = this.#configs.map(async ({ provider, weight }, index) => {
try {
const result = await provider._perform(req);
results[index] = Object.assign(normalizeResult(req.method, { result }), { weight });
} catch (error: any) {
results[index] = Object.assign(normalizeResult(req.method, { error }), { weight });
}
});
// As each promise finishes...
while (true) {
// Check for a valid broadcast result
const done = <Array<any>>results.filter((r) => (r != null));
for (const { value } of done) {
if (!(value instanceof Error)) { return value; }
}
// Check for a legit broadcast error (one which we cannot
// recover from; some nodes may return the following red
// herring events:
// - alredy seend (UNKNOWN_ERROR)
// - NONCE_EXPIRED
// - REPLACEMENT_UNDERPRICED
const result = checkQuorum(this.quorum, <Array<any>>results.filter((r) => (r != null)));
if (isError(result, "INSUFFICIENT_FUNDS")) {
throw result;
}
// Kick off the next provider (if any)
const waiting = broadcasts.filter((b, i) => (results[i] == null));
if (waiting.length === 0) { break; }
await Promise.race(waiting);
}
// Use standard quorum results; any result was returned above,
// so this will find any error that met quorum if any
const result = getAnyResult(this.quorum, <Array<any>>results);
assert(result !== undefined, "problem multi-broadcasting", "SERVER_ERROR", {
request: "%sub-requests",
info: { request: req, results: results.map(stringify) }
})
if (result instanceof Error) { throw result; }
return result;
}
await this.#initialSync();
// Bootstrap enough runners to meet quorum
const running: Set<RunnerState> = new Set();
let inflightQuorum = 0;
while (true) {
const runner = this.#addRunner(running, req);
if (runner == null) { break; }
inflightQuorum += runner.config.weight;
if (inflightQuorum >= this.quorum) { break; }
}
const result = await this.#waitForQuorum(running, req);
// Track requests sent to a provider that are still
// outstanding after quorum has been otherwise found
for (const runner of running) {
if (runner.perform && runner.result == null) {
runner.config.lateResponses++;
}
}
return result;
}
async destroy(): Promise<void> {
for (const { provider } of this.#configs) {
provider.destroy();
}
super.destroy();
}
}

View File

@@ -0,0 +1,220 @@
/**
* [[link-infura]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Sepolia Testnet (``sepolia``)
* - Arbitrum (``arbitrum``)
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
* - Base (``base``)
* - Base Goerlia Testnet (``base-goerli``)
* - Base Sepolia Testnet (``base-sepolia``)
* - BNB Smart Chain Mainnet (``bnb``)
* - BNB Smart Chain Testnet (``bnbt``)
* - Linea (``linea``)
* - Linea Goerli Testnet (``linea-goerli``)
* - Linea Sepolia Testnet (``linea-sepolia``)
* - Optimism (``optimism``)
* - Optimism Goerli Testnet (``optimism-goerli``)
* - Optimism Sepolia Testnet (``optimism-sepolia``)
* - Polygon (``matic``)
* - Polygon Amoy Testnet (``matic-amoy``)
* - Polygon Mumbai Testnet (``matic-mumbai``)
*
* @_subsection: api/providers/thirdparty:INFURA [providers-infura]
*/
import {
defineProperties, FetchRequest, assert, assertArgument
} from "../utils/index.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import { WebSocketProvider } from "./provider-websocket.js";
import type { AbstractProvider } from "./abstract-provider.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
const defaultProjectId = "84842078b09946638c03157f83405213";
function getHost(name: string): string {
switch(name) {
case "mainnet":
return "mainnet.infura.io";
case "goerli":
return "goerli.infura.io";
case "sepolia":
return "sepolia.infura.io";
case "arbitrum":
return "arbitrum-mainnet.infura.io";
case "arbitrum-goerli":
return "arbitrum-goerli.infura.io";
case "arbitrum-sepolia":
return "arbitrum-sepolia.infura.io";
case "base":
return "base-mainnet.infura.io";
case "base-goerlia": // @TODO: Remove this typo in the future!
case "base-goerli":
return "base-goerli.infura.io";
case "base-sepolia":
return "base-sepolia.infura.io";
case "bnb":
return "bsc-mainnet.infura.io";
case "bnbt":
return "bsc-testnet.infura.io";
case "linea":
return "linea-mainnet.infura.io";
case "linea-goerli":
return "linea-goerli.infura.io";
case "linea-sepolia":
return "linea-sepolia.infura.io";
case "matic":
return "polygon-mainnet.infura.io";
case "matic-amoy":
return "polygon-amoy.infura.io";
case "matic-mumbai":
return "polygon-mumbai.infura.io";
case "optimism":
return "optimism-mainnet.infura.io";
case "optimism-goerli":
return "optimism-goerli.infura.io";
case "optimism-sepolia":
return "optimism-sepolia.infura.io";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **InfuraWebSocketProvider** connects to the [[link-infura]]
* WebSocket end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-infura-signup).
*/
export class InfuraWebSocketProvider extends WebSocketProvider implements CommunityResourcable {
/**
* The Project ID for the INFURA connection.
*/
readonly projectId!: string;
/**
* The Project Secret.
*
* If null, no authenticated requests are made. This should not
* be used outside of private contexts.
*/
readonly projectSecret!: null | string;
/**
* Creates a new **InfuraWebSocketProvider**.
*/
constructor(network?: Networkish, projectId?: string) {
const provider = new InfuraProvider(network, projectId);
const req = provider._getConnection();
assert(!req.credentials, "INFURA WebSocket project secrets unsupported",
"UNSUPPORTED_OPERATION", { operation: "InfuraProvider.getWebSocketProvider()" });
const url = req.url.replace(/^http/i, "ws").replace("/v3/", "/ws/v3/");
super(url, provider._network);
defineProperties<InfuraWebSocketProvider>(this, {
projectId: provider.projectId,
projectSecret: provider.projectSecret
});
}
isCommunityResource(): boolean {
return (this.projectId === defaultProjectId);
}
}
/**
* The **InfuraProvider** connects to the [[link-infura]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-infura-signup).
*/
export class InfuraProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The Project ID for the INFURA connection.
*/
readonly projectId!: string;
/**
* The Project Secret.
*
* If null, no authenticated requests are made. This should not
* be used outside of private contexts.
*/
readonly projectSecret!: null | string;
/**
* Creates a new **InfuraProvider**.
*/
constructor(_network?: Networkish, projectId?: null | string, projectSecret?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (projectId == null) { projectId = defaultProjectId; }
if (projectSecret == null) { projectSecret = null; }
const request = InfuraProvider.getRequest(network, projectId, projectSecret);
super(request, network, { staticNetwork: network });
defineProperties<InfuraProvider>(this, { projectId, projectSecret });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new InfuraProvider(chainId, this.projectId, this.projectSecret);
} catch (error) { }
return super._getProvider(chainId);
}
isCommunityResource(): boolean {
return (this.projectId === defaultProjectId);
}
/**
* Creates a new **InfuraWebSocketProvider**.
*/
static getWebSocketProvider(network?: Networkish, projectId?: string): InfuraWebSocketProvider {
return new InfuraWebSocketProvider(network, projectId);
}
/**
* Returns a prepared request for connecting to %%network%%
* with %%projectId%% and %%projectSecret%%.
*/
static getRequest(network: Network, projectId?: null | string, projectSecret?: null | string): FetchRequest {
if (projectId == null) { projectId = defaultProjectId; }
if (projectSecret == null) { projectSecret = null; }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v3/${ projectId }`);
request.allowGzip = true;
if (projectSecret) { request.setCredentials("", projectSecret); }
if (projectId === defaultProjectId) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("InfuraProvider");
return true;
};
}
return request;
}
}

View File

@@ -0,0 +1,3 @@
const IpcSocketProvider = undefined;
export { IpcSocketProvider };

View File

@@ -0,0 +1,81 @@
import { connect } from "net";
import { SocketProvider } from "./provider-socket.js";
import type { Socket } from "net";
import type { JsonRpcApiProviderOptions } from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
// @TODO: Is this sufficient? Is this robust? Will newlines occur between
// all payloads and only between payloads?
function splitBuffer(data: Buffer): { messages: Array<string>, remaining: Buffer } {
const messages: Array<string> = [ ];
let lastStart = 0;
while (true) {
const nl = data.indexOf(10, lastStart);
if (nl === -1) { break; }
messages.push(data.subarray(lastStart, nl).toString().trim());
lastStart = nl + 1;
}
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 {
#socket: Socket;
/**
* The connected socket.
*/
get socket(): Socket { return this.#socket; }
constructor(path: string, network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network, options);
this.#socket = connect(path);
this.socket.on("ready", async () => {
try {
await this._start();
} catch (error) {
console.log("failed to start IpcSocketProvider", error);
// @TODO: Now what? Restart?
}
});
let response = Buffer.alloc(0);
this.socket.on("data", (data) => {
response = Buffer.concat([ response, data ]);
const { messages, remaining } = splitBuffer(response);
messages.forEach((message) => {
this._processMessage(message);
});
response = remaining;
});
this.socket.on("end", () => {
this.emit("close");
this.socket.destroy();
this.socket.end();
});
}
destroy(): void {
this.socket.destroy();
this.socket.end();
super.destroy();
}
async _write(message: string): Promise<void> {
if (!message.endsWith("\n")) { message += "\n"; }
this.socket.write(message);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,121 @@
/**
* [[link-pocket]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Polygon (``matic``)
* - Arbitrum (``arbitrum``)
*
* @_subsection: api/providers/thirdparty:Pocket [providers-pocket]
*/
import {
defineProperties, FetchRequest, assertArgument
} from "../utils/index.js";
import { AbstractProvider } from "./abstract-provider.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
const defaultApplicationId = "62e1ad51b37b8e00394bda3b";
function getHost(name: string): string {
switch (name) {
case "mainnet":
return "eth-mainnet.gateway.pokt.network";
case "goerli":
return "eth-goerli.gateway.pokt.network";
case "matic":
return "poly-mainnet.gateway.pokt.network";
case "matic-mumbai":
return "polygon-mumbai-rpc.gateway.pokt.network";
}
assertArgument(false, "unsupported network", "network", name);
}
/**
* The **PocketProvider** connects to the [[link-pocket]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API key is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-pocket-signup).
*/
export class PocketProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The Application ID for the Pocket connection.
*/
readonly applicationId!: string;
/**
* The Application Secret for making authenticated requests
* to the Pocket connection.
*/
readonly applicationSecret!: null | string;
/**
* Create a new **PocketProvider**.
*
* By default connecting to ``mainnet`` with a highly throttled
* API key.
*/
constructor(_network?: Networkish, applicationId?: null | string, applicationSecret?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (applicationId == null) { applicationId = defaultApplicationId; }
if (applicationSecret == null) { applicationSecret = null; }
const options = { staticNetwork: network };
const request = PocketProvider.getRequest(network, applicationId, applicationSecret);
super(request, network, options);
defineProperties<PocketProvider>(this, { applicationId, applicationSecret });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new PocketProvider(chainId, this.applicationId, this.applicationSecret);
} catch (error) { }
return super._getProvider(chainId);
}
/**
* Returns a prepared request for connecting to %%network%% with
* %%applicationId%%.
*/
static getRequest(network: Network, applicationId?: null | string, applicationSecret?: null | string): FetchRequest {
if (applicationId == null) { applicationId = defaultApplicationId; }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/v1/lb/${ applicationId }`);
request.allowGzip = true;
if (applicationSecret) {
request.setCredentials("", applicationSecret);
}
if (applicationId === defaultApplicationId) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("PocketProvider");
return true;
};
}
return request;
}
isCommunityResource(): boolean {
return (this.applicationId === defaultApplicationId);
}
}

View File

@@ -0,0 +1,177 @@
/**
* [[link-quicknode]] provides a third-party service for connecting to
* various blockchains over JSON-RPC.
*
* **Supported Networks**
*
* - Ethereum Mainnet (``mainnet``)
* - Goerli Testnet (``goerli``)
* - Sepolia Testnet (``sepolia``)
* - Holesky Testnet (``holesky``)
* - Arbitrum (``arbitrum``)
* - Arbitrum Goerli Testnet (``arbitrum-goerli``)
* - Arbitrum Sepolia Testnet (``arbitrum-sepolia``)
* - Base Mainnet (``base``);
* - Base Goerli Testnet (``base-goerli``);
* - Base Sepolia Testnet (``base-sepolia``);
* - BNB Smart Chain Mainnet (``bnb``)
* - BNB Smart Chain Testnet (``bnbt``)
* - Optimism (``optimism``)
* - Optimism Goerli Testnet (``optimism-goerli``)
* - Optimism Sepolia Testnet (``optimism-sepolia``)
* - Polygon (``matic``)
* - Polygon Mumbai Testnet (``matic-mumbai``)
*
* @_subsection: api/providers/thirdparty:QuickNode [providers-quicknode]
*/
import {
defineProperties, FetchRequest, assertArgument
} from "../utils/index.js";
import { showThrottleMessage } from "./community.js";
import { Network } from "./network.js";
import { JsonRpcProvider } from "./provider-jsonrpc.js";
import type { AbstractProvider } from "./abstract-provider.js";
import type { CommunityResourcable } from "./community.js";
import type { Networkish } from "./network.js";
const defaultToken = "919b412a057b5e9c9b6dce193c5a60242d6efadb";
function getHost(name: string): string {
switch(name) {
case "mainnet":
return "ethers.quiknode.pro";
case "goerli":
return "ethers.ethereum-goerli.quiknode.pro";
case "sepolia":
return "ethers.ethereum-sepolia.quiknode.pro";
case "holesky":
return "ethers.ethereum-holesky.quiknode.pro";
case "arbitrum":
return "ethers.arbitrum-mainnet.quiknode.pro";
case "arbitrum-goerli":
return "ethers.arbitrum-goerli.quiknode.pro";
case "arbitrum-sepolia":
return "ethers.arbitrum-sepolia.quiknode.pro";
case "base":
return "ethers.base-mainnet.quiknode.pro";
case "base-goerli":
return "ethers.base-goerli.quiknode.pro";
case "base-spolia":
return "ethers.base-sepolia.quiknode.pro";
case "bnb":
return "ethers.bsc.quiknode.pro";
case "bnbt":
return "ethers.bsc-testnet.quiknode.pro";
case "matic":
return "ethers.matic.quiknode.pro";
case "matic-mumbai":
return "ethers.matic-testnet.quiknode.pro";
case "optimism":
return "ethers.optimism.quiknode.pro";
case "optimism-goerli":
return "ethers.optimism-goerli.quiknode.pro";
case "optimism-sepolia":
return "ethers.optimism-sepolia.quiknode.pro";
case "xdai":
return "ethers.xdai.quiknode.pro";
}
assertArgument(false, "unsupported network", "network", name);
}
/*
@TODO:
These networks are not currently present in the Network
default included networks. Research them and ensure they
are EVM compatible and work with ethers
http://ethers.matic-amoy.quiknode.pro
http://ethers.avalanche-mainnet.quiknode.pro
http://ethers.avalanche-testnet.quiknode.pro
http://ethers.blast-sepolia.quiknode.pro
http://ethers.celo-mainnet.quiknode.pro
http://ethers.fantom.quiknode.pro
http://ethers.imx-demo.quiknode.pro
http://ethers.imx-mainnet.quiknode.pro
http://ethers.imx-testnet.quiknode.pro
http://ethers.near-mainnet.quiknode.pro
http://ethers.near-testnet.quiknode.pro
http://ethers.nova-mainnet.quiknode.pro
http://ethers.scroll-mainnet.quiknode.pro
http://ethers.scroll-testnet.quiknode.pro
http://ethers.tron-mainnet.quiknode.pro
http://ethers.zkevm-mainnet.quiknode.pro
http://ethers.zkevm-testnet.quiknode.pro
http://ethers.zksync-mainnet.quiknode.pro
http://ethers.zksync-testnet.quiknode.pro
*/
/**
* The **QuickNodeProvider** connects to the [[link-quicknode]]
* JSON-RPC end-points.
*
* By default, a highly-throttled API token is used, which is
* appropriate for quick prototypes and simple scripts. To
* gain access to an increased rate-limit, it is highly
* recommended to [sign up here](link-quicknode).
*/
export class QuickNodeProvider extends JsonRpcProvider implements CommunityResourcable {
/**
* The API token.
*/
readonly token!: string;
/**
* Creates a new **QuickNodeProvider**.
*/
constructor(_network?: Networkish, token?: null | string) {
if (_network == null) { _network = "mainnet"; }
const network = Network.from(_network);
if (token == null) { token = defaultToken; }
const request = QuickNodeProvider.getRequest(network, token);
super(request, network, { staticNetwork: network });
defineProperties<QuickNodeProvider>(this, { token });
}
_getProvider(chainId: number): AbstractProvider {
try {
return new QuickNodeProvider(chainId, this.token);
} catch (error) { }
return super._getProvider(chainId);
}
isCommunityResource(): boolean {
return (this.token === defaultToken);
}
/**
* Returns a new request prepared for %%network%% and the
* %%token%%.
*/
static getRequest(network: Network, token?: null | string): FetchRequest {
if (token == null) { token = defaultToken; }
const request = new FetchRequest(`https:/\/${ getHost(network.name) }/${ token }`);
request.allowGzip = true;
//if (projectSecret) { request.setCredentials("", projectSecret); }
if (token === defaultToken) {
request.retryFunc = async (request, response, attempt) => {
showThrottleMessage("QuickNodeProvider");
return true;
};
}
return request;
}
}

View File

@@ -0,0 +1,352 @@
/**
* Generic long-lived socket provider.
*
* Sub-classing notes
* - a sub-class MUST call the `_start()` method once connected
* - a sub-class MUST override the `_write(string)` method
* - a sub-class MUST call `_processMessage(string)` for each message
*
* @_subsection: api/providers/abstract-provider:Socket Providers [about-socketProvider]
*/
import { UnmanagedSubscriber } from "./abstract-provider.js";
import { assert, assertArgument, makeError } from "../utils/index.js";
import { JsonRpcApiProvider } from "./provider-jsonrpc.js";
import type { Subscriber, Subscription } from "./abstract-provider.js";
import type { EventFilter } from "./provider.js";
import type {
JsonRpcApiProviderOptions, JsonRpcError, JsonRpcPayload, JsonRpcResult
} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
type JsonRpcSubscription = {
method: string,
params: {
result: any,
subscription: string
}
};
/**
* A **SocketSubscriber** uses a socket transport to handle events and
* should use [[_emit]] to manage the events.
*/
export class SocketSubscriber implements Subscriber {
#provider: SocketProvider;
#filter: string;
/**
* The filter.
*/
get filter(): Array<any> { return JSON.parse(this.#filter); }
#filterId: null | Promise<string |number>;
#paused: null | boolean;
#emitPromise: null | Promise<void>;
/**
* Creates a new **SocketSubscriber** attached to %%provider%% listening
* to %%filter%%.
*/
constructor(provider: SocketProvider, filter: Array<any>) {
this.#provider = provider;
this.#filter = JSON.stringify(filter);
this.#filterId = null;
this.#paused = null;
this.#emitPromise = null;
}
start(): void {
this.#filterId = this.#provider.send("eth_subscribe", this.filter).then((filterId) => {;
this.#provider._register(filterId, this);
return filterId;
});
}
stop(): void {
(<Promise<number>>(this.#filterId)).then((filterId) => {
if (this.#provider.destroyed) { return; }
this.#provider.send("eth_unsubscribe", [ filterId ]);
});
this.#filterId = null;
}
// @TODO: pause should trap the current blockNumber, unsub, and on resume use getLogs
// and resume
pause(dropWhilePaused?: boolean): void {
assert(dropWhilePaused, "preserve logs while paused not supported by SocketSubscriber yet",
"UNSUPPORTED_OPERATION", { operation: "pause(false)" });
this.#paused = !!dropWhilePaused;
}
resume(): void {
this.#paused = null;
}
/**
* @_ignore:
*/
_handleMessage(message: any): void {
if (this.#filterId == null) { return; }
if (this.#paused === null) {
let emitPromise: null | Promise<void> = this.#emitPromise;
if (emitPromise == null) {
emitPromise = this._emit(this.#provider, message);
} else {
emitPromise = emitPromise.then(async () => {
await this._emit(this.#provider, message);
});
}
this.#emitPromise = emitPromise.then(() => {
if (this.#emitPromise === emitPromise) {
this.#emitPromise = null;
}
});
}
}
/**
* Sub-classes **must** override this to emit the events on the
* provider.
*/
async _emit(provider: SocketProvider, message: any): Promise<void> {
throw new Error("sub-classes must implemente this; _emit");
}
}
/**
* A **SocketBlockSubscriber** listens for ``newHeads`` events and emits
* ``"block"`` events.
*/
export class SocketBlockSubscriber extends SocketSubscriber {
/**
* @_ignore:
*/
constructor(provider: SocketProvider) {
super(provider, [ "newHeads" ]);
}
async _emit(provider: SocketProvider, message: any): Promise<void> {
provider.emit("block", parseInt(message.number));
}
}
/**
* A **SocketPendingSubscriber** listens for pending transacitons and emits
* ``"pending"`` events.
*/
export class SocketPendingSubscriber extends SocketSubscriber {
/**
* @_ignore:
*/
constructor(provider: SocketProvider) {
super(provider, [ "newPendingTransactions" ]);
}
async _emit(provider: SocketProvider, message: any): Promise<void> {
provider.emit("pending", message);
}
}
/**
* A **SocketEventSubscriber** listens for event logs.
*/
export class SocketEventSubscriber extends SocketSubscriber {
#logFilter: string;
/**
* The filter.
*/
get logFilter(): EventFilter { return JSON.parse(this.#logFilter); }
/**
* @_ignore:
*/
constructor(provider: SocketProvider, filter: EventFilter) {
super(provider, [ "logs", filter ]);
this.#logFilter = JSON.stringify(filter);
}
async _emit(provider: SocketProvider, message: any): Promise<void> {
provider.emit(this.logFilter, provider._wrapLog(message, provider._network));
}
}
/**
* 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 {
#callbacks: Map<number, { payload: JsonRpcPayload, resolve: (r: any) => void, reject: (e: Error) => void }>;
// Maps each filterId to its subscriber
#subs: Map<number | string, SocketSubscriber>;
// If any events come in before a subscriber has finished
// registering, queue them
#pending: Map<number | string, Array<any>>;
/**
* Creates a new **SocketProvider** connected to %%network%%.
*
* If unspecified, the network will be discovered.
*/
constructor(network?: Networkish, _options?: JsonRpcApiProviderOptions) {
// Copy the options
const options = Object.assign({ }, (_options != null) ? _options: { });
// Support for batches is generally not supported for
// connection-base providers; if this changes in the future
// the _send should be updated to reflect this
assertArgument(options.batchMaxCount == null || options.batchMaxCount === 1,
"sockets-based providers do not support batches", "options.batchMaxCount", _options);
options.batchMaxCount = 1;
// Socket-based Providers (generally) cannot change their network,
// since they have a long-lived connection; but let people override
// this if they have just cause.
if (options.staticNetwork == null) { options.staticNetwork = true; }
super(network, options);
this.#callbacks = new Map();
this.#subs = new Map();
this.#pending = new Map();
}
// This value is only valid after _start has been called
/*
get _network(): Network {
if (this.#network == null) {
throw new Error("this shouldn't happen");
}
return this.#network.clone();
}
*/
_getSubscriber(sub: Subscription): Subscriber {
switch (sub.type) {
case "close":
return new UnmanagedSubscriber("close");
case "block":
return new SocketBlockSubscriber(this);
case "pending":
return new SocketPendingSubscriber(this);
case "event":
return new SocketEventSubscriber(this, sub.filter);
case "orphan":
// Handled auto-matically within AbstractProvider
// when the log.removed = true
if (sub.filter.orphan === "drop-log") {
return new UnmanagedSubscriber("drop-log");
}
}
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 {
this.#subs.set(filterId, subscriber);
const pending = this.#pending.get(filterId);
if (pending) {
for (const message of pending) {
subscriber._handleMessage(message);
}
this.#pending.delete(filterId);
}
}
async _send(payload: JsonRpcPayload | Array<JsonRpcPayload>): Promise<Array<JsonRpcResult | JsonRpcError>> {
// WebSocket provider doesn't accept batches
assertArgument(!Array.isArray(payload), "WebSocket does not support batch send", "payload", payload);
// @TODO: stringify payloads here and store to prevent mutations
// Prepare a promise to respond to
const promise = new Promise((resolve, reject) => {
this.#callbacks.set(payload.id, { payload, resolve, reject });
});
// Wait until the socket is connected before writing to it
await this._waitUntilReady();
// Write the request to the socket
await this._write(JSON.stringify(payload));
return <Array<JsonRpcResult | JsonRpcError>>[ await promise ];
}
// Sub-classes must call this once they are connected
/*
async _start(): Promise<void> {
if (this.#ready) { return; }
for (const { payload } of this.#callbacks.values()) {
await this._write(JSON.stringify(payload));
}
this.#ready = (async function() {
await super._start();
})();
}
*/
/**
* Sub-classes **must** call this with messages received over their
* transport to be processed and dispatched.
*/
async _processMessage(message: string): Promise<void> {
const result = <JsonRpcResult | JsonRpcError | JsonRpcSubscription>(JSON.parse(message));
if (result && typeof(result) === "object" && "id" in result) {
const callback = this.#callbacks.get(result.id);
if (callback == null) {
this.emit("error", makeError("received result for unknown id", "UNKNOWN_ERROR", {
reasonCode: "UNKNOWN_ID",
result
}));
return;
}
this.#callbacks.delete(result.id);
callback.resolve(result);
} else if (result && result.method === "eth_subscription") {
const filterId = result.params.subscription;
const subscriber = this.#subs.get(filterId);
if (subscriber) {
subscriber._handleMessage(result.params.result);
} else {
let pending = this.#pending.get(filterId);
if (pending == null) {
pending = [ ];
this.#pending.set(filterId, pending);
}
pending.push(result.params.result);
}
} else {
this.emit("error", makeError("received unexpected message", "UNKNOWN_ERROR", {
reasonCode: "UNEXPECTED_MESSAGE",
result
}));
return;
}
}
/**
* Sub-classes **must** override this to send %%message%% over their
* transport.
*/
async _write(message: string): Promise<void> {
throw new Error("sub-classes must override this");
}
}

View File

@@ -0,0 +1,103 @@
import { WebSocket as _WebSocket } from "./ws.js"; /*-browser*/
import { SocketProvider } from "./provider-socket.js";
import type { JsonRpcApiProviderOptions} from "./provider-jsonrpc.js";
import type { Networkish } from "./network.js";
/**
* A generic interface to a Websocket-like object.
*/
export interface WebSocketLike {
onopen: null | ((...args: Array<any>) => any);
onmessage: null | ((...args: Array<any>) => any);
onerror: null | ((...args: Array<any>) => any);
readyState: number;
send(payload: any): 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;
/**
* 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 {
#connect: null | WebSocketCreator;
#websocket: null | WebSocketLike;
get websocket(): WebSocketLike {
if (this.#websocket == null) { throw new Error("websocket closed"); }
return this.#websocket;
}
constructor(url: string | WebSocketLike | WebSocketCreator, network?: Networkish, options?: JsonRpcApiProviderOptions) {
super(network, options);
if (typeof(url) === "string") {
this.#connect = () => { return new _WebSocket(url); };
this.#websocket = this.#connect();
} else if (typeof(url) === "function") {
this.#connect = url;
this.#websocket = url();
} else {
this.#connect = null;
this.#websocket = url;
}
this.websocket.onopen = async () => {
try {
await this._start()
this.resume();
} catch (error) {
console.log("failed to start WebsocketProvider", error);
// @TODO: now what? Attempt reconnect?
}
};
this.websocket.onmessage = (message: { data: string }) => {
this._processMessage(message.data);
};
/*
this.websocket.onclose = (event) => {
// @TODO: What event.code should we reconnect on?
const reconnect = false;
if (reconnect) {
this.pause(true);
if (this.#connect) {
this.#websocket = this.#connect();
this.#websocket.onopen = ...
// @TODO: this requires the super class to rebroadcast; move it there
}
this._reconnect();
}
};
*/
}
async _write(message: string): Promise<void> {
this.websocket.send(message);
}
async destroy(): Promise<void> {
if (this.#websocket != null) {
this.#websocket.close();
this.#websocket = null;
}
super.destroy();
}
}

2146
dev/env/node_modules/ethers/src.ts/providers/provider.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,98 @@
import { defineProperties } from "../utils/index.js";
import { AbstractSigner } from "./abstract-signer.js";
import type { TypedDataDomain, TypedDataField } from "../hash/index.js";
import type {
BlockTag, Provider, TransactionRequest, TransactionResponse
} from "./provider.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 {
/**
* The Signer being managed.
*/
signer!: Signer;
#noncePromise: null | Promise<number>;
#delta: number;
/**
* Creates a new **NonceManager** to manage %%signer%%.
*/
constructor(signer: Signer) {
super(signer.provider);
defineProperties<NonceManager>(this, { signer });
this.#noncePromise = null;
this.#delta = 0;
}
async getAddress(): Promise<string> {
return this.signer.getAddress();
}
connect(provider: null | Provider): NonceManager {
return new NonceManager(this.signer.connect(provider));
}
async getNonce(blockTag?: BlockTag): Promise<number> {
if (blockTag === "pending") {
if (this.#noncePromise == null) {
this.#noncePromise = super.getNonce("pending");
}
const delta = this.#delta;
return (await this.#noncePromise) + delta;
}
return super.getNonce(blockTag);
}
/**
* Manually increment the nonce. This may be useful when managng
* offline transactions.
*/
increment(): void {
this.#delta++;
}
/**
* Resets the nonce, causing the **NonceManager** to reload the current
* nonce from the blockchain on the next transaction.
*/
reset(): void {
this.#delta = 0;
this.#noncePromise = null;
}
async sendTransaction(tx: TransactionRequest): Promise<TransactionResponse> {
const noncePromise = this.getNonce("pending");
this.increment();
tx = await this.signer.populateTransaction(tx);
tx.nonce = await noncePromise;
// @TODO: Maybe handle interesting/recoverable errors?
// Like don't increment if the tx was certainly not sent
return await this.signer.sendTransaction(tx);
}
signTransaction(tx: TransactionRequest): Promise<string> {
return this.signer.signTransaction(tx);
}
signMessage(message: string | Uint8Array): Promise<string> {
return this.signer.signMessage(message);
}
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string> {
return this.signer.signTypedData(domain, types, value);
}
}

166
dev/env/node_modules/ethers/src.ts/providers/signer.ts generated vendored Executable file
View File

@@ -0,0 +1,166 @@
import type { Addressable, NameResolver } from "../address/index.js";
import type {
AuthorizationRequest, TypedDataDomain, TypedDataField
} from "../hash/index.js";
import type { Authorization, TransactionLike } from "../transaction/index.js";
import type { ContractRunner } from "./contracts.js";
import type { BlockTag, Provider, TransactionRequest, TransactionResponse } from "./provider.js";
/**
* A Signer represents an account on the Ethereum Blockchain, and is most often
* backed by a private key represented by a mnemonic or residing on a Hardware Wallet.
*
* The API remains abstract though, so that it can deal with more advanced exotic
* Signing entities, such as Smart Contract Wallets or Virtual Wallets (where the
* private key may not be known).
*/
export interface Signer extends Addressable, ContractRunner, NameResolver {
/**
* The [[Provider]] attached to this Signer (if any).
*/
provider: null | Provider;
/**
* Returns a new instance of this Signer connected to //provider// or detached
* from any Provider if null.
*/
connect(provider: null | Provider): Signer;
////////////////////
// State
/**
* Get the address of the Signer.
*/
getAddress(): Promise<string>;
/**
* Gets the next nonce required for this Signer to send a transaction.
*
* @param blockTag - The blocktag to base the transaction count on, keep in mind
* many nodes do not honour this value and silently ignore it [default: ``"latest"``]
*/
getNonce(blockTag?: BlockTag): Promise<number>;
////////////////////
// Preparation
/**
* Prepares a {@link TransactionRequest} for calling:
* - resolves ``to`` and ``from`` addresses
* - if ``from`` is specified , check that it matches this Signer
*
* @param tx - The call to prepare
*/
populateCall(tx: TransactionRequest): Promise<TransactionLike<string>>;
/**
* Prepares a {@link TransactionRequest} for sending to the network by
* populating any missing properties:
* - resolves ``to`` and ``from`` addresses
* - if ``from`` is specified , check that it matches this Signer
* - populates ``nonce`` via ``signer.getNonce("pending")``
* - populates ``gasLimit`` via ``signer.estimateGas(tx)``
* - populates ``chainId`` via ``signer.provider.getNetwork()``
* - populates ``type`` and relevant fee data for that type (``gasPrice``
* for legacy transactions, ``maxFeePerGas`` for EIP-1559, etc)
*
* @note Some Signer implementations may skip populating properties that
* are populated downstream; for example JsonRpcSigner defers to the
* node to populate the nonce and fee data.
*
* @param tx - The call to prepare
*/
populateTransaction(tx: TransactionRequest): Promise<TransactionLike<string>>;
////////////////////
// Execution
/**
* Estimates the required gas required to execute //tx// on the Blockchain. This
* will be the expected amount a transaction will require as its ``gasLimit``
* to successfully run all the necessary computations and store the needed state
* that the transaction intends.
*
* Keep in mind that this is **best efforts**, since the state of the Blockchain
* is in flux, which could affect transaction gas requirements.
*
* @throws UNPREDICTABLE_GAS_LIMIT A transaction that is believed by the node to likely
* fail will throw an error during gas estimation. This could indicate that it
* will actually fail or that the circumstances are simply too complex for the
* node to take into account. In these cases, a manually determined ``gasLimit``
* will need to be made.
*/
estimateGas(tx: TransactionRequest): Promise<bigint>;
/**
* Evaluates the //tx// by running it against the current Blockchain state. This
* cannot change state and has no cost in ether, as it is effectively simulating
* execution.
*
* This can be used to have the Blockchain perform computations based on its state
* (e.g. running a Contract's getters) or to simulate the effect of a transaction
* before actually performing an operation.
*/
call(tx: TransactionRequest): Promise<string>;
/**
* Resolves an ENS Name to an address.
*/
resolveName(name: string): Promise<null | string>;
////////////////////
// Signing
/**
* Signs %%tx%%, returning the fully signed transaction. This does not
* populate any additional properties within the transaction.
*/
signTransaction(tx: TransactionRequest): Promise<string>;
/**
* Sends %%tx%% to the Network. The ``signer.populateTransaction(tx)``
* is called first to ensure all necessary properties for the
* transaction to be valid have been popualted first.
*/
sendTransaction(tx: TransactionRequest): Promise<TransactionResponse>;
/**
* Signs an [[link-eip-191]] prefixed personal message.
*
* If the %%message%% is a string, it is signed as UTF-8 encoded bytes. It is **not**
* interpretted as a [[BytesLike]]; so the string ``"0x1234"`` is signed as six
* characters, **not** two bytes.
*
* To sign that example as two bytes, the Uint8Array should be used
* (i.e. ``new Uint8Array([ 0x12, 0x34 ])``).
*/
signMessage(message: string | Uint8Array): Promise<string>;
/**
* Signs the [[link-eip-712]] typed data.
*/
signTypedData(domain: TypedDataDomain, types: Record<string, Array<TypedDataField>>, value: Record<string, any>): Promise<string>;
/**
* Prepares an [[AuthorizationRequest]] for authorization by
* populating any missing properties:
* - resolves ``address`` (if an Addressable or ENS name)
* - populates ``nonce`` via ``signer.getNonce("pending")``
* - populates ``chainId`` via ``signer.provider.getNetwork()``
*/
populateAuthorization(auth: AuthorizationRequest): Promise<AuthorizationRequest>;
/**
* Signs an %%authorization%% to be used in [[link-eip-7702]]
* transactions.
*/
authorize(authorization: AuthorizationRequest): Promise<Authorization>;
}

View File

@@ -0,0 +1,74 @@
import { getNumber } from "../utils/index.js";
import type { Subscriber } from "./abstract-provider.js";
//#TODO: Temp
import type { Provider } from "./provider.js";
/**
* @TODO
*
* @_docloc: api/providers/abstract-provider
*/
export interface ConnectionRpcProvider extends Provider {
//send(method: string, params: Array<any>): Promise<any>;
_subscribe(param: Array<any>, processFunc: (result: any) => void): number;
_unsubscribe(filterId: number): void;
}
/**
* @TODO
*
* @_docloc: api/providers/abstract-provider
*/
export class BlockConnectionSubscriber implements Subscriber {
#provider: ConnectionRpcProvider;
#blockNumber: number;
#running: boolean;
#filterId: null | number;
constructor(provider: ConnectionRpcProvider) {
this.#provider = provider;
this.#blockNumber = -2;
this.#running = false;
this.#filterId = null;
}
start(): void {
if (this.#running) { return; }
this.#running = true;
this.#filterId = this.#provider._subscribe([ "newHeads" ], (result: any) => {
const blockNumber = getNumber(result.number);
const initial = (this.#blockNumber === -2) ? blockNumber: (this.#blockNumber + 1)
for (let b = initial; b <= blockNumber; b++) {
this.#provider.emit("block", b);
}
this.#blockNumber = blockNumber;
});
}
stop(): void {
if (!this.#running) { return; }
this.#running = false;
if (this.#filterId != null) {
this.#provider._unsubscribe(this.#filterId);
this.#filterId = null;
}
}
pause(dropWhilePaused?: boolean): void {
if (dropWhilePaused) { this.#blockNumber = -2; }
this.stop();
}
resume(): void {
this.start();
}
}

View File

@@ -0,0 +1,199 @@
import { isError } from "../utils/index.js";
import { PollingEventSubscriber } from "./subscriber-polling.js";
import type { AbstractProvider, Subscriber } from "./abstract-provider.js";
import type { Network } from "./network.js";
import type { EventFilter } from "./provider.js";
import type { JsonRpcApiProvider } from "./provider-jsonrpc.js";
function copy(obj: any): any {
return JSON.parse(JSON.stringify(obj));
}
/**
* Some backends support subscribing to events using a Filter ID.
*
* When subscribing with this technique, the node issues a unique
* //Filter ID//. At this point the node dedicates resources to
* the filter, so that periodic calls to follow up on the //Filter ID//
* will receive any events since the last call.
*
* @_docloc: api/providers/abstract-provider
*/
export class FilterIdSubscriber implements Subscriber {
#provider: JsonRpcApiProvider;
#filterIdPromise: null | Promise<string>;
#poller: (b: number) => Promise<void>;
#running: boolean;
#network: null | Network;
#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) {
this.#provider = provider;
this.#filterIdPromise = null;
this.#poller = this.#poll.bind(this);
this.#running = false;
this.#network = null;
this.#hault = false;
}
/**
* Sub-classes **must** override this to begin the subscription.
*/
_subscribe(provider: JsonRpcApiProvider): Promise<string> {
throw new Error("subclasses must override this");
}
/**
* Sub-classes **must** override this handle the events.
*/
_emitResults(provider: AbstractProvider, result: Array<any>): Promise<void> {
throw new Error("subclasses must override this");
}
/**
* Sub-classes **must** override this handle recovery on errors.
*/
_recover(provider: AbstractProvider): Subscriber {
throw new Error("subclasses must override this");
}
async #poll(blockNumber: number): Promise<void> {
try {
// Subscribe if necessary
if (this.#filterIdPromise == null) {
this.#filterIdPromise = this._subscribe(this.#provider);
}
// Get the Filter ID
let filterId: null | string = null;
try {
filterId = await this.#filterIdPromise;
} catch (error) {
if (!isError(error, "UNSUPPORTED_OPERATION") || error.operation !== "eth_newFilter") {
throw error;
}
}
// The backend does not support Filter ID; downgrade to
// polling
if (filterId == null) {
this.#filterIdPromise = null;
this.#provider._recoverSubscriber(this, this._recover(this.#provider));
return;
}
const network = await this.#provider.getNetwork();
if (!this.#network) { this.#network = network; }
if ((this.#network as Network).chainId !== network.chainId) {
throw new Error("chaid changed");
}
if (this.#hault) { return; }
const result = await this.#provider.send("eth_getFilterChanges", [ filterId ]);
await this._emitResults(this.#provider, result);
} catch (error) { console.log("@TODO", error); }
this.#provider.once("block", this.#poller);
}
#teardown(): void {
const filterIdPromise = this.#filterIdPromise;
if (filterIdPromise) {
this.#filterIdPromise = null;
filterIdPromise.then((filterId) => {
if (this.#provider.destroyed) { return; }
this.#provider.send("eth_uninstallFilter", [ filterId ]);
});
}
}
start(): void {
if (this.#running) { return; }
this.#running = true;
this.#poll(-2);
}
stop(): void {
if (!this.#running) { return; }
this.#running = false;
this.#hault = true;
this.#teardown();
this.#provider.off("block", this.#poller);
}
pause(dropWhilePaused?: boolean): void {
if (dropWhilePaused){ this.#teardown(); }
this.#provider.off("block", this.#poller);
}
resume(): void { this.start(); }
}
/**
* A **FilterIdSubscriber** for receiving contract events.
*
* @_docloc: api/providers/abstract-provider
*/
export class FilterIdEventSubscriber extends FilterIdSubscriber {
#event: EventFilter;
/**
* Creates a new **FilterIdEventSubscriber** attached to %%provider%%
* listening for %%filter%%.
*/
constructor(provider: JsonRpcApiProvider, filter: EventFilter) {
super(provider);
this.#event = copy(filter);
}
_recover(provider: AbstractProvider): Subscriber {
return new PollingEventSubscriber(provider, this.#event);
}
async _subscribe(provider: JsonRpcApiProvider): Promise<string> {
const filterId = await provider.send("eth_newFilter", [ this.#event ]);
return filterId;
}
async _emitResults(provider: JsonRpcApiProvider, results: Array<any>): Promise<void> {
for (const result of results) {
provider.emit(this.#event, provider._wrapLog(result, provider._network));
}
}
}
/**
* A **FilterIdSubscriber** for receiving pending transactions events.
*
* @_docloc: api/providers/abstract-provider
*/
export class FilterIdPendingSubscriber extends FilterIdSubscriber {
async _subscribe(provider: JsonRpcApiProvider): Promise<string> {
return await provider.send("eth_newPendingTransactionFilter", [ ]);
}
async _emitResults(provider: JsonRpcApiProvider, results: Array<any>): Promise<void> {
for (const result of results) {
provider.emit("pending", result);
}
}
}

View File

@@ -0,0 +1,321 @@
import { assert, isHexString } from "../utils/index.js";
import type { AbstractProvider, Subscriber } from "./abstract-provider.js";
import type { EventFilter, OrphanFilter, ProviderEvent } from "./provider.js";
function copy(obj: any): any {
return JSON.parse(JSON.stringify(obj));
}
/**
* Return the polling subscriber for common events.
*
* @_docloc: api/providers/abstract-provider
*/
export function getPollingSubscriber(provider: AbstractProvider, event: ProviderEvent): Subscriber {
if (event === "block") { return new PollingBlockSubscriber(provider); }
if (isHexString(event, 32)) { return new PollingTransactionSubscriber(provider, event); }
assert(false, "unsupported polling event", "UNSUPPORTED_OPERATION", {
operation: "getPollingSubscriber", info: { event }
});
}
// @TODO: refactor this
/**
* A **PollingBlockSubscriber** polls at a regular interval for a change
* in the block number.
*
* @_docloc: api/providers/abstract-provider
*/
export class PollingBlockSubscriber implements Subscriber {
#provider: AbstractProvider;
#poller: null | number;
#interval: number;
// The most recent block we have scanned for events. The value -2
// indicates we still need to fetch an initial block number
#blockNumber: number;
/**
* Create a new **PollingBlockSubscriber** attached to %%provider%%.
*/
constructor(provider: AbstractProvider) {
this.#provider = provider;
this.#poller = null;
this.#interval = 4000;
this.#blockNumber = -2;
}
/**
* The polling interval.
*/
get pollingInterval(): number { return this.#interval; }
set pollingInterval(value: number) { this.#interval = value; }
async #poll(): Promise<void> {
try {
const blockNumber = await this.#provider.getBlockNumber();
// Bootstrap poll to setup our initial block number
if (this.#blockNumber === -2) {
this.#blockNumber = blockNumber;
return;
}
// @TODO: Put a cap on the maximum number of events per loop?
if (blockNumber !== this.#blockNumber) {
for (let b = this.#blockNumber + 1; b <= blockNumber; b++) {
// We have been stopped
if (this.#poller == null) { return; }
await this.#provider.emit("block", b);
}
this.#blockNumber = blockNumber;
}
} catch (error) {
// @TODO: Minor bump, add an "error" event to let subscribers
// know things went awry.
//console.log(error);
}
// We have been stopped
if (this.#poller == null) { return; }
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
}
start(): void {
if (this.#poller) { return; }
this.#poller = this.#provider._setTimeout(this.#poll.bind(this), this.#interval);
this.#poll();
}
stop(): void {
if (!this.#poller) { return; }
this.#provider._clearTimeout(this.#poller);
this.#poller = null;
}
pause(dropWhilePaused?: boolean): void {
this.stop();
if (dropWhilePaused) { this.#blockNumber = -2; }
}
resume(): void {
this.start();
}
}
/**
* An **OnBlockSubscriber** can be sub-classed, with a [[_poll]]
* implmentation which will be called on every new block.
*
* @_docloc: api/providers/abstract-provider
*/
export class OnBlockSubscriber implements Subscriber {
#provider: AbstractProvider;
#poll: (b: number) => void;
#running: boolean;
/**
* Create a new **OnBlockSubscriber** attached to %%provider%%.
*/
constructor(provider: AbstractProvider) {
this.#provider = provider;
this.#running = false;
this.#poll = (blockNumber: number) => {
this._poll(blockNumber, this.#provider);
}
}
/**
* Called on every new block.
*/
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
throw new Error("sub-classes must override this");
}
start(): void {
if (this.#running) { return; }
this.#running = true;
this.#poll(-2);
this.#provider.on("block", this.#poll);
}
stop(): void {
if (!this.#running) { return; }
this.#running = false;
this.#provider.off("block", this.#poll);
}
pause(dropWhilePaused?: boolean): void { this.stop(); }
resume(): void { this.start(); }
}
export class PollingBlockTagSubscriber extends OnBlockSubscriber {
readonly #tag: string;
#lastBlock: number;
constructor(provider: AbstractProvider, tag: string) {
super(provider);
this.#tag = tag;
this.#lastBlock = -2;
}
pause(dropWhilePaused?: boolean): void {
if (dropWhilePaused) { this.#lastBlock = -2; }
super.pause(dropWhilePaused);
}
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
const block = await provider.getBlock(this.#tag);
if (block == null) { return; }
if (this.#lastBlock === -2) {
this.#lastBlock = block.number;
} else if (block.number > this.#lastBlock) {
provider.emit(this.#tag, block.number);
this.#lastBlock = block.number;
}
}
}
/**
* @_ignore:
*
* @_docloc: api/providers/abstract-provider
*/
export class PollingOrphanSubscriber extends OnBlockSubscriber {
#filter: OrphanFilter;
constructor(provider: AbstractProvider, filter: OrphanFilter) {
super(provider);
this.#filter = copy(filter);
}
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
throw new Error("@TODO");
console.log(this.#filter);
}
}
/**
* A **PollingTransactionSubscriber** will poll for a given transaction
* hash for its receipt.
*
* @_docloc: api/providers/abstract-provider
*/
export class PollingTransactionSubscriber extends OnBlockSubscriber {
#hash: string;
/**
* Create a new **PollingTransactionSubscriber** attached to
* %%provider%%, listening for %%hash%%.
*/
constructor(provider: AbstractProvider, hash: string) {
super(provider);
this.#hash = hash;
}
async _poll(blockNumber: number, provider: AbstractProvider): Promise<void> {
const tx = await provider.getTransactionReceipt(this.#hash);
if (tx) { provider.emit(this.#hash, tx); }
}
}
/**
* A **PollingEventSubscriber** will poll for a given filter for its logs.
*
* @_docloc: api/providers/abstract-provider
*/
export class PollingEventSubscriber implements Subscriber {
#provider: AbstractProvider;
#filter: EventFilter;
#poller: (b: number) => void;
#running: boolean;
// The most recent block we have scanned for events. The value -2
// indicates we still need to fetch an initial block number
#blockNumber: number;
/**
* Create a new **PollingTransactionSubscriber** attached to
* %%provider%%, listening for %%filter%%.
*/
constructor(provider: AbstractProvider, filter: EventFilter) {
this.#provider = provider;
this.#filter = copy(filter);
this.#poller = this.#poll.bind(this);
this.#running = false;
this.#blockNumber = -2;
}
async #poll(blockNumber: number): Promise<void> {
// The initial block hasn't been determined yet
if (this.#blockNumber === -2) { return; }
const filter = copy(this.#filter);
filter.fromBlock = this.#blockNumber + 1;
filter.toBlock = blockNumber;
const logs = await this.#provider.getLogs(filter);
// No logs could just mean the node has not indexed them yet,
// so we keep a sliding window of 60 blocks to keep scanning
if (logs.length === 0) {
if (this.#blockNumber < blockNumber - 60) {
this.#blockNumber = blockNumber - 60;
}
return;
}
for (const log of logs) {
this.#provider.emit(this.#filter, log);
// Only advance the block number when logs were found to
// account for networks (like BNB and Polygon) which may
// sacrifice event consistency for block event speed
this.#blockNumber = log.blockNumber;
}
}
start(): void {
if (this.#running) { return; }
this.#running = true;
if (this.#blockNumber === -2) {
this.#provider.getBlockNumber().then((blockNumber) => {
this.#blockNumber = blockNumber;
});
}
this.#provider.on("block", this.#poller);
}
stop(): void {
if (!this.#running) { return; }
this.#running = false;
this.#provider.off("block", this.#poller);
}
pause(dropWhilePaused?: boolean): void {
this.stop();
if (dropWhilePaused) { this.#blockNumber = -2; }
}
resume(): void {
this.start();
}
}

11
dev/env/node_modules/ethers/src.ts/providers/ws-browser.ts generated vendored Executable file
View File

@@ -0,0 +1,11 @@
function getGlobal(): any {
if (typeof self !== 'undefined') { return self; }
if (typeof window !== 'undefined') { return window; }
if (typeof global !== 'undefined') { return global; }
throw new Error('unable to locate global object');
};
const _WebSocket = getGlobal().WebSocket;
export { _WebSocket as WebSocket };

3
dev/env/node_modules/ethers/src.ts/providers/ws.ts generated vendored Executable file
View File

@@ -0,0 +1,3 @@
export { WebSocket } from "ws";

16
dev/env/node_modules/ethers/src.ts/thirdparty.d.ts generated vendored Executable file
View File

@@ -0,0 +1,16 @@
declare module "ws" {
export class WebSocket {
constructor(...args: Array<any>);
onopen: null | ((...args: Array<any>) => any);
onmessage: null | ((...args: Array<any>) => any);
onerror: null | ((...args: Array<any>) => any);
readyState: number;
send(payload: any): void;
close(code?: number, reason?: string): void;
}
}

43
dev/env/node_modules/ethers/src.ts/transaction/accesslist.ts generated vendored Executable file
View File

@@ -0,0 +1,43 @@
import { getAddress } from "../address/index.js";
import { assertArgument, isHexString } from "../utils/index.js";
import type { AccessList, AccessListish } from "./index.js";
function accessSetify(addr: string, storageKeys: Array<string>): { address: string,storageKeys: Array<string> } {
return {
address: getAddress(addr),
storageKeys: storageKeys.map((storageKey, index) => {
assertArgument(isHexString(storageKey, 32), "invalid slot", `storageKeys[${ index }]`, storageKey);
return storageKey.toLowerCase();
})
};
}
/**
* Returns a [[AccessList]] from any ethers-supported access-list structure.
*/
export function accessListify(value: AccessListish): AccessList {
if (Array.isArray(value)) {
return (<Array<[ string, Array<string>] | { address: string, storageKeys: Array<string>}>>value).map((set, index) => {
if (Array.isArray(set)) {
assertArgument(set.length === 2, "invalid slot set", `value[${ index }]`, set);
return accessSetify(set[0], set[1])
}
assertArgument(set != null && typeof(set) === "object", "invalid address-slot set", "value", value);
return accessSetify(set.address, set.storageKeys);
});
}
assertArgument(value != null && typeof(value) === "object", "invalid access list", "value", value);
const result: Array<{ address: string, storageKeys: Array<string> }> = Object.keys(value).map((addr) => {
const storageKeys: Record<string, true> = value[addr].reduce((accum, storageKey) => {
accum[storageKey] = true;
return accum;
}, <Record<string, true>>{ });
return accessSetify(addr, Object.keys(storageKeys).sort())
});
result.sort((a, b) => (a.address.localeCompare(b.address)));
return result;
}

28
dev/env/node_modules/ethers/src.ts/transaction/address.ts generated vendored Executable file
View File

@@ -0,0 +1,28 @@
import { getAddress } from "../address/index.js";
import { keccak256, SigningKey } from "../crypto/index.js";
import type { SignatureLike } from "../crypto/index.js";
import type { BytesLike } from "../utils/index.js";
/**
* Returns the address for the %%key%%.
*
* The key may be any standard form of public key or a private key.
*/
export function computeAddress(key: string | SigningKey): string {
let pubkey: string;
if (typeof(key) === "string") {
pubkey = SigningKey.computePublicKey(key, false);
} else {
pubkey = key.publicKey;
}
return getAddress(keccak256("0x" + pubkey.substring(4)).substring(26));
}
/**
* Returns the recovered address for the private key that was
* used to sign %%digest%% that resulted in %%signature%%.
*/
export function recoverAddress(digest: BytesLike, signature: SignatureLike): string {
return computeAddress(SigningKey.recoverPublicKey(digest, signature));
}

View File

@@ -0,0 +1,14 @@
import { getAddress } from "../address/index.js";
import { Signature } from "../crypto/index.js";
import { getBigInt } from "../utils/index.js";
import type { Authorization, AuthorizationLike } from "./index.js";
export function authorizationify(auth: AuthorizationLike): Authorization {
return {
address: getAddress(auth.address),
nonce: getBigInt((auth.nonce != null) ? auth.nonce: 0),
chainId: getBigInt((auth.chainId != null)? auth.chainId: 0),
signature: Signature.from(auth.signature)
};
}

51
dev/env/node_modules/ethers/src.ts/transaction/index.ts generated vendored Executable file
View File

@@ -0,0 +1,51 @@
/**
* Each state-changing operation on Ethereum requires a transaction.
*
* @_section api/transaction:Transactions [about-transactions]
*/
null;
import type { BigNumberish } from "../utils/maths.js";
import type { Signature, SignatureLike } from "../crypto/index.js";
/**
* A single [[AccessList]] entry of storage keys (slots) for an address.
*/
export type AccessListEntry = { address: string, storageKeys: Array<string> };
/**
* An ordered collection of [[AccessList]] entries.
*/
export type AccessList = Array<AccessListEntry>;
/**
* Any ethers-supported access list structure.
*/
export type AccessListish = AccessList |
Array<[ string, Array<string> ]> |
Record<string, Array<string>>;
// Keep here?
export interface Authorization {
address: string;
nonce: bigint;
chainId: bigint;
signature: Signature;
}
export type AuthorizationLike = {
address: string;
nonce: BigNumberish;
chainId: BigNumberish;
signature: SignatureLike
};
export { accessListify } from "./accesslist.js";
export { authorizationify } from "./authorization.js";
export { computeAddress, recoverAddress } from "./address.js";
export { Transaction } from "./transaction.js";
export type {
Blob, BlobLike, KzgLibrary, KzgLibraryLike, TransactionLike
} from "./transaction.js";

1556
dev/env/node_modules/ethers/src.ts/transaction/transaction.ts generated vendored Executable file

File diff suppressed because it is too large Load Diff

73
dev/env/node_modules/ethers/src.ts/utils/base58.ts generated vendored Executable file
View File

@@ -0,0 +1,73 @@
/**
* The [Base58 Encoding](link-base58) scheme allows a **numeric** value
* to be encoded as a compact string using a radix of 58 using only
* alpha-numeric characters. Confusingly similar characters are omitted
* (i.e. ``"l0O"``).
*
* Note that Base58 encodes a **numeric** value, not arbitrary bytes,
* since any zero-bytes on the left would get removed. To mitigate this
* issue most schemes that use Base58 choose specific high-order values
* to ensure non-zero prefixes.
*
* @_subsection: api/utils:Base58 Encoding [about-base58]
*/
import { getBytes } from "./data.js";
import { assertArgument } from "./errors.js";
import { toBigInt } from "./maths.js";
import type { BytesLike } from "./index.js";
const Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
let Lookup: null | Record<string, bigint> = null;
function getAlpha(letter: string): bigint {
if (Lookup == null) {
Lookup = { };
for (let i = 0; i < Alphabet.length; i++) {
Lookup[Alphabet[i]] = BigInt(i);
}
}
const result = Lookup[letter];
assertArgument(result != null, `invalid base58 value`, "letter", letter);
return result;
}
const BN_0 = BigInt(0);
const BN_58 = BigInt(58);
/**
* Encode %%value%% as a Base58-encoded string.
*/
export function encodeBase58(_value: BytesLike): string {
const bytes = getBytes(_value);
let value = toBigInt(bytes);
let result = "";
while (value) {
result = Alphabet[Number(value % BN_58)] + result;
value /= BN_58;
}
// Account for leading padding zeros
for (let i = 0; i < bytes.length; i++) {
if (bytes[i]) { break; }
result = Alphabet[0] + result;
}
return result;
}
/**
* Decode the Base58-encoded %%value%%.
*/
export function decodeBase58(value: string): bigint {
let result = BN_0;
for (let i = 0; i < value.length; i++) {
result *= BN_58;
result += getAlpha(value[i]);
}
return result;
}

25
dev/env/node_modules/ethers/src.ts/utils/base64-browser.ts generated vendored Executable file
View File

@@ -0,0 +1,25 @@
// utils/base64-browser
import { getBytes } from "./data.js";
import type { BytesLike } from "./data.js";
export function decodeBase64(textData: string): Uint8Array {
textData = atob(textData);
const data = new Uint8Array(textData.length);
for (let i = 0; i < textData.length; i++) {
data[i] = textData.charCodeAt(i);
}
return getBytes(data);
}
export function encodeBase64(_data: BytesLike): string {
const data = getBytes(_data);
let textData = "";
for (let i = 0; i < data.length; i++) {
textData += String.fromCharCode(data[i]);
}
return btoa(textData);
}

56
dev/env/node_modules/ethers/src.ts/utils/base64.ts generated vendored Executable file
View File

@@ -0,0 +1,56 @@
/**
* [Base64 encoding](link-wiki-base64) using 6-bit words to encode
* arbitrary bytes into a string using 65 printable symbols, the
* upper-case and lower-case alphabet, the digits ``0`` through ``9``,
* ``"+"`` and ``"/"`` with the ``"="`` used for padding.
*
* @_subsection: api/utils:Base64 Encoding [about-base64]
*/
import { getBytes, getBytesCopy } from "./data.js";
import type { BytesLike } from "./data.js";
/**
* Decodes the base-64 encoded %%value%%.
*
* @example:
* // The decoded value is always binary data...
* result = decodeBase64("SGVsbG8gV29ybGQhIQ==")
* //_result:
*
* // ...use toUtf8String to convert it to a string.
* toUtf8String(result)
* //_result:
*
* // Decoding binary data
* decodeBase64("EjQ=")
* //_result:
*/
export function decodeBase64(value: string): Uint8Array {
return getBytesCopy(Buffer.from(value, "base64"));
};
/**
* Encodes %%data%% as a base-64 encoded string.
*
* @example:
* // Encoding binary data as a hexstring
* encodeBase64("0x1234")
* //_result:
*
* // Encoding binary data as a Uint8Array
* encodeBase64(new Uint8Array([ 0x12, 0x34 ]))
* //_result:
*
* // The input MUST be data...
* encodeBase64("Hello World!!")
* //_error:
*
* // ...use toUtf8Bytes for this.
* encodeBase64(toUtf8Bytes("Hello World!!"))
* //_result:
*/
export function encodeBase64(data: BytesLike): string {
return Buffer.from(getBytes(data)).toString("base64");
}

200
dev/env/node_modules/ethers/src.ts/utils/data.ts generated vendored Executable file
View File

@@ -0,0 +1,200 @@
/**
* Some data helpers.
*
*
* @_subsection api/utils:Data Helpers [about-data]
*/
import { assert, assertArgument } from "./errors.js";
/**
* A [[HexString]] whose length is even, which ensures it is a valid
* representation of binary data.
*/
export type DataHexString = string;
/**
* A string which is prefixed with ``0x`` and followed by any number
* of case-agnostic hexadecimal characters.
*
* It must match the regular expression ``/0x[0-9A-Fa-f]*\/``.
*/
export type HexString = string;
/**
* An object that can be used to represent binary data.
*/
export type BytesLike = DataHexString | Uint8Array;
function _getBytes(value: BytesLike, name?: string, copy?: boolean): Uint8Array {
if (value instanceof Uint8Array) {
if (copy) { return new Uint8Array(value); }
return value;
}
if (typeof(value) === "string" && (value.length % 2) === 0 &&
value.match(/^0x[0-9a-f]*$/i)) {
const result = new Uint8Array((value.length - 2) / 2);
let offset = 2;
for (let i = 0; i < result.length; i++) {
result[i] = parseInt(value.substring(offset, offset + 2), 16);
offset += 2;
}
return result;
}
assertArgument(false, "invalid BytesLike value", name || "value", value);
}
/**
* Get a typed Uint8Array for %%value%%. If already a Uint8Array
* the original %%value%% is returned; if a copy is required use
* [[getBytesCopy]].
*
* @see: getBytesCopy
*/
export function getBytes(value: BytesLike, name?: string): Uint8Array {
return _getBytes(value, name, false);
}
/**
* Get a typed Uint8Array for %%value%%, creating a copy if necessary
* to prevent any modifications of the returned value from being
* reflected elsewhere.
*
* @see: getBytes
*/
export function getBytesCopy(value: BytesLike, name?: string): Uint8Array {
return _getBytes(value, name, true);
}
/**
* Returns true if %%value%% is a valid [[HexString]].
*
* If %%length%% is ``true`` or a //number//, it also checks that
* %%value%% is a valid [[DataHexString]] of %%length%% (if a //number//)
* bytes of data (e.g. ``0x1234`` is 2 bytes).
*/
export function isHexString(value: any, length?: number | boolean): value is `0x${ string }` {
if (typeof(value) !== "string" || !value.match(/^0x[0-9A-Fa-f]*$/)) {
return false
}
if (typeof(length) === "number" && value.length !== 2 + 2 * length) { return false; }
if (length === true && (value.length % 2) !== 0) { return false; }
return true;
}
/**
* Returns true if %%value%% is a valid representation of arbitrary
* data (i.e. a valid [[DataHexString]] or a Uint8Array).
*/
export function isBytesLike(value: any): value is BytesLike {
return (isHexString(value, true) || (value instanceof Uint8Array));
}
const HexCharacters: string = "0123456789abcdef";
/**
* Returns a [[DataHexString]] representation of %%data%%.
*/
export function hexlify(data: BytesLike): string {
const bytes = getBytes(data);
let result = "0x";
for (let i = 0; i < bytes.length; i++) {
const v = bytes[i];
result += HexCharacters[(v & 0xf0) >> 4] + HexCharacters[v & 0x0f];
}
return result;
}
/**
* Returns a [[DataHexString]] by concatenating all values
* within %%data%%.
*/
export function concat(datas: ReadonlyArray<BytesLike>): string {
return "0x" + datas.map((d) => hexlify(d).substring(2)).join("");
}
/**
* Returns the length of %%data%%, in bytes.
*/
export function dataLength(data: BytesLike): number {
if (isHexString(data, true)) { return (data.length - 2) / 2; }
return getBytes(data).length;
}
/**
* Returns a [[DataHexString]] by slicing %%data%% from the %%start%%
* offset to the %%end%% offset.
*
* By default %%start%% is 0 and %%end%% is the length of %%data%%.
*/
export function dataSlice(data: BytesLike, start?: number, end?: number): string {
const bytes = getBytes(data);
if (end != null && end > bytes.length) {
assert(false, "cannot slice beyond data bounds", "BUFFER_OVERRUN", {
buffer: bytes, length: bytes.length, offset: end
});
}
return hexlify(bytes.slice((start == null) ? 0: start, (end == null) ? bytes.length: end));
}
/**
* Return the [[DataHexString]] result by stripping all **leading**
** zero bytes from %%data%%.
*/
export function stripZerosLeft(data: BytesLike): string {
let bytes = hexlify(data).substring(2);
while (bytes.startsWith("00")) { bytes = bytes.substring(2); }
return "0x" + bytes;
}
function zeroPad(data: BytesLike, length: number, left: boolean): string {
const bytes = getBytes(data);
assert(length >= bytes.length, "padding exceeds data length", "BUFFER_OVERRUN", {
buffer: new Uint8Array(bytes),
length: length,
offset: length + 1
});
const result = new Uint8Array(length);
result.fill(0);
if (left) {
result.set(bytes, length - bytes.length);
} else {
result.set(bytes, 0);
}
return hexlify(result);
}
/**
* Return the [[DataHexString]] of %%data%% padded on the **left**
* to %%length%% bytes.
*
* If %%data%% already exceeds %%length%%, a [[BufferOverrunError]] is
* thrown.
*
* This pads data the same as **values** are in Solidity
* (e.g. ``uint128``).
*/
export function zeroPadValue(data: BytesLike, length: number): string {
return zeroPad(data, length, true);
}
/**
* Return the [[DataHexString]] of %%data%% padded on the **right**
* to %%length%% bytes.
*
* If %%data%% already exceeds %%length%%, a [[BufferOverrunError]] is
* thrown.
*
* This pads data the same as **bytes** are in Solidity
* (e.g. ``bytes16``).
*/
export function zeroPadBytes(data: BytesLike, length: number): string {
return zeroPad(data, length, false);
}

Some files were not shown because too many files have changed in this diff Show More