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:
73
dev/env/node_modules/ethers/src.ts/utils/base58.ts
generated
vendored
Executable file
73
dev/env/node_modules/ethers/src.ts/utils/base58.ts
generated
vendored
Executable 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
25
dev/env/node_modules/ethers/src.ts/utils/base64-browser.ts
generated
vendored
Executable 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
56
dev/env/node_modules/ethers/src.ts/utils/base64.ts
generated
vendored
Executable 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
200
dev/env/node_modules/ethers/src.ts/utils/data.ts
generated
vendored
Executable 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);
|
||||
}
|
||||
797
dev/env/node_modules/ethers/src.ts/utils/errors.ts
generated
vendored
Executable file
797
dev/env/node_modules/ethers/src.ts/utils/errors.ts
generated
vendored
Executable file
@@ -0,0 +1,797 @@
|
||||
/**
|
||||
* All errors in ethers include properties to ensure they are both
|
||||
* human-readable (i.e. ``.message``) and machine-readable (i.e. ``.code``).
|
||||
*
|
||||
* The [[isError]] function can be used to check the error ``code`` and
|
||||
* provide a type guard for the properties present on that error interface.
|
||||
*
|
||||
* @_section: api/utils/errors:Errors [about-errors]
|
||||
*/
|
||||
|
||||
import { version } from "../_version.js";
|
||||
|
||||
import { defineProperties } from "./properties.js";
|
||||
|
||||
import type {
|
||||
TransactionRequest, TransactionReceipt, TransactionResponse
|
||||
} from "../providers/index.js";
|
||||
|
||||
import type { FetchRequest, FetchResponse } from "./fetch.js";
|
||||
|
||||
/**
|
||||
* An error may contain additional properties, but those must not
|
||||
* conflict with any implicit properties.
|
||||
*/
|
||||
export type ErrorInfo<T> = Omit<T, "code" | "name" | "message" | "shortMessage"> & { shortMessage?: string };
|
||||
|
||||
|
||||
function stringify(value: any, seen?: Set<any>): any {
|
||||
if (value == null) { return "null"; }
|
||||
|
||||
if (seen == null) { seen = new Set(); }
|
||||
if (typeof(value) === "object") {
|
||||
if (seen.has(value)) { return "[Circular]"; }
|
||||
seen.add(value);
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
return "[ " + (value.map((v) => stringify(v, seen))).join(", ") + " ]";
|
||||
}
|
||||
|
||||
if (value instanceof Uint8Array) {
|
||||
const HEX = "0123456789abcdef";
|
||||
let result = "0x";
|
||||
for (let i = 0; i < value.length; i++) {
|
||||
result += HEX[value[i] >> 4];
|
||||
result += HEX[value[i] & 0xf];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof(value) === "object" && typeof(value.toJSON) === "function") {
|
||||
return stringify(value.toJSON(), seen);
|
||||
}
|
||||
|
||||
switch (typeof(value)) {
|
||||
case "boolean": case "number": case "symbol":
|
||||
return value.toString();
|
||||
case "bigint":
|
||||
return BigInt(value).toString();
|
||||
case "string":
|
||||
return JSON.stringify(value);
|
||||
case "object": {
|
||||
const keys = Object.keys(value);
|
||||
keys.sort();
|
||||
return "{ " + keys.map((k) => `${ stringify(k, seen) }: ${ stringify(value[k], seen) }`).join(", ") + " }";
|
||||
}
|
||||
}
|
||||
|
||||
return `[ COULD NOT SERIALIZE ]`;
|
||||
}
|
||||
|
||||
/**
|
||||
* All errors emitted by ethers have an **ErrorCode** to help
|
||||
* identify and coalesce errors to simplify programmatic analysis.
|
||||
*
|
||||
* Each **ErrorCode** is the %%code%% proerty of a coresponding
|
||||
* [[EthersError]].
|
||||
*
|
||||
* **Generic Errors**
|
||||
*
|
||||
* **``"UNKNOWN_ERROR"``** - see [[UnknownError]]
|
||||
*
|
||||
* **``"NOT_IMPLEMENTED"``** - see [[NotImplementedError]]
|
||||
*
|
||||
* **``"UNSUPPORTED_OPERATION"``** - see [[UnsupportedOperationError]]
|
||||
*
|
||||
* **``"NETWORK_ERROR"``** - see [[NetworkError]]
|
||||
*
|
||||
* **``"SERVER_ERROR"``** - see [[ServerError]]
|
||||
*
|
||||
* **``"TIMEOUT"``** - see [[TimeoutError]]
|
||||
*
|
||||
* **``"BAD_DATA"``** - see [[BadDataError]]
|
||||
*
|
||||
* **``"CANCELLED"``** - see [[CancelledError]]
|
||||
*
|
||||
* **Operational Errors**
|
||||
*
|
||||
* **``"BUFFER_OVERRUN"``** - see [[BufferOverrunError]]
|
||||
*
|
||||
* **``"NUMERIC_FAULT"``** - see [[NumericFaultError]]
|
||||
*
|
||||
* **Argument Errors**
|
||||
*
|
||||
* **``"INVALID_ARGUMENT"``** - see [[InvalidArgumentError]]
|
||||
*
|
||||
* **``"MISSING_ARGUMENT"``** - see [[MissingArgumentError]]
|
||||
*
|
||||
* **``"UNEXPECTED_ARGUMENT"``** - see [[UnexpectedArgumentError]]
|
||||
*
|
||||
* **``"VALUE_MISMATCH"``** - //unused//
|
||||
*
|
||||
* **Blockchain Errors**
|
||||
*
|
||||
* **``"CALL_EXCEPTION"``** - see [[CallExceptionError]]
|
||||
*
|
||||
* **``"INSUFFICIENT_FUNDS"``** - see [[InsufficientFundsError]]
|
||||
*
|
||||
* **``"NONCE_EXPIRED"``** - see [[NonceExpiredError]]
|
||||
*
|
||||
* **``"REPLACEMENT_UNDERPRICED"``** - see [[ReplacementUnderpricedError]]
|
||||
*
|
||||
* **``"TRANSACTION_REPLACED"``** - see [[TransactionReplacedError]]
|
||||
*
|
||||
* **``"UNCONFIGURED_NAME"``** - see [[UnconfiguredNameError]]
|
||||
*
|
||||
* **``"OFFCHAIN_FAULT"``** - see [[OffchainFaultError]]
|
||||
*
|
||||
* **User Interaction Errors**
|
||||
*
|
||||
* **``"ACTION_REJECTED"``** - see [[ActionRejectedError]]
|
||||
*/
|
||||
export type ErrorCode =
|
||||
|
||||
// Generic Errors
|
||||
"UNKNOWN_ERROR" | "NOT_IMPLEMENTED" | "UNSUPPORTED_OPERATION" |
|
||||
"NETWORK_ERROR" | "SERVER_ERROR" | "TIMEOUT" | "BAD_DATA" |
|
||||
"CANCELLED" |
|
||||
|
||||
// Operational Errors
|
||||
"BUFFER_OVERRUN" | "NUMERIC_FAULT" |
|
||||
|
||||
// Argument Errors
|
||||
"INVALID_ARGUMENT" | "MISSING_ARGUMENT" | "UNEXPECTED_ARGUMENT" |
|
||||
"VALUE_MISMATCH" |
|
||||
|
||||
// Blockchain Errors
|
||||
"CALL_EXCEPTION" | "INSUFFICIENT_FUNDS" | "NONCE_EXPIRED" |
|
||||
"REPLACEMENT_UNDERPRICED" | "TRANSACTION_REPLACED" |
|
||||
"UNCONFIGURED_NAME" | "OFFCHAIN_FAULT" |
|
||||
|
||||
// User Interaction
|
||||
"ACTION_REJECTED"
|
||||
;
|
||||
|
||||
/**
|
||||
* All errors in Ethers include properties to assist in
|
||||
* machine-readable errors.
|
||||
*/
|
||||
export interface EthersError<T extends ErrorCode = ErrorCode> extends Error {
|
||||
/**
|
||||
* The string error code.
|
||||
*/
|
||||
code: ErrorCode;
|
||||
|
||||
/**
|
||||
* A short message describing the error, with minimal additional
|
||||
* details.
|
||||
*/
|
||||
shortMessage: string;
|
||||
|
||||
/**
|
||||
* Additional info regarding the error that may be useful.
|
||||
*
|
||||
* This is generally helpful mostly for human-based debugging.
|
||||
*/
|
||||
info?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* Any related error.
|
||||
*/
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
// Generic Errors
|
||||
|
||||
/**
|
||||
* This Error is a catch-all for when there is no way for Ethers to
|
||||
* know what the underlying problem is.
|
||||
*/
|
||||
export interface UnknownError extends EthersError<"UNKNOWN_ERROR"> {
|
||||
[ key: string ]: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error is mostly used as a stub for functionality that is
|
||||
* intended for the future, but is currently not implemented.
|
||||
*/
|
||||
export interface NotImplementedError extends EthersError<"NOT_IMPLEMENTED"> {
|
||||
/**
|
||||
* The attempted operation.
|
||||
*/
|
||||
operation: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates that the attempted operation is not supported.
|
||||
*
|
||||
* This could range from a specific JSON-RPC end-point not supporting
|
||||
* a feature to a specific configuration of an object prohibiting the
|
||||
* operation.
|
||||
*
|
||||
* For example, a [[Wallet]] with no connected [[Provider]] is unable
|
||||
* to send a transaction.
|
||||
*/
|
||||
export interface UnsupportedOperationError extends EthersError<"UNSUPPORTED_OPERATION"> {
|
||||
/**
|
||||
* The attempted operation.
|
||||
*/
|
||||
operation: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates a problem connecting to a network.
|
||||
*/
|
||||
export interface NetworkError extends EthersError<"NETWORK_ERROR"> {
|
||||
/**
|
||||
* The network event.
|
||||
*/
|
||||
event: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates there was a problem fetching a resource from
|
||||
* a server.
|
||||
*/
|
||||
export interface ServerError extends EthersError<"SERVER_ERROR"> {
|
||||
/**
|
||||
* The requested resource.
|
||||
*/
|
||||
request: FetchRequest | string;
|
||||
|
||||
/**
|
||||
* The response received from the server, if available.
|
||||
*/
|
||||
response?: FetchResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates that the timeout duration has expired and
|
||||
* that the operation has been implicitly cancelled.
|
||||
*
|
||||
* The side-effect of the operation may still occur, as this
|
||||
* generally means a request has been sent and there has simply
|
||||
* been no response to indicate whether it was processed or not.
|
||||
*/
|
||||
export interface TimeoutError extends EthersError<"TIMEOUT"> {
|
||||
/**
|
||||
* The attempted operation.
|
||||
*/
|
||||
operation: string;
|
||||
|
||||
/**
|
||||
* The reason.
|
||||
*/
|
||||
reason: string;
|
||||
|
||||
/**
|
||||
* The resource request, if available.
|
||||
*/
|
||||
request?: FetchRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates that a provided set of data cannot
|
||||
* be correctly interpreted.
|
||||
*/
|
||||
export interface BadDataError extends EthersError<"BAD_DATA"> {
|
||||
/**
|
||||
* The data.
|
||||
*/
|
||||
value: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates that the operation was cancelled by a
|
||||
* programmatic call, for example to ``cancel()``.
|
||||
*/
|
||||
export interface CancelledError extends EthersError<"CANCELLED"> {
|
||||
}
|
||||
|
||||
|
||||
// Operational Errors
|
||||
|
||||
/**
|
||||
* This Error indicates an attempt was made to read outside the bounds
|
||||
* of protected data.
|
||||
*
|
||||
* Most operations in Ethers are protected by bounds checks, to mitigate
|
||||
* exploits when parsing data.
|
||||
*/
|
||||
export interface BufferOverrunError extends EthersError<"BUFFER_OVERRUN"> {
|
||||
/**
|
||||
* The buffer that was overrun.
|
||||
*/
|
||||
buffer: Uint8Array;
|
||||
|
||||
/**
|
||||
* The length of the buffer.
|
||||
*/
|
||||
length: number;
|
||||
|
||||
/**
|
||||
* The offset that was requested.
|
||||
*/
|
||||
offset: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates an operation which would result in incorrect
|
||||
* arithmetic output has occurred.
|
||||
*
|
||||
* For example, trying to divide by zero or using a ``uint8`` to store
|
||||
* a negative value.
|
||||
*/
|
||||
export interface NumericFaultError extends EthersError<"NUMERIC_FAULT"> {
|
||||
/**
|
||||
* The attempted operation.
|
||||
*/
|
||||
operation: string;
|
||||
|
||||
/**
|
||||
* The fault reported.
|
||||
*/
|
||||
fault: string;
|
||||
|
||||
/**
|
||||
* The value the operation was attempted against.
|
||||
*/
|
||||
value: any;
|
||||
}
|
||||
|
||||
|
||||
// Argument Errors
|
||||
|
||||
/**
|
||||
* This Error indicates an incorrect type or value was passed to
|
||||
* a function or method.
|
||||
*/
|
||||
export interface InvalidArgumentError extends EthersError<"INVALID_ARGUMENT"> {
|
||||
/**
|
||||
* The name of the argument.
|
||||
*/
|
||||
argument: string;
|
||||
|
||||
/**
|
||||
* The value that was provided.
|
||||
*/
|
||||
value: any;
|
||||
|
||||
info?: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates there were too few arguments were provided.
|
||||
*/
|
||||
export interface MissingArgumentError extends EthersError<"MISSING_ARGUMENT"> {
|
||||
/**
|
||||
* The number of arguments received.
|
||||
*/
|
||||
count: number;
|
||||
|
||||
/**
|
||||
* The number of arguments expected.
|
||||
*/
|
||||
expectedCount: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates too many arguments were provided.
|
||||
*/
|
||||
export interface UnexpectedArgumentError extends EthersError<"UNEXPECTED_ARGUMENT"> {
|
||||
/**
|
||||
* The number of arguments received.
|
||||
*/
|
||||
count: number;
|
||||
|
||||
/**
|
||||
* The number of arguments expected.
|
||||
*/
|
||||
expectedCount: number;
|
||||
}
|
||||
|
||||
|
||||
// Blockchain Errors
|
||||
|
||||
/**
|
||||
* The action that resulted in the call exception.
|
||||
*/
|
||||
export type CallExceptionAction = "call" | "estimateGas" | "getTransactionResult" | "sendTransaction" | "unknown";
|
||||
|
||||
/**
|
||||
* The related transaction that caused the error.
|
||||
*/
|
||||
export type CallExceptionTransaction = {
|
||||
to: null | string;
|
||||
from?: string;
|
||||
data: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* This **Error** indicates a transaction reverted.
|
||||
*/
|
||||
export interface CallExceptionError extends EthersError<"CALL_EXCEPTION"> {
|
||||
|
||||
/**
|
||||
* The action being performed when the revert was encountered.
|
||||
*/
|
||||
action: CallExceptionAction;
|
||||
|
||||
/**
|
||||
* The revert data returned.
|
||||
*/
|
||||
data: null | string;
|
||||
|
||||
/**
|
||||
* A human-readable representation of data, if possible.
|
||||
*/
|
||||
reason: null | string;
|
||||
|
||||
/**
|
||||
* The transaction that triggered the exception.
|
||||
*/
|
||||
transaction: CallExceptionTransaction,
|
||||
|
||||
/**
|
||||
* The contract invocation details, if available.
|
||||
*/
|
||||
invocation: null | {
|
||||
method: string;
|
||||
signature: string;
|
||||
args: Array<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The built-in or custom revert error, if available
|
||||
*/
|
||||
revert: null | {
|
||||
signature: string;
|
||||
name: string;
|
||||
args: Array<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the error occurred in a transaction that was mined
|
||||
* (with a status of ``0``), this is the receipt.
|
||||
*/
|
||||
receipt?: TransactionReceipt; // @TODO: in v7, make this `null | TransactionReceipt`
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The sending account has insufficient funds to cover the
|
||||
* entire transaction cost.
|
||||
*/
|
||||
export interface InsufficientFundsError extends EthersError<"INSUFFICIENT_FUNDS"> {
|
||||
/**
|
||||
* The transaction.
|
||||
*/
|
||||
transaction: TransactionRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* The sending account has already used this nonce in a
|
||||
* transaction that has been included.
|
||||
*/
|
||||
export interface NonceExpiredError extends EthersError<"NONCE_EXPIRED"> {
|
||||
/**
|
||||
* The transaction.
|
||||
*/
|
||||
transaction: TransactionRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A CCIP-read exception, which cannot be recovered from or
|
||||
* be further processed.
|
||||
*/
|
||||
export interface OffchainFaultError extends EthersError<"OFFCHAIN_FAULT"> {
|
||||
/**
|
||||
* The transaction.
|
||||
*/
|
||||
transaction?: TransactionRequest;
|
||||
|
||||
/**
|
||||
* The reason the CCIP-read failed.
|
||||
*/
|
||||
reason: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An attempt was made to replace a transaction, but with an
|
||||
* insufficient additional fee to afford evicting the old
|
||||
* transaction from the memory pool.
|
||||
*/
|
||||
export interface ReplacementUnderpricedError extends EthersError<"REPLACEMENT_UNDERPRICED"> {
|
||||
/**
|
||||
* The transaction.
|
||||
*/
|
||||
transaction: TransactionRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* A pending transaction was replaced by another.
|
||||
*/
|
||||
export interface TransactionReplacedError extends EthersError<"TRANSACTION_REPLACED"> {
|
||||
/**
|
||||
* If the transaction was cancelled, such that the original
|
||||
* effects of the transaction cannot be assured.
|
||||
*/
|
||||
cancelled: boolean;
|
||||
|
||||
/**
|
||||
* The reason the transaction was replaced.
|
||||
*/
|
||||
reason: "repriced" | "cancelled" | "replaced";
|
||||
|
||||
/**
|
||||
* The hash of the replaced transaction.
|
||||
*/
|
||||
hash: string;
|
||||
|
||||
/**
|
||||
* The transaction that replaced the transaction.
|
||||
*/
|
||||
replacement: TransactionResponse;
|
||||
|
||||
/**
|
||||
* The receipt of the transaction that replace the transaction.
|
||||
*/
|
||||
receipt: TransactionReceipt;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates an ENS name was used, but the name has not
|
||||
* been configured.
|
||||
*
|
||||
* This could indicate an ENS name is unowned or that the current
|
||||
* address being pointed to is the [[ZeroAddress]].
|
||||
*/
|
||||
export interface UnconfiguredNameError extends EthersError<"UNCONFIGURED_NAME"> {
|
||||
/**
|
||||
* The ENS name that was requested
|
||||
*/
|
||||
value: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* This Error indicates a request was rejected by the user.
|
||||
*
|
||||
* In most clients (such as MetaMask), when an operation requires user
|
||||
* authorization (such as ``signer.sendTransaction``), the client
|
||||
* presents a dialog box to the user. If the user denies the request
|
||||
* this error is thrown.
|
||||
*/
|
||||
export interface ActionRejectedError extends EthersError<"ACTION_REJECTED"> {
|
||||
/**
|
||||
* The requested action.
|
||||
*/
|
||||
action: "requestAccess" | "sendTransaction" | "signMessage" | "signTransaction" | "signTypedData" | "unknown",
|
||||
|
||||
/**
|
||||
* The reason the action was rejected.
|
||||
*
|
||||
* If there is already a pending request, some clients may indicate
|
||||
* there is already a ``"pending"`` action. This prevents an app
|
||||
* from spamming the user.
|
||||
*/
|
||||
reason: "expired" | "rejected" | "pending"
|
||||
}
|
||||
|
||||
// Coding; converts an ErrorCode its Typed Error
|
||||
|
||||
/**
|
||||
* A conditional type that transforms the [[ErrorCode]] T into
|
||||
* its EthersError type.
|
||||
*
|
||||
* @flatworm-skip-docs
|
||||
*/
|
||||
export type CodedEthersError<T> =
|
||||
T extends "UNKNOWN_ERROR" ? UnknownError:
|
||||
T extends "NOT_IMPLEMENTED" ? NotImplementedError:
|
||||
T extends "UNSUPPORTED_OPERATION" ? UnsupportedOperationError:
|
||||
T extends "NETWORK_ERROR" ? NetworkError:
|
||||
T extends "SERVER_ERROR" ? ServerError:
|
||||
T extends "TIMEOUT" ? TimeoutError:
|
||||
T extends "BAD_DATA" ? BadDataError:
|
||||
T extends "CANCELLED" ? CancelledError:
|
||||
|
||||
T extends "BUFFER_OVERRUN" ? BufferOverrunError:
|
||||
T extends "NUMERIC_FAULT" ? NumericFaultError:
|
||||
|
||||
T extends "INVALID_ARGUMENT" ? InvalidArgumentError:
|
||||
T extends "MISSING_ARGUMENT" ? MissingArgumentError:
|
||||
T extends "UNEXPECTED_ARGUMENT" ? UnexpectedArgumentError:
|
||||
|
||||
T extends "CALL_EXCEPTION" ? CallExceptionError:
|
||||
T extends "INSUFFICIENT_FUNDS" ? InsufficientFundsError:
|
||||
T extends "NONCE_EXPIRED" ? NonceExpiredError:
|
||||
T extends "OFFCHAIN_FAULT" ? OffchainFaultError:
|
||||
T extends "REPLACEMENT_UNDERPRICED" ? ReplacementUnderpricedError:
|
||||
T extends "TRANSACTION_REPLACED" ? TransactionReplacedError:
|
||||
T extends "UNCONFIGURED_NAME" ? UnconfiguredNameError:
|
||||
|
||||
T extends "ACTION_REJECTED" ? ActionRejectedError:
|
||||
|
||||
never;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the %%error%% matches an error thrown by ethers
|
||||
* that matches the error %%code%%.
|
||||
*
|
||||
* In TypeScript environments, this can be used to check that %%error%%
|
||||
* matches an EthersError type, which means the expected properties will
|
||||
* be set.
|
||||
*
|
||||
* @See [ErrorCodes](api:ErrorCode)
|
||||
* @example
|
||||
* try {
|
||||
* // code....
|
||||
* } catch (e) {
|
||||
* if (isError(e, "CALL_EXCEPTION")) {
|
||||
* // The Type Guard has validated this object
|
||||
* console.log(e.data);
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
export function isError<K extends ErrorCode, T extends CodedEthersError<K>>(error: any, code: K): error is T {
|
||||
return (error && (<EthersError>error).code === code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if %%error%% is a [[CallExceptionError].
|
||||
*/
|
||||
export function isCallException(error: any): error is CallExceptionError {
|
||||
return isError(error, "CALL_EXCEPTION");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Error configured to the format ethers emits errors, with
|
||||
* the %%message%%, [[api:ErrorCode]] %%code%% and additional properties
|
||||
* for the corresponding EthersError.
|
||||
*
|
||||
* Each error in ethers includes the version of ethers, a
|
||||
* machine-readable [[ErrorCode]], and depending on %%code%%, additional
|
||||
* required properties. The error message will also include the %%message%%,
|
||||
* ethers version, %%code%% and all additional properties, serialized.
|
||||
*/
|
||||
export function makeError<K extends ErrorCode, T extends CodedEthersError<K>>(message: string, code: K, info?: ErrorInfo<T>): T {
|
||||
let shortMessage = message;
|
||||
|
||||
{
|
||||
const details: Array<string> = [];
|
||||
if (info) {
|
||||
if ("message" in info || "code" in info || "name" in info) {
|
||||
throw new Error(`value will overwrite populated values: ${ stringify(info) }`);
|
||||
}
|
||||
for (const key in info) {
|
||||
if (key === "shortMessage") { continue; }
|
||||
const value = <any>(info[<keyof ErrorInfo<T>>key]);
|
||||
// try {
|
||||
details.push(key + "=" + stringify(value));
|
||||
// } catch (error: any) {
|
||||
// console.log("MMM", error.message);
|
||||
// details.push(key + "=[could not serialize object]");
|
||||
// }
|
||||
}
|
||||
}
|
||||
details.push(`code=${ code }`);
|
||||
details.push(`version=${ version }`);
|
||||
|
||||
if (details.length) {
|
||||
message += " (" + details.join(", ") + ")";
|
||||
}
|
||||
}
|
||||
|
||||
let error;
|
||||
switch (code) {
|
||||
case "INVALID_ARGUMENT":
|
||||
error = new TypeError(message);
|
||||
break;
|
||||
case "NUMERIC_FAULT":
|
||||
case "BUFFER_OVERRUN":
|
||||
error = new RangeError(message);
|
||||
break;
|
||||
default:
|
||||
error = new Error(message);
|
||||
}
|
||||
|
||||
defineProperties<EthersError>(<EthersError>error, { code });
|
||||
|
||||
if (info) { Object.assign(error, info); }
|
||||
|
||||
if ((<any>error).shortMessage == null) {
|
||||
defineProperties<EthersError>(<EthersError>error, { shortMessage });
|
||||
}
|
||||
|
||||
return <T>error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an EthersError with %%message%%, %%code%% and additional error
|
||||
* %%info%% when %%check%% is falsish..
|
||||
*
|
||||
* @see [[api:makeError]]
|
||||
*/
|
||||
export function assert<K extends ErrorCode, T extends CodedEthersError<K>>(check: unknown, message: string, code: K, info?: ErrorInfo<T>): asserts check {
|
||||
if (!check) { throw makeError(message, code, info); }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A simple helper to simply ensuring provided arguments match expected
|
||||
* constraints, throwing if not.
|
||||
*
|
||||
* In TypeScript environments, the %%check%% has been asserted true, so
|
||||
* any further code does not need additional compile-time checks.
|
||||
*/
|
||||
export function assertArgument(check: unknown, message: string, name: string, value: unknown): asserts check {
|
||||
assert(check, message, "INVALID_ARGUMENT", { argument: name, value: value });
|
||||
}
|
||||
|
||||
export function assertArgumentCount(count: number, expectedCount: number, message?: string): void {
|
||||
if (message == null) { message = ""; }
|
||||
if (message) { message = ": " + message; }
|
||||
|
||||
assert(count >= expectedCount, "missing argument" + message, "MISSING_ARGUMENT", {
|
||||
count: count,
|
||||
expectedCount: expectedCount
|
||||
});
|
||||
|
||||
assert(count <= expectedCount, "too many arguments" + message, "UNEXPECTED_ARGUMENT", {
|
||||
count: count,
|
||||
expectedCount: expectedCount
|
||||
});
|
||||
}
|
||||
|
||||
const _normalizeForms = ["NFD", "NFC", "NFKD", "NFKC"].reduce((accum, form) => {
|
||||
try {
|
||||
// General test for normalize
|
||||
/* c8 ignore start */
|
||||
if ("test".normalize(form) !== "test") { throw new Error("bad"); };
|
||||
/* c8 ignore stop */
|
||||
|
||||
if (form === "NFD") {
|
||||
const check = String.fromCharCode(0xe9).normalize("NFD");
|
||||
const expected = String.fromCharCode(0x65, 0x0301)
|
||||
/* c8 ignore start */
|
||||
if (check !== expected) { throw new Error("broken") }
|
||||
/* c8 ignore stop */
|
||||
}
|
||||
|
||||
accum.push(form);
|
||||
} catch(error) { }
|
||||
|
||||
return accum;
|
||||
}, <Array<string>>[]);
|
||||
|
||||
/**
|
||||
* Throws if the normalization %%form%% is not supported.
|
||||
*/
|
||||
export function assertNormalize(form: string): void {
|
||||
assert(_normalizeForms.indexOf(form) >= 0, "platform missing String.prototype.normalize", "UNSUPPORTED_OPERATION", {
|
||||
operation: "String.prototype.normalize", info: { form }
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Many classes use file-scoped values to guard the constructor,
|
||||
* making it effectively private. This facilitates that pattern
|
||||
* by ensuring the %%givenGaurd%% matches the file-scoped %%guard%%,
|
||||
* throwing if not, indicating the %%className%% if provided.
|
||||
*/
|
||||
export function assertPrivate(givenGuard: any, guard: any, className?: string): void {
|
||||
if (className == null) { className = ""; }
|
||||
if (givenGuard !== guard) {
|
||||
let method = className, operation = "new";
|
||||
if (className) {
|
||||
method += ".";
|
||||
operation += " " + className;
|
||||
}
|
||||
assert(false, `private constructor; use ${ method }from* methods`, "UNSUPPORTED_OPERATION", {
|
||||
operation
|
||||
});
|
||||
}
|
||||
}
|
||||
105
dev/env/node_modules/ethers/src.ts/utils/events.ts
generated
vendored
Executable file
105
dev/env/node_modules/ethers/src.ts/utils/events.ts
generated
vendored
Executable file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Events allow for applications to use the observer pattern, which
|
||||
* allows subscribing and publishing events, outside the normal
|
||||
* execution paths.
|
||||
*
|
||||
* @_section api/utils/events:Events [about-events]
|
||||
*/
|
||||
import { defineProperties } from "./properties.js";
|
||||
|
||||
/**
|
||||
* A callback function called when a an event is triggered.
|
||||
*/
|
||||
export type Listener = (...args: Array<any>) => void;
|
||||
|
||||
/**
|
||||
* An **EventEmitterable** behaves similar to an EventEmitter
|
||||
* except provides async access to its methods.
|
||||
*
|
||||
* An EventEmitter implements the observer pattern.
|
||||
*/
|
||||
export interface EventEmitterable<T> {
|
||||
/**
|
||||
* Registers a %%listener%% that is called whenever the
|
||||
* %%event%% occurs until unregistered.
|
||||
*/
|
||||
on(event: T, listener: Listener): Promise<this>;
|
||||
|
||||
/**
|
||||
* Registers a %%listener%% that is called the next time
|
||||
* %%event%% occurs.
|
||||
*/
|
||||
once(event: T, listener: Listener): Promise<this>;
|
||||
|
||||
/**
|
||||
* Triggers each listener for %%event%% with the %%args%%.
|
||||
*/
|
||||
emit(event: T, ...args: Array<any>): Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Resolves to the number of listeners for %%event%%.
|
||||
*/
|
||||
listenerCount(event?: T): Promise<number>;
|
||||
|
||||
/**
|
||||
* Resolves to the listeners for %%event%%.
|
||||
*/
|
||||
listeners(event?: T): Promise<Array<Listener>>;
|
||||
|
||||
/**
|
||||
* Unregister the %%listener%% for %%event%%. If %%listener%%
|
||||
* is unspecified, all listeners are unregistered.
|
||||
*/
|
||||
off(event: T, listener?: Listener): Promise<this>;
|
||||
|
||||
/**
|
||||
* Unregister all listeners for %%event%%.
|
||||
*/
|
||||
removeAllListeners(event?: T): Promise<this>;
|
||||
|
||||
/**
|
||||
* Alias for [[on]].
|
||||
*/
|
||||
addListener(event: T, listener: Listener): Promise<this>;
|
||||
|
||||
/**
|
||||
* Alias for [[off]].
|
||||
*/
|
||||
removeListener(event: T, listener: Listener): Promise<this>;
|
||||
}
|
||||
|
||||
/**
|
||||
* When an [[EventEmitterable]] triggers a [[Listener]], the
|
||||
* callback always ahas one additional argument passed, which is
|
||||
* an **EventPayload**.
|
||||
*/
|
||||
export class EventPayload<T> {
|
||||
/**
|
||||
* The event filter.
|
||||
*/
|
||||
readonly filter!: T;
|
||||
|
||||
/**
|
||||
* The **EventEmitterable**.
|
||||
*/
|
||||
readonly emitter!: EventEmitterable<T>;
|
||||
|
||||
readonly #listener: null | Listener;
|
||||
|
||||
/**
|
||||
* Create a new **EventPayload** for %%emitter%% with
|
||||
* the %%listener%% and for %%filter%%.
|
||||
*/
|
||||
constructor(emitter: EventEmitterable<T>, listener: null | Listener, filter: T) {
|
||||
this.#listener = listener;
|
||||
defineProperties<EventPayload<any>>(this, { emitter, filter });
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister the triggered listener for future events.
|
||||
*/
|
||||
async removeListener(): Promise<void> {
|
||||
if (this.#listener == null) { return; }
|
||||
await this.emitter.off(this.filter, this.#listener);
|
||||
}
|
||||
}
|
||||
970
dev/env/node_modules/ethers/src.ts/utils/fetch.ts
generated
vendored
Executable file
970
dev/env/node_modules/ethers/src.ts/utils/fetch.ts
generated
vendored
Executable file
@@ -0,0 +1,970 @@
|
||||
/**
|
||||
* Fetching content from the web is environment-specific, so Ethers
|
||||
* provides an abstraction that each environment can implement to provide
|
||||
* this service.
|
||||
*
|
||||
* On [Node.js](link-node), the ``http`` and ``https`` libs are used to
|
||||
* create a request object, register event listeners and process data
|
||||
* and populate the [[FetchResponse]].
|
||||
*
|
||||
* In a browser, the [DOM fetch](link-js-fetch) is used, and the resulting
|
||||
* ``Promise`` is waited on to retrieve the payload.
|
||||
*
|
||||
* The [[FetchRequest]] is responsible for handling many common situations,
|
||||
* such as redirects, server throttling, authentication, etc.
|
||||
*
|
||||
* It also handles common gateways, such as IPFS and data URIs.
|
||||
*
|
||||
* @_section api/utils/fetching:Fetching Web Content [about-fetch]
|
||||
*/
|
||||
import { decodeBase64, encodeBase64 } from "./base64.js";
|
||||
import { hexlify } from "./data.js";
|
||||
import { assert, assertArgument } from "./errors.js";
|
||||
import { defineProperties } from "./properties.js";
|
||||
import { toUtf8Bytes, toUtf8String } from "./utf8.js";
|
||||
|
||||
import { createGetUrl } from "./geturl.js";
|
||||
|
||||
/**
|
||||
* An environment's implementation of ``getUrl`` must return this type.
|
||||
*/
|
||||
export type GetUrlResponse = {
|
||||
statusCode: number,
|
||||
statusMessage: string,
|
||||
headers: Record<string, string>,
|
||||
body: null | Uint8Array
|
||||
};
|
||||
|
||||
/**
|
||||
* This can be used to control how throttling is handled in
|
||||
* [[FetchRequest-setThrottleParams]].
|
||||
*/
|
||||
export type FetchThrottleParams = {
|
||||
maxAttempts?: number;
|
||||
slotInterval?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called before any network request, allowing updated headers (e.g. Bearer tokens), etc.
|
||||
*/
|
||||
export type FetchPreflightFunc = (req: FetchRequest) => Promise<FetchRequest>;
|
||||
|
||||
/**
|
||||
* Called on the response, allowing client-based throttling logic or post-processing.
|
||||
*/
|
||||
export type FetchProcessFunc = (req: FetchRequest, resp: FetchResponse) => Promise<FetchResponse>;
|
||||
|
||||
/**
|
||||
* Called prior to each retry; return true to retry, false to abort.
|
||||
*/
|
||||
export type FetchRetryFunc = (req: FetchRequest, resp: FetchResponse, attempt: number) => Promise<boolean>;
|
||||
|
||||
/**
|
||||
* Called on Gateway URLs.
|
||||
*/
|
||||
export type FetchGatewayFunc = (url: string, signal?: FetchCancelSignal) => Promise<FetchRequest | FetchResponse>;
|
||||
|
||||
/**
|
||||
* Used to perform a fetch; use this to override the underlying network
|
||||
* fetch layer. In NodeJS, the default uses the "http" and "https" libraries
|
||||
* and in the browser ``fetch`` is used. If you wish to use Axios, this is
|
||||
* how you would register it.
|
||||
*/
|
||||
export type FetchGetUrlFunc = (req: FetchRequest, signal?: FetchCancelSignal) => Promise<GetUrlResponse>;
|
||||
|
||||
|
||||
const MAX_ATTEMPTS = 12;
|
||||
const SLOT_INTERVAL = 250;
|
||||
|
||||
// The global FetchGetUrlFunc implementation.
|
||||
let defaultGetUrlFunc: FetchGetUrlFunc = createGetUrl();
|
||||
|
||||
const reData = new RegExp("^data:([^;:]*)?(;base64)?,(.*)$", "i");
|
||||
const reIpfs = new RegExp("^ipfs:/\/(ipfs/)?(.*)$", "i");
|
||||
|
||||
// If locked, new Gateways cannot be added
|
||||
let locked = false;
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
|
||||
async function dataGatewayFunc(url: string, signal?: FetchCancelSignal): Promise<FetchResponse> {
|
||||
try {
|
||||
const match = url.match(reData);
|
||||
if (!match) { throw new Error("invalid data"); }
|
||||
return new FetchResponse(200, "OK", {
|
||||
"content-type": (match[1] || "text/plain"),
|
||||
}, (match[2] ? decodeBase64(match[3]): unpercent(match[3])));
|
||||
} catch (error) {
|
||||
return new FetchResponse(599, "BAD REQUEST (invalid data: URI)", { }, null, new FetchRequest(url));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[FetchGatewayFunc]] for fetching content from a standard
|
||||
* IPFS gateway hosted at %%baseUrl%%.
|
||||
*/
|
||||
function getIpfsGatewayFunc(baseUrl: string): FetchGatewayFunc {
|
||||
async function gatewayIpfs(url: string, signal?: FetchCancelSignal): Promise<FetchRequest | FetchResponse> {
|
||||
try {
|
||||
const match = url.match(reIpfs);
|
||||
if (!match) { throw new Error("invalid link"); }
|
||||
return new FetchRequest(`${ baseUrl }${ match[2] }`);
|
||||
} catch (error) {
|
||||
return new FetchResponse(599, "BAD REQUEST (invalid IPFS URI)", { }, null, new FetchRequest(url));
|
||||
}
|
||||
}
|
||||
|
||||
return gatewayIpfs;
|
||||
}
|
||||
|
||||
const Gateways: Record<string, FetchGatewayFunc> = {
|
||||
"data": dataGatewayFunc,
|
||||
"ipfs": getIpfsGatewayFunc("https:/\/gateway.ipfs.io/ipfs/")
|
||||
};
|
||||
|
||||
const fetchSignals: WeakMap<FetchRequest, () => void> = new WeakMap();
|
||||
|
||||
/**
|
||||
* @_ignore
|
||||
*/
|
||||
export class FetchCancelSignal {
|
||||
#listeners: Array<() => void>;
|
||||
#cancelled: boolean;
|
||||
|
||||
constructor(request: FetchRequest) {
|
||||
this.#listeners = [ ];
|
||||
this.#cancelled = false;
|
||||
|
||||
fetchSignals.set(request, () => {
|
||||
if (this.#cancelled) { return; }
|
||||
this.#cancelled = true;
|
||||
|
||||
for (const listener of this.#listeners) {
|
||||
setTimeout(() => { listener(); }, 0);
|
||||
}
|
||||
this.#listeners = [ ];
|
||||
});
|
||||
}
|
||||
|
||||
addListener(listener: () => void): void {
|
||||
assert(!this.#cancelled, "singal already cancelled", "UNSUPPORTED_OPERATION", {
|
||||
operation: "fetchCancelSignal.addCancelListener"
|
||||
});
|
||||
this.#listeners.push(listener);
|
||||
}
|
||||
|
||||
get cancelled(): boolean { return this.#cancelled; }
|
||||
|
||||
checkSignal(): void {
|
||||
assert(!this.cancelled, "cancelled", "CANCELLED", { });
|
||||
}
|
||||
}
|
||||
|
||||
// Check the signal, throwing if it is cancelled
|
||||
function checkSignal(signal?: FetchCancelSignal): FetchCancelSignal {
|
||||
if (signal == null) { throw new Error("missing signal; should not happen"); }
|
||||
signal.checkSignal();
|
||||
return signal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a request for a resource using a URI.
|
||||
*
|
||||
* By default, the supported schemes are ``HTTP``, ``HTTPS``, ``data:``,
|
||||
* and ``IPFS:``.
|
||||
*
|
||||
* Additional schemes can be added globally using [[registerGateway]].
|
||||
*
|
||||
* @example:
|
||||
* req = new FetchRequest("https://www.ricmoo.com")
|
||||
* resp = await req.send()
|
||||
* resp.body.length
|
||||
* //_result:
|
||||
*/
|
||||
export class FetchRequest implements Iterable<[ key: string, value: string ]> {
|
||||
#allowInsecure: boolean;
|
||||
#gzip: boolean;
|
||||
#headers: Record<string, string>;
|
||||
#method: string;
|
||||
#timeout: number;
|
||||
#url: string;
|
||||
|
||||
#body?: Uint8Array;
|
||||
#bodyType?: string;
|
||||
#creds?: string;
|
||||
|
||||
// Hooks
|
||||
#preflight?: null | FetchPreflightFunc;
|
||||
#process?: null | FetchProcessFunc;
|
||||
#retry?: null | FetchRetryFunc;
|
||||
|
||||
#signal?: FetchCancelSignal;
|
||||
|
||||
#throttle: Required<FetchThrottleParams>;
|
||||
|
||||
#getUrlFunc: null | FetchGetUrlFunc;
|
||||
|
||||
/**
|
||||
* The fetch URL to request.
|
||||
*/
|
||||
get url(): string { return this.#url; }
|
||||
set url(url: string) {
|
||||
this.#url = String(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* The fetch body, if any, to send as the request body. //(default: null)//
|
||||
*
|
||||
* When setting a body, the intrinsic ``Content-Type`` is automatically
|
||||
* set and will be used if **not overridden** by setting a custom
|
||||
* header.
|
||||
*
|
||||
* If %%body%% is null, the body is cleared (along with the
|
||||
* intrinsic ``Content-Type``).
|
||||
*
|
||||
* If %%body%% is a string, the intrinsic ``Content-Type`` is set to
|
||||
* ``text/plain``.
|
||||
*
|
||||
* If %%body%% is a Uint8Array, the intrinsic ``Content-Type`` is set to
|
||||
* ``application/octet-stream``.
|
||||
*
|
||||
* If %%body%% is any other object, the intrinsic ``Content-Type`` is
|
||||
* set to ``application/json``.
|
||||
*/
|
||||
get body(): null | Uint8Array {
|
||||
if (this.#body == null) { return null; }
|
||||
return new Uint8Array(this.#body);
|
||||
}
|
||||
set body(body: null | string | Readonly<object> | Readonly<Uint8Array>) {
|
||||
if (body == null) {
|
||||
this.#body = undefined;
|
||||
this.#bodyType = undefined;
|
||||
} else if (typeof(body) === "string") {
|
||||
this.#body = toUtf8Bytes(body);
|
||||
this.#bodyType = "text/plain";
|
||||
} else if (body instanceof Uint8Array) {
|
||||
this.#body = body;
|
||||
this.#bodyType = "application/octet-stream";
|
||||
} else if (typeof(body) === "object") {
|
||||
this.#body = toUtf8Bytes(JSON.stringify(body));
|
||||
this.#bodyType = "application/json";
|
||||
} else {
|
||||
throw new Error("invalid body");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the request has a body.
|
||||
*/
|
||||
hasBody(): this is (FetchRequest & { body: Uint8Array }) {
|
||||
return (this.#body != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The HTTP method to use when requesting the URI. If no method
|
||||
* has been explicitly set, then ``GET`` is used if the body is
|
||||
* null and ``POST`` otherwise.
|
||||
*/
|
||||
get method(): string {
|
||||
if (this.#method) { return this.#method; }
|
||||
if (this.hasBody()) { return "POST"; }
|
||||
return "GET";
|
||||
}
|
||||
set method(method: null | string) {
|
||||
if (method == null) { method = ""; }
|
||||
this.#method = String(method).toUpperCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* The headers that will be used when requesting the URI. All
|
||||
* keys are lower-case.
|
||||
*
|
||||
* This object is a copy, so any changes will **NOT** be reflected
|
||||
* in the ``FetchRequest``.
|
||||
*
|
||||
* To set a header entry, use the ``setHeader`` method.
|
||||
*/
|
||||
get headers(): Record<string, string> {
|
||||
const headers = Object.assign({ }, this.#headers);
|
||||
|
||||
if (this.#creds) {
|
||||
headers["authorization"] = `Basic ${ encodeBase64(toUtf8Bytes(this.#creds)) }`;
|
||||
};
|
||||
|
||||
if (this.allowGzip) {
|
||||
headers["accept-encoding"] = "gzip";
|
||||
}
|
||||
|
||||
if (headers["content-type"] == null && this.#bodyType) {
|
||||
headers["content-type"] = this.#bodyType;
|
||||
}
|
||||
if (this.body) { headers["content-length"] = String(this.body.length); }
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header for %%key%%, ignoring case.
|
||||
*/
|
||||
getHeader(key: string): string {
|
||||
return this.headers[key.toLowerCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the header for %%key%% to %%value%%. All values are coerced
|
||||
* to a string.
|
||||
*/
|
||||
setHeader(key: string, value: string | number): void {
|
||||
this.#headers[String(key).toLowerCase()] = String(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all headers, resetting all intrinsic headers.
|
||||
*/
|
||||
clearHeaders(): void {
|
||||
this.#headers = { };
|
||||
}
|
||||
|
||||
[Symbol.iterator](): Iterator<[ key: string, value: string ]> {
|
||||
const headers = this.headers;
|
||||
const keys = Object.keys(headers);
|
||||
let index = 0;
|
||||
return {
|
||||
next: () => {
|
||||
if (index < keys.length) {
|
||||
const key = keys[index++];
|
||||
return {
|
||||
value: [ key, headers[key] ], done: false
|
||||
}
|
||||
}
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The value that will be sent for the ``Authorization`` header.
|
||||
*
|
||||
* To set the credentials, use the ``setCredentials`` method.
|
||||
*/
|
||||
get credentials(): null | string {
|
||||
return this.#creds || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an ``Authorization`` for %%username%% with %%password%%.
|
||||
*/
|
||||
setCredentials(username: string, password: string): void {
|
||||
assertArgument(!username.match(/:/), "invalid basic authentication username", "username", "[REDACTED]");
|
||||
this.#creds = `${ username }:${ password }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable and request gzip-encoded responses. The response will
|
||||
* automatically be decompressed. //(default: true)//
|
||||
*/
|
||||
get allowGzip(): boolean {
|
||||
return this.#gzip;
|
||||
}
|
||||
set allowGzip(value: boolean) {
|
||||
this.#gzip = !!value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow ``Authentication`` credentials to be sent over insecure
|
||||
* channels. //(default: false)//
|
||||
*/
|
||||
get allowInsecureAuthentication(): boolean {
|
||||
return !!this.#allowInsecure;
|
||||
}
|
||||
set allowInsecureAuthentication(value: boolean) {
|
||||
this.#allowInsecure = !!value;
|
||||
}
|
||||
|
||||
/**
|
||||
* The timeout (in milliseconds) to wait for a complete response.
|
||||
* //(default: 5 minutes)//
|
||||
*/
|
||||
get timeout(): number { return this.#timeout; }
|
||||
set timeout(timeout: number) {
|
||||
assertArgument(timeout >= 0, "timeout must be non-zero", "timeout", timeout);
|
||||
this.#timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called prior to each request, for example
|
||||
* during a redirection or retry in case of server throttling.
|
||||
*
|
||||
* This offers an opportunity to populate headers or update
|
||||
* content before sending a request.
|
||||
*/
|
||||
get preflightFunc(): null | FetchPreflightFunc {
|
||||
return this.#preflight || null;
|
||||
}
|
||||
set preflightFunc(preflight: null | FetchPreflightFunc) {
|
||||
this.#preflight = preflight;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called after each response, offering an
|
||||
* opportunity to provide client-level throttling or updating
|
||||
* response data.
|
||||
*
|
||||
* Any error thrown in this causes the ``send()`` to throw.
|
||||
*
|
||||
* To schedule a retry attempt (assuming the maximum retry limit
|
||||
* has not been reached), use [[response.throwThrottleError]].
|
||||
*/
|
||||
get processFunc(): null | FetchProcessFunc {
|
||||
return this.#process || null;
|
||||
}
|
||||
set processFunc(process: null | FetchProcessFunc) {
|
||||
this.#process = process;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called on each retry attempt.
|
||||
*/
|
||||
get retryFunc(): null | FetchRetryFunc {
|
||||
return this.#retry || null;
|
||||
}
|
||||
set retryFunc(retry: null | FetchRetryFunc) {
|
||||
this.#retry = retry;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called to fetch content from HTTP and
|
||||
* HTTPS URLs and is platform specific (e.g. nodejs vs
|
||||
* browsers).
|
||||
*
|
||||
* This is by default the currently registered global getUrl
|
||||
* function, which can be changed using [[registerGetUrl]].
|
||||
* If this has been set, setting is to ``null`` will cause
|
||||
* this FetchRequest (and any future clones) to revert back to
|
||||
* using the currently registered global getUrl function.
|
||||
*
|
||||
* Setting this is generally not necessary, but may be useful
|
||||
* for developers that wish to intercept requests or to
|
||||
* configurege a proxy or other agent.
|
||||
*/
|
||||
get getUrlFunc(): FetchGetUrlFunc {
|
||||
return this.#getUrlFunc || defaultGetUrlFunc;
|
||||
}
|
||||
set getUrlFunc(value: null | FetchGetUrlFunc) {
|
||||
this.#getUrlFunc = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new FetchRequest instance with default values.
|
||||
*
|
||||
* Once created, each property may be set before issuing a
|
||||
* ``.send()`` to make the request.
|
||||
*/
|
||||
constructor(url: string) {
|
||||
this.#url = String(url);
|
||||
|
||||
this.#allowInsecure = false;
|
||||
this.#gzip = true;
|
||||
this.#headers = { };
|
||||
this.#method = "";
|
||||
this.#timeout = 300000;
|
||||
|
||||
this.#throttle = {
|
||||
slotInterval: SLOT_INTERVAL,
|
||||
maxAttempts: MAX_ATTEMPTS
|
||||
};
|
||||
|
||||
this.#getUrlFunc = null;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `<FetchRequest method=${ JSON.stringify(this.method) } url=${ JSON.stringify(this.url) } headers=${ JSON.stringify(this.headers) } body=${ this.#body ? hexlify(this.#body): "null" }>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the throttle parameters used to determine maximum
|
||||
* attempts and exponential-backoff properties.
|
||||
*/
|
||||
setThrottleParams(params: FetchThrottleParams): void {
|
||||
if (params.slotInterval != null) {
|
||||
this.#throttle.slotInterval = params.slotInterval;
|
||||
}
|
||||
if (params.maxAttempts != null) {
|
||||
this.#throttle.maxAttempts = params.maxAttempts;
|
||||
}
|
||||
}
|
||||
|
||||
async #send(attempt: number, expires: number, delay: number, _request: FetchRequest, _response: FetchResponse): Promise<FetchResponse> {
|
||||
if (attempt >= this.#throttle.maxAttempts) {
|
||||
return _response.makeServerError("exceeded maximum retry limit");
|
||||
}
|
||||
|
||||
assert(getTime() <= expires, "timeout", "TIMEOUT", {
|
||||
operation: "request.send", reason: "timeout", request: _request
|
||||
});
|
||||
|
||||
if (delay > 0) { await wait(delay); }
|
||||
|
||||
let req = this.clone();
|
||||
const scheme = (req.url.split(":")[0] || "").toLowerCase();
|
||||
|
||||
// Process any Gateways
|
||||
if (scheme in Gateways) {
|
||||
const result = await Gateways[scheme](req.url, checkSignal(_request.#signal));
|
||||
if (result instanceof FetchResponse) {
|
||||
let response = result;
|
||||
|
||||
if (this.processFunc) {
|
||||
checkSignal(_request.#signal);
|
||||
try {
|
||||
response = await this.processFunc(req, response);
|
||||
} catch (error: any) {
|
||||
|
||||
// Something went wrong during processing; throw a 5xx server error
|
||||
if (error.throttle == null || typeof(error.stall) !== "number") {
|
||||
response.makeServerError("error in post-processing function", error).assertOk();
|
||||
}
|
||||
|
||||
// Ignore throttling
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
req = result;
|
||||
}
|
||||
|
||||
// We have a preflight function; update the request
|
||||
if (this.preflightFunc) { req = await this.preflightFunc(req); }
|
||||
|
||||
const resp = await this.getUrlFunc(req, checkSignal(_request.#signal));
|
||||
let response = new FetchResponse(resp.statusCode, resp.statusMessage, resp.headers, resp.body, _request);
|
||||
|
||||
if (response.statusCode === 301 || response.statusCode === 302) {
|
||||
|
||||
// Redirect
|
||||
try {
|
||||
const location = response.headers.location || "";
|
||||
return req.redirect(location).#send(attempt + 1, expires, 0, _request, response);
|
||||
} catch (error) { }
|
||||
|
||||
// Things won't get any better on another attempt; abort
|
||||
return response;
|
||||
|
||||
} else if (response.statusCode === 429) {
|
||||
|
||||
// Throttle
|
||||
if (this.retryFunc == null || (await this.retryFunc(req, response, attempt))) {
|
||||
const retryAfter = response.headers["retry-after"];
|
||||
let delay = this.#throttle.slotInterval * Math.trunc(Math.random() * Math.pow(2, attempt));
|
||||
if (typeof(retryAfter) === "string" && retryAfter.match(/^[1-9][0-9]*$/)) {
|
||||
delay = parseInt(retryAfter);
|
||||
}
|
||||
return req.clone().#send(attempt + 1, expires, delay, _request, response);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.processFunc) {
|
||||
checkSignal(_request.#signal);
|
||||
try {
|
||||
response = await this.processFunc(req, response);
|
||||
} catch (error: any) {
|
||||
|
||||
// Something went wrong during processing; throw a 5xx server error
|
||||
if (error.throttle == null || typeof(error.stall) !== "number") {
|
||||
response.makeServerError("error in post-processing function", error).assertOk();
|
||||
}
|
||||
|
||||
// Throttle
|
||||
let delay = this.#throttle.slotInterval * Math.trunc(Math.random() * Math.pow(2, attempt));;
|
||||
if (error.stall >= 0) { delay = error.stall; }
|
||||
|
||||
return req.clone().#send(attempt + 1, expires, delay, _request, response);
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to the response by sending the request.
|
||||
*/
|
||||
send(): Promise<FetchResponse> {
|
||||
assert(this.#signal == null, "request already sent", "UNSUPPORTED_OPERATION", { operation: "fetchRequest.send" });
|
||||
this.#signal = new FetchCancelSignal(this);
|
||||
return this.#send(0, getTime() + this.timeout, 0, this, new FetchResponse(0, "", { }, null, this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the inflight response, causing a ``CANCELLED``
|
||||
* error to be rejected from the [[send]].
|
||||
*/
|
||||
cancel(): void {
|
||||
assert(this.#signal != null, "request has not been sent", "UNSUPPORTED_OPERATION", { operation: "fetchRequest.cancel" });
|
||||
const signal = fetchSignals.get(this);
|
||||
if (!signal) { throw new Error("missing signal; should not happen"); }
|
||||
signal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FetchRequest]] that represents the redirection
|
||||
* to %%location%%.
|
||||
*/
|
||||
redirect(location: string): FetchRequest {
|
||||
// Redirection; for now we only support absolute locations
|
||||
const current = this.url.split(":")[0].toLowerCase();
|
||||
const target = location.split(":")[0].toLowerCase();
|
||||
|
||||
// Don't allow redirecting:
|
||||
// - non-GET requests
|
||||
// - downgrading the security (e.g. https => http)
|
||||
// - to non-HTTP (or non-HTTPS) protocols [this could be relaxed?]
|
||||
assert(this.method === "GET" && (current !== "https" || target !== "http") && location.match(/^https?:/), `unsupported redirect`, "UNSUPPORTED_OPERATION", {
|
||||
operation: `redirect(${ this.method } ${ JSON.stringify(this.url) } => ${ JSON.stringify(location) })`
|
||||
});
|
||||
|
||||
// Create a copy of this request, with a new URL
|
||||
const req = new FetchRequest(location);
|
||||
req.method = "GET";
|
||||
req.allowGzip = this.allowGzip;
|
||||
req.timeout = this.timeout;
|
||||
req.#headers = Object.assign({ }, this.#headers);
|
||||
if (this.#body) { req.#body = new Uint8Array(this.#body); }
|
||||
req.#bodyType = this.#bodyType;
|
||||
|
||||
// Do not forward credentials unless on the same domain; only absolute
|
||||
//req.allowInsecure = false;
|
||||
// paths are currently supported; may want a way to specify to forward?
|
||||
//setStore(req.#props, "creds", getStore(this.#pros, "creds"));
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new copy of this request.
|
||||
*/
|
||||
clone(): FetchRequest {
|
||||
const clone = new FetchRequest(this.url);
|
||||
|
||||
// Preserve "default method" (i.e. null)
|
||||
clone.#method = this.#method;
|
||||
|
||||
// Preserve "default body" with type, copying the Uint8Array is present
|
||||
if (this.#body) { clone.#body = this.#body; }
|
||||
clone.#bodyType = this.#bodyType;
|
||||
|
||||
// Preserve "default headers"
|
||||
clone.#headers = Object.assign({ }, this.#headers);
|
||||
|
||||
// Credentials is readonly, so we copy internally
|
||||
clone.#creds = this.#creds;
|
||||
|
||||
if (this.allowGzip) { clone.allowGzip = true; }
|
||||
|
||||
clone.timeout = this.timeout;
|
||||
if (this.allowInsecureAuthentication) { clone.allowInsecureAuthentication = true; }
|
||||
|
||||
clone.#preflight = this.#preflight;
|
||||
clone.#process = this.#process;
|
||||
clone.#retry = this.#retry;
|
||||
|
||||
clone.#throttle = Object.assign({ }, this.#throttle);
|
||||
|
||||
clone.#getUrlFunc = this.#getUrlFunc;
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks all static configuration for gateways and FetchGetUrlFunc
|
||||
* registration.
|
||||
*/
|
||||
static lockConfig(): void {
|
||||
locked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Gateway function for %%scheme%%.
|
||||
*/
|
||||
static getGateway(scheme: string): null | FetchGatewayFunc {
|
||||
return Gateways[scheme.toLowerCase()] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the %%func%% when fetching URIs using %%scheme%%.
|
||||
*
|
||||
* This method affects all requests globally.
|
||||
*
|
||||
* If [[lockConfig]] has been called, no change is made and this
|
||||
* throws.
|
||||
*/
|
||||
static registerGateway(scheme: string, func: FetchGatewayFunc): void {
|
||||
scheme = scheme.toLowerCase();
|
||||
if (scheme === "http" || scheme === "https") {
|
||||
throw new Error(`cannot intercept ${ scheme }; use registerGetUrl`);
|
||||
}
|
||||
if (locked) { throw new Error("gateways locked"); }
|
||||
Gateways[scheme] = func;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use %%getUrl%% when fetching URIs over HTTP and HTTPS requests.
|
||||
*
|
||||
* This method affects all requests globally.
|
||||
*
|
||||
* If [[lockConfig]] has been called, no change is made and this
|
||||
* throws.
|
||||
*/
|
||||
static registerGetUrl(getUrl: FetchGetUrlFunc): void {
|
||||
if (locked) { throw new Error("gateways locked"); }
|
||||
defaultGetUrlFunc = getUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a getUrl function that fetches content from HTTP and
|
||||
* HTTPS URLs.
|
||||
*
|
||||
* The available %%options%% are dependent on the platform
|
||||
* implementation of the default getUrl function.
|
||||
*
|
||||
* This is not generally something that is needed, but is useful
|
||||
* when trying to customize simple behaviour when fetching HTTP
|
||||
* content.
|
||||
*/
|
||||
static createGetUrlFunc(options?: Record<string, any>): FetchGetUrlFunc {
|
||||
return createGetUrl(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that can "fetch" data URIs.
|
||||
*
|
||||
* Note that this is automatically done internally to support
|
||||
* data URIs, so it is not necessary to register it.
|
||||
*
|
||||
* This is not generally something that is needed, but may
|
||||
* be useful in a wrapper to perfom custom data URI functionality.
|
||||
*/
|
||||
static createDataGateway(): FetchGatewayFunc {
|
||||
return dataGatewayFunc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that will fetch IPFS (unvalidated) from
|
||||
* a custom gateway baseUrl.
|
||||
*
|
||||
* The default IPFS gateway used internally is
|
||||
* ``"https:/\/gateway.ipfs.io/ipfs/"``.
|
||||
*/
|
||||
static createIpfsGatewayFunc(baseUrl: string): FetchGatewayFunc {
|
||||
return getIpfsGatewayFunc(baseUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface ThrottleError extends Error {
|
||||
stall: number;
|
||||
throttle: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* The response for a FetchRequest.
|
||||
*/
|
||||
export class FetchResponse implements Iterable<[ key: string, value: string ]> {
|
||||
#statusCode: number;
|
||||
#statusMessage: string;
|
||||
#headers: Record<string, string>;
|
||||
#body: null | Readonly<Uint8Array>;
|
||||
#request: null | FetchRequest;
|
||||
|
||||
#error: { error?: Error, message: string };
|
||||
|
||||
toString(): string {
|
||||
return `<FetchResponse status=${ this.statusCode } body=${ this.#body ? hexlify(this.#body): "null" }>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* The response status code.
|
||||
*/
|
||||
get statusCode(): number { return this.#statusCode; }
|
||||
|
||||
/**
|
||||
* The response status message.
|
||||
*/
|
||||
get statusMessage(): string { return this.#statusMessage; }
|
||||
|
||||
/**
|
||||
* The response headers. All keys are lower-case.
|
||||
*/
|
||||
get headers(): Record<string, string> { return Object.assign({ }, this.#headers); }
|
||||
|
||||
/**
|
||||
* The response body, or ``null`` if there was no body.
|
||||
*/
|
||||
get body(): null | Readonly<Uint8Array> {
|
||||
return (this.#body == null) ? null: new Uint8Array(this.#body);
|
||||
}
|
||||
|
||||
/**
|
||||
* The response body as a UTF-8 encoded string, or the empty
|
||||
* string (i.e. ``""``) if there was no body.
|
||||
*
|
||||
* An error is thrown if the body is invalid UTF-8 data.
|
||||
*/
|
||||
get bodyText(): string {
|
||||
try {
|
||||
return (this.#body == null) ? "": toUtf8String(this.#body);
|
||||
} catch (error) {
|
||||
assert(false, "response body is not valid UTF-8 data", "UNSUPPORTED_OPERATION", {
|
||||
operation: "bodyText", info: { response: this }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The response body, decoded as JSON.
|
||||
*
|
||||
* An error is thrown if the body is invalid JSON-encoded data
|
||||
* or if there was no body.
|
||||
*/
|
||||
get bodyJson(): any {
|
||||
try {
|
||||
return JSON.parse(this.bodyText);
|
||||
} catch (error) {
|
||||
assert(false, "response body is not valid JSON", "UNSUPPORTED_OPERATION", {
|
||||
operation: "bodyJson", info: { response: this }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator](): Iterator<[ key: string, value: string ]> {
|
||||
const headers = this.headers;
|
||||
const keys = Object.keys(headers);
|
||||
let index = 0;
|
||||
return {
|
||||
next: () => {
|
||||
if (index < keys.length) {
|
||||
const key = keys[index++];
|
||||
return {
|
||||
value: [ key, headers[key] ], done: false
|
||||
}
|
||||
}
|
||||
return { value: undefined, done: true };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constructor(statusCode: number, statusMessage: string, headers: Readonly<Record<string, string>>, body: null | Uint8Array, request?: FetchRequest) {
|
||||
this.#statusCode = statusCode;
|
||||
this.#statusMessage = statusMessage;
|
||||
this.#headers = Object.keys(headers).reduce((accum, k) => {
|
||||
accum[k.toLowerCase()] = String(headers[k]);
|
||||
return accum;
|
||||
}, <Record<string, string>>{ });
|
||||
this.#body = ((body == null) ? null: new Uint8Array(body));
|
||||
this.#request = (request || null);
|
||||
|
||||
this.#error = { message: "" };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Response with matching headers and body, but with
|
||||
* an error status code (i.e. 599) and %%message%% with an
|
||||
* optional %%error%%.
|
||||
*/
|
||||
makeServerError(message?: string, error?: Error): FetchResponse {
|
||||
let statusMessage: string;
|
||||
if (!message) {
|
||||
message = `${ this.statusCode } ${ this.statusMessage }`;
|
||||
statusMessage = `CLIENT ESCALATED SERVER ERROR (${ message })`;
|
||||
} else {
|
||||
statusMessage = `CLIENT ESCALATED SERVER ERROR (${ this.statusCode } ${ this.statusMessage }; ${ message })`;
|
||||
}
|
||||
const response = new FetchResponse(599, statusMessage, this.headers,
|
||||
this.body, this.#request || undefined);
|
||||
response.#error = { message, error };
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* If called within a [request.processFunc](FetchRequest-processFunc)
|
||||
* call, causes the request to retry as if throttled for %%stall%%
|
||||
* milliseconds.
|
||||
*/
|
||||
throwThrottleError(message?: string, stall?: number): never {
|
||||
if (stall == null) {
|
||||
stall = -1;
|
||||
} else {
|
||||
assertArgument(Number.isInteger(stall) && stall >= 0, "invalid stall timeout", "stall", stall);
|
||||
}
|
||||
|
||||
const error = new Error(message || "throttling requests");
|
||||
|
||||
defineProperties(<ThrottleError>error, { stall, throttle: true });
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the header value for %%key%%, ignoring case.
|
||||
*/
|
||||
getHeader(key: string): string {
|
||||
return this.headers[key.toLowerCase()];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the response has a body.
|
||||
*/
|
||||
hasBody(): this is (FetchResponse & { body: Uint8Array }) {
|
||||
return (this.#body != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The request made for this response.
|
||||
*/
|
||||
get request(): null | FetchRequest { return this.#request; }
|
||||
|
||||
/**
|
||||
* Returns true if this response was a success statusCode.
|
||||
*/
|
||||
ok(): boolean {
|
||||
return (this.#error.message === "" && this.statusCode >= 200 && this.statusCode < 300);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a ``SERVER_ERROR`` if this response is not ok.
|
||||
*/
|
||||
assertOk(): void {
|
||||
if (this.ok()) { return; }
|
||||
let { message, error } = this.#error;
|
||||
if (message === "") {
|
||||
message = `server response ${ this.statusCode } ${ this.statusMessage }`;
|
||||
}
|
||||
|
||||
let requestUrl: null | string = null;
|
||||
if (this.request) { requestUrl = this.request.url; }
|
||||
|
||||
let responseBody: null | string = null;
|
||||
try {
|
||||
if (this.#body) { responseBody = toUtf8String(this.#body); }
|
||||
} catch (e) { }
|
||||
|
||||
assert(false, message, "SERVER_ERROR", {
|
||||
request: (this.request || "unknown request"), response: this, error,
|
||||
info: {
|
||||
requestUrl, responseBody,
|
||||
responseStatus: `${ this.statusCode } ${ this.statusMessage }` }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getTime(): number { return (new Date()).getTime(); }
|
||||
|
||||
function unpercent(value: string): Uint8Array {
|
||||
return toUtf8Bytes(value.replace(/%([0-9a-f][0-9a-f])/gi, (all, code) => {
|
||||
return String.fromCharCode(parseInt(code, 16));
|
||||
}));
|
||||
}
|
||||
|
||||
function wait(delay: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
643
dev/env/node_modules/ethers/src.ts/utils/fixednumber.ts
generated
vendored
Executable file
643
dev/env/node_modules/ethers/src.ts/utils/fixednumber.ts
generated
vendored
Executable file
@@ -0,0 +1,643 @@
|
||||
/**
|
||||
* The **FixedNumber** class permits using values with decimal places,
|
||||
* using fixed-pont math.
|
||||
*
|
||||
* Fixed-point math is still based on integers under-the-hood, but uses an
|
||||
* internal offset to store fractional components below, and each operation
|
||||
* corrects for this after each operation.
|
||||
*
|
||||
* @_section: api/utils/fixed-point-math:Fixed-Point Maths [about-fixed-point-math]
|
||||
*/
|
||||
import { getBytes } from "./data.js";
|
||||
import { assert, assertArgument, assertPrivate } from "./errors.js";
|
||||
import {
|
||||
getBigInt, getNumber, fromTwos, mask, toBigInt
|
||||
} from "./maths.js";
|
||||
import { defineProperties } from "./properties.js";
|
||||
|
||||
import type { BigNumberish, BytesLike, Numeric } from "./index.js";
|
||||
|
||||
const BN_N1 = BigInt(-1);
|
||||
const BN_0 = BigInt(0);
|
||||
const BN_1 = BigInt(1);
|
||||
const BN_5 = BigInt(5);
|
||||
|
||||
const _guard = { };
|
||||
|
||||
|
||||
// Constant to pull zeros from for multipliers
|
||||
let Zeros = "0000";
|
||||
while (Zeros.length < 80) { Zeros += Zeros; }
|
||||
|
||||
// Returns a string "1" followed by decimal "0"s
|
||||
function getTens(decimals: number): bigint {
|
||||
let result = Zeros;
|
||||
while (result.length < decimals) { result += result; }
|
||||
return BigInt("1" + result.substring(0, decimals));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Returns a new FixedFormat for %%value%%.
|
||||
*
|
||||
* If %%value%% is specified as a ``number``, the bit-width is
|
||||
* 128 bits and %%value%% is used for the ``decimals``.
|
||||
*
|
||||
* A string %%value%% may begin with ``fixed`` or ``ufixed``
|
||||
* for signed and unsigned respectfully. If no other properties
|
||||
* are specified, the bit-width is 128-bits with 18 decimals.
|
||||
*
|
||||
* To specify the bit-width and demicals, append them separated
|
||||
* by an ``"x"`` to the %%value%%.
|
||||
*
|
||||
* For example, ``ufixed128x18`` describes an unsigned, 128-bit
|
||||
* wide format with 18 decimals.
|
||||
*
|
||||
* If %%value%% is an other object, its properties for ``signed``,
|
||||
* ``width`` and ``decimals`` are checked.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A description of a fixed-point arithmetic field.
|
||||
*
|
||||
* When specifying the fixed format, the values override the default of
|
||||
* a ``fixed128x18``, which implies a signed 128-bit value with 18
|
||||
* decimals of precision.
|
||||
*
|
||||
* The alias ``fixed`` and ``ufixed`` can be used for ``fixed128x18`` and
|
||||
* ``ufixed128x18`` respectively.
|
||||
*
|
||||
* When a fixed format string begins with a ``u``, it indicates the field
|
||||
* is unsigned, so any negative values will overflow. The first number
|
||||
* indicates the bit-width and the second number indicates the decimal
|
||||
* precision.
|
||||
*
|
||||
* When a ``number`` is used for a fixed format, it indicates the number
|
||||
* of decimal places, and the default width and signed-ness will be used.
|
||||
*
|
||||
* The bit-width must be byte aligned and the decimals can be at most 80.
|
||||
*/
|
||||
export type FixedFormat = number | string | {
|
||||
signed?: boolean,
|
||||
width?: number,
|
||||
decimals?: number
|
||||
};
|
||||
|
||||
function checkValue(val: bigint, format: _FixedFormat, safeOp?: string): bigint {
|
||||
const width = BigInt(format.width);
|
||||
if (format.signed) {
|
||||
const limit = (BN_1 << (width - BN_1));
|
||||
assert(safeOp == null || (val >= -limit && val < limit), "overflow", "NUMERIC_FAULT", {
|
||||
operation: <string>safeOp, fault: "overflow", value: val
|
||||
});
|
||||
|
||||
if (val > BN_0) {
|
||||
val = fromTwos(mask(val, width), width);
|
||||
} else {
|
||||
val = -fromTwos(mask(-val, width), width);
|
||||
}
|
||||
|
||||
} else {
|
||||
const limit = (BN_1 << width);
|
||||
assert(safeOp == null || (val >= 0 && val < limit), "overflow", "NUMERIC_FAULT", {
|
||||
operation: <string>safeOp, fault: "overflow", value: val
|
||||
});
|
||||
val = (((val % limit) + limit) % limit) & (limit - BN_1);
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
type _FixedFormat = { signed: boolean, width: number, decimals: number, name: string }
|
||||
|
||||
function getFormat(value?: FixedFormat): _FixedFormat {
|
||||
if (typeof(value) === "number") { value = `fixed128x${value}` }
|
||||
|
||||
let signed = true;
|
||||
let width = 128;
|
||||
let decimals = 18;
|
||||
|
||||
if (typeof(value) === "string") {
|
||||
// Parse the format string
|
||||
if (value === "fixed") {
|
||||
// defaults...
|
||||
} else if (value === "ufixed") {
|
||||
signed = false;
|
||||
} else {
|
||||
const match = value.match(/^(u?)fixed([0-9]+)x([0-9]+)$/);
|
||||
assertArgument(match, "invalid fixed format", "format", value);
|
||||
signed = (match[1] !== "u");
|
||||
width = parseInt(match[2]);
|
||||
decimals = parseInt(match[3]);
|
||||
}
|
||||
} else if (value) {
|
||||
// Extract the values from the object
|
||||
const v: any = value;
|
||||
const check = (key: string, type: string, defaultValue: any): any => {
|
||||
if (v[key] == null) { return defaultValue; }
|
||||
assertArgument(typeof(v[key]) === type,
|
||||
"invalid fixed format (" + key + " not " + type +")", "format." + key, v[key]);
|
||||
return v[key];
|
||||
}
|
||||
signed = check("signed", "boolean", signed);
|
||||
width = check("width", "number", width);
|
||||
decimals = check("decimals", "number", decimals);
|
||||
}
|
||||
|
||||
assertArgument((width % 8) === 0, "invalid FixedNumber width (not byte aligned)", "format.width", width);
|
||||
assertArgument(decimals <= 80, "invalid FixedNumber decimals (too large)", "format.decimals", decimals);
|
||||
|
||||
const name = (signed ? "": "u") + "fixed" + String(width) + "x" + String(decimals);
|
||||
|
||||
return { signed, width, decimals, name };
|
||||
}
|
||||
|
||||
function toString(val: bigint, decimals: number) {
|
||||
let negative = "";
|
||||
if (val < BN_0) {
|
||||
negative = "-";
|
||||
val *= BN_N1;
|
||||
}
|
||||
|
||||
let str = val.toString();
|
||||
|
||||
// No decimal point for whole values
|
||||
if (decimals === 0) { return (negative + str); }
|
||||
|
||||
// Pad out to the whole component (including a whole digit)
|
||||
while (str.length <= decimals) { str = Zeros + str; }
|
||||
|
||||
// Insert the decimal point
|
||||
const index = str.length - decimals;
|
||||
str = str.substring(0, index) + "." + str.substring(index);
|
||||
|
||||
// Trim the whole component (leaving at least one 0)
|
||||
while (str[0] === "0" && str[1] !== ".") {
|
||||
str = str.substring(1);
|
||||
}
|
||||
|
||||
// Trim the decimal component (leaving at least one 0)
|
||||
while (str[str.length - 1] === "0" && str[str.length - 2] !== ".") {
|
||||
str = str.substring(0, str.length - 1);
|
||||
}
|
||||
|
||||
return (negative + str);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A FixedNumber represents a value over its [[FixedFormat]]
|
||||
* arithmetic field.
|
||||
*
|
||||
* A FixedNumber can be used to perform math, losslessly, on
|
||||
* values which have decmial places.
|
||||
*
|
||||
* A FixedNumber has a fixed bit-width to store values in, and stores all
|
||||
* values internally by multiplying the value by 10 raised to the power of
|
||||
* %%decimals%%.
|
||||
*
|
||||
* If operations are performed that cause a value to grow too high (close to
|
||||
* positive infinity) or too low (close to negative infinity), the value
|
||||
* is said to //overflow//.
|
||||
*
|
||||
* For example, an 8-bit signed value, with 0 decimals may only be within
|
||||
* the range ``-128`` to ``127``; so ``-128 - 1`` will overflow and become
|
||||
* ``127``. Likewise, ``127 + 1`` will overflow and become ``-127``.
|
||||
*
|
||||
* Many operation have a normal and //unsafe// variant. The normal variant
|
||||
* will throw a [[NumericFaultError]] on any overflow, while the //unsafe//
|
||||
* variant will silently allow overflow, corrupting its value value.
|
||||
*
|
||||
* If operations are performed that cause a value to become too small
|
||||
* (close to zero), the value loses precison and is said to //underflow//.
|
||||
*
|
||||
* For example, a value with 1 decimal place may store a number as small
|
||||
* as ``0.1``, but the value of ``0.1 / 2`` is ``0.05``, which cannot fit
|
||||
* into 1 decimal place, so underflow occurs which means precision is lost
|
||||
* and the value becomes ``0``.
|
||||
*
|
||||
* Some operations have a normal and //signalling// variant. The normal
|
||||
* variant will silently ignore underflow, while the //signalling// variant
|
||||
* will thow a [[NumericFaultError]] on underflow.
|
||||
*/
|
||||
export class FixedNumber {
|
||||
|
||||
/**
|
||||
* The specific fixed-point arithmetic field for this value.
|
||||
*/
|
||||
readonly format!: string;
|
||||
|
||||
readonly #format: _FixedFormat;
|
||||
|
||||
// The actual value (accounting for decimals)
|
||||
#val: bigint;
|
||||
|
||||
// A base-10 value to multiple values by to maintain the magnitude
|
||||
readonly #tens: bigint;
|
||||
|
||||
/**
|
||||
* This is a property so console.log shows a human-meaningful value.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
readonly _value!: string;
|
||||
|
||||
// Use this when changing this file to get some typing info,
|
||||
// but then switch to any to mask the internal type
|
||||
//constructor(guard: any, value: bigint, format: _FixedFormat) {
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
constructor(guard: any, value: bigint, format: any) {
|
||||
assertPrivate(guard, _guard, "FixedNumber");
|
||||
|
||||
this.#val = value;
|
||||
|
||||
this.#format = format;
|
||||
|
||||
const _value = toString(value, format.decimals);
|
||||
|
||||
defineProperties<FixedNumber>(this, { format: format.name, _value });
|
||||
|
||||
this.#tens = getTens(format.decimals);
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, negative values are permitted, otherwise only
|
||||
* positive values and zero are allowed.
|
||||
*/
|
||||
get signed(): boolean { return this.#format.signed; }
|
||||
|
||||
/**
|
||||
* The number of bits available to store the value.
|
||||
*/
|
||||
get width(): number { return this.#format.width; }
|
||||
|
||||
/**
|
||||
* The number of decimal places in the fixed-point arithment field.
|
||||
*/
|
||||
get decimals(): number { return this.#format.decimals; }
|
||||
|
||||
/**
|
||||
* The value as an integer, based on the smallest unit the
|
||||
* [[decimals]] allow.
|
||||
*/
|
||||
get value(): bigint { return this.#val; }
|
||||
|
||||
#checkFormat(other: FixedNumber): void {
|
||||
assertArgument(this.format === other.format,
|
||||
"incompatible format; use fixedNumber.toFormat", "other", other);
|
||||
}
|
||||
|
||||
#checkValue(val: bigint, safeOp?: string): FixedNumber {
|
||||
/*
|
||||
const width = BigInt(this.width);
|
||||
if (this.signed) {
|
||||
const limit = (BN_1 << (width - BN_1));
|
||||
assert(safeOp == null || (val >= -limit && val < limit), "overflow", "NUMERIC_FAULT", {
|
||||
operation: <string>safeOp, fault: "overflow", value: val
|
||||
});
|
||||
|
||||
if (val > BN_0) {
|
||||
val = fromTwos(mask(val, width), width);
|
||||
} else {
|
||||
val = -fromTwos(mask(-val, width), width);
|
||||
}
|
||||
|
||||
} else {
|
||||
const masked = mask(val, width);
|
||||
assert(safeOp == null || (val >= 0 && val === masked), "overflow", "NUMERIC_FAULT", {
|
||||
operation: <string>safeOp, fault: "overflow", value: val
|
||||
});
|
||||
val = masked;
|
||||
}
|
||||
*/
|
||||
val = checkValue(val, this.#format, safeOp);
|
||||
return new FixedNumber(_guard, val, this.#format);
|
||||
}
|
||||
|
||||
#add(o: FixedNumber, safeOp?: string): FixedNumber {
|
||||
this.#checkFormat(o);
|
||||
return this.#checkValue(this.#val + o.#val, safeOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% added
|
||||
* to %%other%%, ignoring overflow.
|
||||
*/
|
||||
addUnsafe(other: FixedNumber): FixedNumber { return this.#add(other); }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% added
|
||||
* to %%other%%. A [[NumericFaultError]] is thrown if overflow
|
||||
* occurs.
|
||||
*/
|
||||
add(other: FixedNumber): FixedNumber { return this.#add(other, "add"); }
|
||||
|
||||
#sub(o: FixedNumber, safeOp?: string): FixedNumber {
|
||||
this.#checkFormat(o);
|
||||
return this.#checkValue(this.#val - o.#val, safeOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%other%% subtracted
|
||||
* from %%this%%, ignoring overflow.
|
||||
*/
|
||||
subUnsafe(other: FixedNumber): FixedNumber { return this.#sub(other); }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%other%% subtracted
|
||||
* from %%this%%. A [[NumericFaultError]] is thrown if overflow
|
||||
* occurs.
|
||||
*/
|
||||
sub(other: FixedNumber): FixedNumber { return this.#sub(other, "sub"); }
|
||||
|
||||
#mul(o: FixedNumber, safeOp?: string): FixedNumber {
|
||||
this.#checkFormat(o);
|
||||
return this.#checkValue((this.#val * o.#val) / this.#tens, safeOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% multiplied
|
||||
* by %%other%%, ignoring overflow and underflow (precision loss).
|
||||
*/
|
||||
mulUnsafe(other: FixedNumber): FixedNumber { return this.#mul(other); }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% multiplied
|
||||
* by %%other%%. A [[NumericFaultError]] is thrown if overflow
|
||||
* occurs.
|
||||
*/
|
||||
mul(other: FixedNumber): FixedNumber { return this.#mul(other, "mul"); }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% multiplied
|
||||
* by %%other%%. A [[NumericFaultError]] is thrown if overflow
|
||||
* occurs or if underflow (precision loss) occurs.
|
||||
*/
|
||||
mulSignal(other: FixedNumber): FixedNumber {
|
||||
this.#checkFormat(other);
|
||||
const value = this.#val * other.#val;
|
||||
assert((value % this.#tens) === BN_0, "precision lost during signalling mul", "NUMERIC_FAULT", {
|
||||
operation: "mulSignal", fault: "underflow", value: this
|
||||
});
|
||||
return this.#checkValue(value / this.#tens, "mulSignal");
|
||||
}
|
||||
|
||||
#div(o: FixedNumber, safeOp?: string): FixedNumber {
|
||||
assert(o.#val !== BN_0, "division by zero", "NUMERIC_FAULT", {
|
||||
operation: "div", fault: "divide-by-zero", value: this
|
||||
});
|
||||
this.#checkFormat(o);
|
||||
return this.#checkValue((this.#val * this.#tens) / o.#val, safeOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% divided
|
||||
* by %%other%%, ignoring underflow (precision loss). A
|
||||
* [[NumericFaultError]] is thrown if overflow occurs.
|
||||
*/
|
||||
divUnsafe(other: FixedNumber): FixedNumber { return this.#div(other); }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% divided
|
||||
* by %%other%%, ignoring underflow (precision loss). A
|
||||
* [[NumericFaultError]] is thrown if overflow occurs.
|
||||
*/
|
||||
div(other: FixedNumber): FixedNumber { return this.#div(other, "div"); }
|
||||
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the result of %%this%% divided
|
||||
* by %%other%%. A [[NumericFaultError]] is thrown if underflow
|
||||
* (precision loss) occurs.
|
||||
*/
|
||||
divSignal(other: FixedNumber): FixedNumber {
|
||||
assert(other.#val !== BN_0, "division by zero", "NUMERIC_FAULT", {
|
||||
operation: "div", fault: "divide-by-zero", value: this
|
||||
});
|
||||
this.#checkFormat(other);
|
||||
const value = (this.#val * this.#tens);
|
||||
assert((value % other.#val) === BN_0, "precision lost during signalling div", "NUMERIC_FAULT", {
|
||||
operation: "divSignal", fault: "underflow", value: this
|
||||
});
|
||||
return this.#checkValue(value / other.#val, "divSignal");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a comparison result between %%this%% and %%other%%.
|
||||
*
|
||||
* This is suitable for use in sorting, where ``-1`` implies %%this%%
|
||||
* is smaller, ``1`` implies %%this%% is larger and ``0`` implies
|
||||
* both are equal.
|
||||
*/
|
||||
cmp(other: FixedNumber): number {
|
||||
let a = this.value, b = other.value;
|
||||
|
||||
// Coerce a and b to the same magnitude
|
||||
const delta = this.decimals - other.decimals;
|
||||
if (delta > 0) {
|
||||
b *= getTens(delta);
|
||||
} else if (delta < 0) {
|
||||
a *= getTens(-delta);
|
||||
}
|
||||
|
||||
// Comnpare
|
||||
if (a < b) { return -1; }
|
||||
if (a > b) { return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% is equal to %%this%%.
|
||||
*/
|
||||
eq(other: FixedNumber): boolean { return this.cmp(other) === 0; }
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% is less than to %%this%%.
|
||||
*/
|
||||
lt(other: FixedNumber): boolean { return this.cmp(other) < 0; }
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% is less than or equal to %%this%%.
|
||||
*/
|
||||
lte(other: FixedNumber): boolean { return this.cmp(other) <= 0; }
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% is greater than to %%this%%.
|
||||
*/
|
||||
gt(other: FixedNumber): boolean { return this.cmp(other) > 0; }
|
||||
|
||||
/**
|
||||
* Returns true if %%other%% is greater than or equal to %%this%%.
|
||||
*/
|
||||
gte(other: FixedNumber): boolean { return this.cmp(other) >= 0; }
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] which is the largest **integer**
|
||||
* that is less than or equal to %%this%%.
|
||||
*
|
||||
* The decimal component of the result will always be ``0``.
|
||||
*/
|
||||
floor(): FixedNumber {
|
||||
let val = this.#val;
|
||||
if (this.#val < BN_0) { val -= this.#tens - BN_1; }
|
||||
val = (this.#val / this.#tens) * this.#tens;
|
||||
return this.#checkValue(val, "floor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] which is the smallest **integer**
|
||||
* that is greater than or equal to %%this%%.
|
||||
*
|
||||
* The decimal component of the result will always be ``0``.
|
||||
*/
|
||||
ceiling(): FixedNumber {
|
||||
let val = this.#val;
|
||||
if (this.#val > BN_0) { val += this.#tens - BN_1; }
|
||||
val = (this.#val / this.#tens) * this.#tens;
|
||||
return this.#checkValue(val, "ceiling");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new [[FixedNumber]] with the decimal component
|
||||
* rounded up on ties at %%decimals%% places.
|
||||
*/
|
||||
round(decimals?: number): FixedNumber {
|
||||
if (decimals == null) { decimals = 0; }
|
||||
|
||||
// Not enough precision to not already be rounded
|
||||
if (decimals >= this.decimals) { return this; }
|
||||
|
||||
const delta = this.decimals - decimals;
|
||||
const bump = BN_5 * getTens(delta - 1);
|
||||
|
||||
let value = this.value + bump;
|
||||
const tens = getTens(delta);
|
||||
value = (value / tens) * tens;
|
||||
|
||||
checkValue(value, this.#format, "round");
|
||||
|
||||
return new FixedNumber(_guard, value, this.#format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if %%this%% is equal to ``0``.
|
||||
*/
|
||||
isZero(): boolean { return (this.#val === BN_0); }
|
||||
|
||||
/**
|
||||
* Returns true if %%this%% is less than ``0``.
|
||||
*/
|
||||
isNegative(): boolean { return (this.#val < BN_0); }
|
||||
|
||||
/**
|
||||
* Returns the string representation of %%this%%.
|
||||
*/
|
||||
toString(): string { return this._value; }
|
||||
|
||||
/**
|
||||
* Returns a float approximation.
|
||||
*
|
||||
* Due to IEEE 754 precission (or lack thereof), this function
|
||||
* can only return an approximation and most values will contain
|
||||
* rounding errors.
|
||||
*/
|
||||
toUnsafeFloat(): number { return parseFloat(this.toString()); }
|
||||
|
||||
/**
|
||||
* Return a new [[FixedNumber]] with the same value but has had
|
||||
* its field set to %%format%%.
|
||||
*
|
||||
* This will throw if the value cannot fit into %%format%%.
|
||||
*/
|
||||
toFormat(format: FixedFormat): FixedNumber {
|
||||
return FixedNumber.fromString(this.toString(), format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [[FixedNumber]] for %%value%% divided by
|
||||
* %%decimal%% places with %%format%%.
|
||||
*
|
||||
* This will throw a [[NumericFaultError]] if %%value%% (once adjusted
|
||||
* for %%decimals%%) cannot fit in %%format%%, either due to overflow
|
||||
* or underflow (precision loss).
|
||||
*/
|
||||
static fromValue(_value: BigNumberish, _decimals?: Numeric, _format?: FixedFormat): FixedNumber {
|
||||
const decimals = (_decimals == null) ? 0: getNumber(_decimals);
|
||||
const format = getFormat(_format);
|
||||
|
||||
let value = getBigInt(_value, "value");
|
||||
const delta = decimals - format.decimals;
|
||||
if (delta > 0) {
|
||||
const tens = getTens(delta);
|
||||
assert((value % tens) === BN_0, "value loses precision for format", "NUMERIC_FAULT", {
|
||||
operation: "fromValue", fault: "underflow", value: _value
|
||||
});
|
||||
value /= tens;
|
||||
} else if (delta < 0) {
|
||||
value *= getTens(-delta);
|
||||
}
|
||||
|
||||
checkValue(value, format, "fromValue");
|
||||
|
||||
return new FixedNumber(_guard, value, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [[FixedNumber]] for %%value%% with %%format%%.
|
||||
*
|
||||
* This will throw a [[NumericFaultError]] if %%value%% cannot fit
|
||||
* in %%format%%, either due to overflow or underflow (precision loss).
|
||||
*/
|
||||
static fromString(_value: string, _format?: FixedFormat): FixedNumber {
|
||||
const match = _value.match(/^(-?)([0-9]*)\.?([0-9]*)$/);
|
||||
assertArgument(match && (match[2].length + match[3].length) > 0, "invalid FixedNumber string value", "value", _value);
|
||||
|
||||
const format = getFormat(_format);
|
||||
|
||||
let whole = (match[2] || "0"), decimal = (match[3] || "");
|
||||
|
||||
// Pad out the decimals
|
||||
while (decimal.length < format.decimals) { decimal += Zeros; }
|
||||
|
||||
// Check precision is safe
|
||||
assert(decimal.substring(format.decimals).match(/^0*$/), "too many decimals for format", "NUMERIC_FAULT", {
|
||||
operation: "fromString", fault: "underflow", value: _value
|
||||
});
|
||||
|
||||
// Remove extra padding
|
||||
decimal = decimal.substring(0, format.decimals);
|
||||
|
||||
const value = BigInt(match[1] + whole + decimal)
|
||||
|
||||
checkValue(value, format, "fromString");
|
||||
|
||||
return new FixedNumber(_guard, value, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new [[FixedNumber]] with the big-endian representation
|
||||
* %%value%% with %%format%%.
|
||||
*
|
||||
* This will throw a [[NumericFaultError]] if %%value%% cannot fit
|
||||
* in %%format%% due to overflow.
|
||||
*/
|
||||
static fromBytes(_value: BytesLike, _format?: FixedFormat): FixedNumber {
|
||||
let value = toBigInt(getBytes(_value, "value"));
|
||||
const format = getFormat(_format);
|
||||
|
||||
if (format.signed) { value = fromTwos(value, format.width); }
|
||||
|
||||
checkValue(value, format, "fromBytes");
|
||||
|
||||
return new FixedNumber(_guard, value, format);
|
||||
}
|
||||
}
|
||||
|
||||
//const f1 = FixedNumber.fromString("12.56", "fixed16x2");
|
||||
//const f2 = FixedNumber.fromString("0.3", "fixed16x2");
|
||||
//console.log(f1.divSignal(f2));
|
||||
//const BUMP = FixedNumber.from("0.5");
|
||||
81
dev/env/node_modules/ethers/src.ts/utils/geturl-browser.ts
generated
vendored
Executable file
81
dev/env/node_modules/ethers/src.ts/utils/geturl-browser.ts
generated
vendored
Executable file
@@ -0,0 +1,81 @@
|
||||
import { assert, makeError } from "./errors.js";
|
||||
|
||||
import type {
|
||||
FetchGetUrlFunc, FetchRequest, FetchCancelSignal, GetUrlResponse
|
||||
} from "./fetch.js";
|
||||
|
||||
export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {
|
||||
|
||||
async function getUrl(req: FetchRequest, _signal?: FetchCancelSignal): Promise<GetUrlResponse> {
|
||||
assert(_signal == null || !_signal.cancelled, "request cancelled before sending", "CANCELLED");
|
||||
|
||||
const protocol = req.url.split(":")[0].toLowerCase();
|
||||
|
||||
assert(protocol === "http" || protocol === "https", `unsupported protocol ${ protocol }`, "UNSUPPORTED_OPERATION", {
|
||||
info: { protocol },
|
||||
operation: "request"
|
||||
});
|
||||
|
||||
assert(protocol === "https" || !req.credentials || req.allowInsecureAuthentication, "insecure authorized connections unsupported", "UNSUPPORTED_OPERATION", {
|
||||
operation: "request"
|
||||
});
|
||||
|
||||
let error: null | Error = null;
|
||||
|
||||
const controller = new AbortController();
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
error = makeError("request timeout", "TIMEOUT");
|
||||
controller.abort();
|
||||
}, req.timeout);
|
||||
|
||||
if (_signal) {
|
||||
_signal.addListener(() => {
|
||||
error = makeError("request cancelled", "CANCELLED");
|
||||
controller.abort();
|
||||
});
|
||||
}
|
||||
|
||||
const init = Object.assign({ }, options, {
|
||||
method: req.method,
|
||||
headers: new Headers(Array.from(req)),
|
||||
body: req.body || undefined,
|
||||
signal: controller.signal
|
||||
});
|
||||
|
||||
let resp: Awaited<ReturnType<typeof fetch>>;
|
||||
try {
|
||||
resp = await fetch(req.url, init);
|
||||
} catch (_error) {
|
||||
clearTimeout(timer);
|
||||
if (error) { throw error; }
|
||||
throw _error;
|
||||
}
|
||||
|
||||
clearTimeout(timer);
|
||||
|
||||
const headers: Record<string, string> = { };
|
||||
resp.headers.forEach((value, key) => {
|
||||
headers[key.toLowerCase()] = value;
|
||||
});
|
||||
|
||||
const respBody = await resp.arrayBuffer();
|
||||
const body = (respBody == null) ? null: new Uint8Array(respBody);
|
||||
|
||||
return {
|
||||
statusCode: resp.status,
|
||||
statusMessage: resp.statusText,
|
||||
headers, body
|
||||
};
|
||||
}
|
||||
|
||||
return getUrl;
|
||||
}
|
||||
|
||||
// @TODO: remove in v7; provided for backwards compat
|
||||
const defaultGetUrl: FetchGetUrlFunc = createGetUrl({ });
|
||||
|
||||
export async function getUrl(req: FetchRequest, _signal?: FetchCancelSignal): Promise<GetUrlResponse> {
|
||||
return defaultGetUrl(req, _signal);
|
||||
}
|
||||
|
||||
141
dev/env/node_modules/ethers/src.ts/utils/geturl.ts
generated
vendored
Executable file
141
dev/env/node_modules/ethers/src.ts/utils/geturl.ts
generated
vendored
Executable file
@@ -0,0 +1,141 @@
|
||||
import http from "http";
|
||||
import https from "https";
|
||||
import { gunzipSync } from "zlib";
|
||||
|
||||
import { assert, makeError } from "./errors.js";
|
||||
import { getBytes } from "./data.js";
|
||||
|
||||
import type {
|
||||
FetchGetUrlFunc, FetchRequest, FetchCancelSignal, GetUrlResponse
|
||||
} from "./fetch.js";
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
export function createGetUrl(options?: Record<string, any>): FetchGetUrlFunc {
|
||||
|
||||
async function getUrl(req: FetchRequest, signal?: FetchCancelSignal): Promise<GetUrlResponse> {
|
||||
// Make sure we weren't cancelled before sending
|
||||
assert(signal == null || !signal.cancelled, "request cancelled before sending", "CANCELLED");
|
||||
|
||||
const protocol = req.url.split(":")[0].toLowerCase();
|
||||
|
||||
assert(protocol === "http" || protocol === "https", `unsupported protocol ${ protocol }`, "UNSUPPORTED_OPERATION", {
|
||||
info: { protocol },
|
||||
operation: "request"
|
||||
});
|
||||
|
||||
assert(protocol === "https" || !req.credentials || req.allowInsecureAuthentication, "insecure authorized connections unsupported", "UNSUPPORTED_OPERATION", {
|
||||
operation: "request"
|
||||
});
|
||||
|
||||
const method = req.method;
|
||||
const headers = Object.assign({ }, req.headers);
|
||||
|
||||
const reqOptions: any = { method, headers };
|
||||
if (options) {
|
||||
if (options.agent) { reqOptions.agent = options.agent; }
|
||||
}
|
||||
|
||||
// Create a Node-specific AbortController, if available
|
||||
let abort: null | AbortController = null;
|
||||
try {
|
||||
abort = new AbortController();
|
||||
reqOptions.abort = abort.signal;
|
||||
} catch (e) { console.log(e); }
|
||||
|
||||
const request = ((protocol === "http") ? http: https).request(req.url, reqOptions);
|
||||
|
||||
request.setTimeout(req.timeout);
|
||||
|
||||
const body = req.body;
|
||||
if (body) { request.write(Buffer.from(body)); }
|
||||
|
||||
request.end();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
if (signal) {
|
||||
signal.addListener(() => {
|
||||
if (abort) { abort.abort(); }
|
||||
reject(makeError("request cancelled", "CANCELLED"));
|
||||
});
|
||||
}
|
||||
|
||||
request.on("timeout", () => {
|
||||
reject(makeError("request timeout", "TIMEOUT"));
|
||||
});
|
||||
|
||||
request.once("response", (resp: http.IncomingMessage) => {
|
||||
const statusCode = resp.statusCode || 0;
|
||||
const statusMessage = resp.statusMessage || "";
|
||||
const headers = Object.keys(resp.headers || {}).reduce((accum, name) => {
|
||||
let value = resp.headers[name] || "";
|
||||
if (Array.isArray(value)) {
|
||||
value = value.join(", ");
|
||||
}
|
||||
accum[name] = value;
|
||||
return accum;
|
||||
}, <{ [ name: string ]: string }>{ });
|
||||
|
||||
let body: null | Uint8Array = null;
|
||||
//resp.setEncoding("utf8");
|
||||
|
||||
resp.on("data", (chunk: Uint8Array) => {
|
||||
if (signal) {
|
||||
try {
|
||||
signal.checkSignal();
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
}
|
||||
}
|
||||
|
||||
if (body == null) {
|
||||
body = chunk;
|
||||
} else {
|
||||
const newBody = new Uint8Array(body.length + chunk.length);
|
||||
newBody.set(body, 0);
|
||||
newBody.set(chunk, body.length);
|
||||
body = newBody;
|
||||
}
|
||||
});
|
||||
|
||||
resp.on("end", () => {
|
||||
try {
|
||||
if (headers["content-encoding"] === "gzip" && body) {
|
||||
body = getBytes(gunzipSync(body));
|
||||
}
|
||||
|
||||
resolve({ statusCode, statusMessage, headers, body });
|
||||
|
||||
} catch (error) {
|
||||
reject(makeError("bad response data", "SERVER_ERROR", {
|
||||
request: req, info: { response: resp, error }
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
resp.on("error", (error) => {
|
||||
//@TODO: Should this just return nornal response with a server error?
|
||||
(<any>error).response = { statusCode, statusMessage, headers, body };
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
|
||||
request.on("error", (error) => { reject(error); });
|
||||
});
|
||||
}
|
||||
|
||||
return getUrl;
|
||||
}
|
||||
|
||||
// @TODO: remove in v7; provided for backwards compat
|
||||
const defaultGetUrl: FetchGetUrlFunc = createGetUrl({ });
|
||||
|
||||
/**
|
||||
* @_ignore:
|
||||
*/
|
||||
export async function getUrl(req: FetchRequest, signal?: FetchCancelSignal): Promise<GetUrlResponse> {
|
||||
return defaultGetUrl(req, signal);
|
||||
}
|
||||
|
||||
95
dev/env/node_modules/ethers/src.ts/utils/index.ts
generated
vendored
Executable file
95
dev/env/node_modules/ethers/src.ts/utils/index.ts
generated
vendored
Executable file
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* There are many simple utilities required to interact with
|
||||
* Ethereum and to simplify the library, without increasing
|
||||
* the library dependencies for simple functions.
|
||||
*
|
||||
* @_section api/utils:Utilities [about-utils]
|
||||
*/
|
||||
|
||||
export { decodeBase58, encodeBase58 } from "./base58.js";
|
||||
|
||||
export { decodeBase64, encodeBase64 } from "./base64.js";
|
||||
|
||||
export {
|
||||
getBytes, getBytesCopy, isHexString, isBytesLike, hexlify, concat, dataLength, dataSlice,
|
||||
stripZerosLeft, zeroPadValue, zeroPadBytes
|
||||
} from "./data.js";
|
||||
|
||||
export {
|
||||
isCallException, isError,
|
||||
assert, assertArgument, assertArgumentCount, assertPrivate, assertNormalize, makeError
|
||||
} from "./errors.js"
|
||||
|
||||
export { EventPayload } from "./events.js";
|
||||
|
||||
export {
|
||||
FetchRequest, FetchResponse, FetchCancelSignal,
|
||||
} from "./fetch.js";
|
||||
|
||||
export { FixedNumber } from "./fixednumber.js"
|
||||
|
||||
export {
|
||||
fromTwos, toTwos, mask,
|
||||
getBigInt, getNumber, getUint, toBigInt, toNumber, toBeHex, toBeArray, toQuantity
|
||||
} from "./maths.js";
|
||||
|
||||
export { resolveProperties, defineProperties} from "./properties.js";
|
||||
|
||||
export { decodeRlp } from "./rlp-decode.js";
|
||||
export { encodeRlp } from "./rlp-encode.js";
|
||||
|
||||
export { formatEther, parseEther, formatUnits, parseUnits } from "./units.js";
|
||||
|
||||
export {
|
||||
toUtf8Bytes,
|
||||
toUtf8CodePoints,
|
||||
toUtf8String,
|
||||
|
||||
Utf8ErrorFuncs,
|
||||
} from "./utf8.js";
|
||||
|
||||
export { uuidV4 } from "./uuid.js";
|
||||
|
||||
/////////////////////////////
|
||||
// Types
|
||||
|
||||
export type { BytesLike } from "./data.js";
|
||||
|
||||
export type {
|
||||
|
||||
//ErrorFetchRequestWithBody, ErrorFetchRequest,
|
||||
//ErrorFetchResponseWithBody, ErrorFetchResponse,
|
||||
|
||||
ErrorCode,
|
||||
|
||||
EthersError, UnknownError, NotImplementedError, UnsupportedOperationError, NetworkError,
|
||||
ServerError, TimeoutError, BadDataError, CancelledError, BufferOverrunError,
|
||||
NumericFaultError, InvalidArgumentError, MissingArgumentError, UnexpectedArgumentError,
|
||||
CallExceptionError, InsufficientFundsError, NonceExpiredError, OffchainFaultError,
|
||||
ReplacementUnderpricedError, TransactionReplacedError, UnconfiguredNameError,
|
||||
ActionRejectedError,
|
||||
|
||||
CallExceptionAction, CallExceptionTransaction,
|
||||
|
||||
CodedEthersError
|
||||
} from "./errors.js"
|
||||
|
||||
export type { EventEmitterable, Listener } from "./events.js";
|
||||
|
||||
export type {
|
||||
GetUrlResponse,
|
||||
FetchPreflightFunc, FetchProcessFunc, FetchRetryFunc,
|
||||
FetchGatewayFunc, FetchGetUrlFunc
|
||||
} from "./fetch.js";
|
||||
|
||||
export type { FixedFormat } from "./fixednumber.js"
|
||||
|
||||
export type { BigNumberish, Numeric } from "./maths.js";
|
||||
|
||||
export type { RlpStructuredData, RlpStructuredDataish } from "./rlp.js";
|
||||
|
||||
export type {
|
||||
Utf8ErrorFunc,
|
||||
UnicodeNormalizationForm,
|
||||
Utf8ErrorReason
|
||||
} from "./utf8.js";
|
||||
259
dev/env/node_modules/ethers/src.ts/utils/maths.ts
generated
vendored
Executable file
259
dev/env/node_modules/ethers/src.ts/utils/maths.ts
generated
vendored
Executable file
@@ -0,0 +1,259 @@
|
||||
/**
|
||||
* Some mathematic operations.
|
||||
*
|
||||
* @_subsection: api/utils:Math Helpers [about-maths]
|
||||
*/
|
||||
import { hexlify, isBytesLike } from "./data.js";
|
||||
import { assert, assertArgument } from "./errors.js";
|
||||
|
||||
import type { BytesLike } from "./data.js";
|
||||
|
||||
/**
|
||||
* Any type that can be used where a numeric value is needed.
|
||||
*/
|
||||
export type Numeric = number | bigint;
|
||||
|
||||
/**
|
||||
* Any type that can be used where a big number is needed.
|
||||
*/
|
||||
export type BigNumberish = string | Numeric;
|
||||
|
||||
|
||||
const BN_0 = BigInt(0);
|
||||
const BN_1 = BigInt(1);
|
||||
|
||||
//const BN_Max256 = (BN_1 << BigInt(256)) - BN_1;
|
||||
|
||||
|
||||
// IEEE 754 support 53-bits of mantissa
|
||||
const maxValue = 0x1fffffffffffff;
|
||||
|
||||
/**
|
||||
* Convert %%value%% from a twos-compliment representation of %%width%%
|
||||
* bits to its value.
|
||||
*
|
||||
* If the highest bit is ``1``, the result will be negative.
|
||||
*/
|
||||
export function fromTwos(_value: BigNumberish, _width: Numeric): bigint {
|
||||
const value = getUint(_value, "value");
|
||||
const width = BigInt(getNumber(_width, "width"));
|
||||
|
||||
assert((value >> width) === BN_0, "overflow", "NUMERIC_FAULT", {
|
||||
operation: "fromTwos", fault: "overflow", value: _value
|
||||
});
|
||||
|
||||
// Top bit set; treat as a negative value
|
||||
if (value >> (width - BN_1)) {
|
||||
const mask = (BN_1 << width) - BN_1;
|
||||
return -(((~value) & mask) + BN_1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert %%value%% to a twos-compliment representation of
|
||||
* %%width%% bits.
|
||||
*
|
||||
* The result will always be positive.
|
||||
*/
|
||||
export function toTwos(_value: BigNumberish, _width: Numeric): bigint {
|
||||
let value = getBigInt(_value, "value");
|
||||
const width = BigInt(getNumber(_width, "width"));
|
||||
|
||||
const limit = (BN_1 << (width - BN_1));
|
||||
|
||||
if (value < BN_0) {
|
||||
value = -value;
|
||||
assert(value <= limit, "too low", "NUMERIC_FAULT", {
|
||||
operation: "toTwos", fault: "overflow", value: _value
|
||||
});
|
||||
const mask = (BN_1 << width) - BN_1;
|
||||
return ((~value) & mask) + BN_1;
|
||||
} else {
|
||||
assert(value < limit, "too high", "NUMERIC_FAULT", {
|
||||
operation: "toTwos", fault: "overflow", value: _value
|
||||
});
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mask %%value%% with a bitmask of %%bits%% ones.
|
||||
*/
|
||||
export function mask(_value: BigNumberish, _bits: Numeric): bigint {
|
||||
const value = getUint(_value, "value");
|
||||
const bits = BigInt(getNumber(_bits, "bits"));
|
||||
return value & ((BN_1 << bits) - BN_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a BigInt from %%value%%. If it is an invalid value for
|
||||
* a BigInt, then an ArgumentError will be thrown for %%name%%.
|
||||
*/
|
||||
export function getBigInt(value: BigNumberish, name?: string): bigint {
|
||||
switch (typeof(value)) {
|
||||
case "bigint": return value;
|
||||
case "number":
|
||||
assertArgument(Number.isInteger(value), "underflow", name || "value", value);
|
||||
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
|
||||
return BigInt(value);
|
||||
case "string":
|
||||
try {
|
||||
if (value === "") { throw new Error("empty string"); }
|
||||
if (value[0] === "-" && value[1] !== "-") {
|
||||
return -BigInt(value.substring(1));
|
||||
}
|
||||
return BigInt(value);
|
||||
} catch(e: any) {
|
||||
assertArgument(false, `invalid BigNumberish string: ${ e.message }`, name || "value", value);
|
||||
}
|
||||
}
|
||||
assertArgument(false, "invalid BigNumberish value", name || "value", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns %%value%% as a bigint, validating it is valid as a bigint
|
||||
* value and that it is positive.
|
||||
*/
|
||||
export function getUint(value: BigNumberish, name?: string): bigint {
|
||||
const result = getBigInt(value, name);
|
||||
assert(result >= BN_0, "unsigned value cannot be negative", "NUMERIC_FAULT", {
|
||||
fault: "overflow", operation: "getUint", value
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
const Nibbles = "0123456789abcdef";
|
||||
|
||||
/*
|
||||
* Converts %%value%% to a BigInt. If %%value%% is a Uint8Array, it
|
||||
* is treated as Big Endian data.
|
||||
*/
|
||||
export function toBigInt(value: BigNumberish | Uint8Array): bigint {
|
||||
if (value instanceof Uint8Array) {
|
||||
let result = "0x0";
|
||||
for (const v of value) {
|
||||
result += Nibbles[v >> 4];
|
||||
result += Nibbles[v & 0x0f];
|
||||
}
|
||||
return BigInt(result);
|
||||
}
|
||||
|
||||
return getBigInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a //number// from %%value%%. If it is an invalid value for
|
||||
* a //number//, then an ArgumentError will be thrown for %%name%%.
|
||||
*/
|
||||
export function getNumber(value: BigNumberish, name?: string): number {
|
||||
switch (typeof(value)) {
|
||||
case "bigint":
|
||||
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
|
||||
return Number(value);
|
||||
case "number":
|
||||
assertArgument(Number.isInteger(value), "underflow", name || "value", value);
|
||||
assertArgument(value >= -maxValue && value <= maxValue, "overflow", name || "value", value);
|
||||
return value;
|
||||
case "string":
|
||||
try {
|
||||
if (value === "") { throw new Error("empty string"); }
|
||||
return getNumber(BigInt(value), name);
|
||||
} catch(e: any) {
|
||||
assertArgument(false, `invalid numeric string: ${ e.message }`, name || "value", value);
|
||||
}
|
||||
}
|
||||
assertArgument(false, "invalid numeric value", name || "value", value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Converts %%value%% to a number. If %%value%% is a Uint8Array, it
|
||||
* is treated as Big Endian data. Throws if the value is not safe.
|
||||
*/
|
||||
export function toNumber(value: BigNumberish | Uint8Array): number {
|
||||
return getNumber(toBigInt(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts %%value%% to a Big Endian hexstring, optionally padded to
|
||||
* %%width%% bytes.
|
||||
*/
|
||||
export function toBeHex(_value: BigNumberish, _width?: Numeric): string {
|
||||
const value = getUint(_value, "value");
|
||||
|
||||
let result = value.toString(16);
|
||||
|
||||
if (_width == null) {
|
||||
// Ensure the value is of even length
|
||||
if (result.length % 2) { result = "0" + result; }
|
||||
} else {
|
||||
const width = getNumber(_width, "width");
|
||||
|
||||
// Special case when both value and width are 0 (see: #5025)
|
||||
if (width === 0 && value === BN_0) { return "0x"; }
|
||||
|
||||
assert(width * 2 >= result.length, `value exceeds width (${ width } bytes)`, "NUMERIC_FAULT", {
|
||||
operation: "toBeHex",
|
||||
fault: "overflow",
|
||||
value: _value
|
||||
});
|
||||
|
||||
// Pad the value to the required width
|
||||
while (result.length < (width * 2)) { result = "0" + result; }
|
||||
|
||||
}
|
||||
|
||||
return "0x" + result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts %%value%% to a Big Endian Uint8Array.
|
||||
*/
|
||||
export function toBeArray(_value: BigNumberish, _width?: Numeric): Uint8Array {
|
||||
const value = getUint(_value, "value");
|
||||
|
||||
if (value === BN_0) {
|
||||
const width = (_width != null) ? getNumber(_width, "width"): 0;
|
||||
return new Uint8Array(width);
|
||||
}
|
||||
|
||||
let hex = value.toString(16);
|
||||
if (hex.length % 2) { hex = "0" + hex; }
|
||||
|
||||
if (_width != null) {
|
||||
const width = getNumber(_width, "width");
|
||||
|
||||
while (hex.length < (width * 2)) { hex = "00" + hex; }
|
||||
|
||||
assert((width * 2) === hex.length, `value exceeds width (${ width } bytes)`, "NUMERIC_FAULT", {
|
||||
operation: "toBeArray",
|
||||
fault: "overflow",
|
||||
value: _value
|
||||
});
|
||||
}
|
||||
|
||||
const result = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
const offset = i * 2;
|
||||
result[i] = parseInt(hex.substring(offset, offset + 2), 16);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[HexString]] for %%value%% safe to use as a //Quantity//.
|
||||
*
|
||||
* A //Quantity// does not have and leading 0 values unless the value is
|
||||
* the literal value `0x0`. This is most commonly used for JSSON-RPC
|
||||
* numeric values.
|
||||
*/
|
||||
export function toQuantity(value: BytesLike | BigNumberish): string {
|
||||
let result = hexlify(isBytesLike(value) ? value: toBeArray(value)).substring(2);
|
||||
while (result.startsWith("0")) { result = result.substring(1); }
|
||||
if (result === "") { result = "0"; }
|
||||
return "0x" + result;
|
||||
}
|
||||
60
dev/env/node_modules/ethers/src.ts/utils/properties.ts
generated
vendored
Executable file
60
dev/env/node_modules/ethers/src.ts/utils/properties.ts
generated
vendored
Executable file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Property helper functions.
|
||||
*
|
||||
* @_subsection api/utils:Properties [about-properties]
|
||||
*/
|
||||
|
||||
function checkType(value: any, type: string, name: string): void {
|
||||
const types = type.split("|").map(t => t.trim());
|
||||
for (let i = 0; i < types.length; i++) {
|
||||
switch (type) {
|
||||
case "any":
|
||||
return;
|
||||
case "bigint":
|
||||
case "boolean":
|
||||
case "number":
|
||||
case "string":
|
||||
if (typeof(value) === type) { return; }
|
||||
}
|
||||
}
|
||||
|
||||
const error: any = new Error(`invalid value for type ${ type }`);
|
||||
error.code = "INVALID_ARGUMENT";
|
||||
error.argument = `value.${ name }`;
|
||||
error.value = value;
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves to a new object that is a copy of %%value%%, but with all
|
||||
* values resolved.
|
||||
*/
|
||||
export async function resolveProperties<T>(value: { [ P in keyof T ]: T[P] | Promise<T[P]>}): Promise<T> {
|
||||
const keys = Object.keys(value);
|
||||
const results = await Promise.all(keys.map((k) => Promise.resolve(value[<keyof T>k])));
|
||||
return results.reduce((accum: any, v, index) => {
|
||||
accum[keys[index]] = v;
|
||||
return accum;
|
||||
}, <{ [ P in keyof T]: T[P] }>{ });
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the %%values%% to %%target%% as read-only values.
|
||||
*
|
||||
* It %%types%% is specified, the values are checked.
|
||||
*/
|
||||
export function defineProperties<T>(
|
||||
target: T,
|
||||
values: { [ K in keyof T ]?: T[K] },
|
||||
types?: { [ K in keyof T ]?: string }): void {
|
||||
|
||||
for (let key in values) {
|
||||
let value = values[key];
|
||||
|
||||
const type = (types ? types[key]: null);
|
||||
if (type) { checkType(value, type, key); }
|
||||
|
||||
Object.defineProperty(target, key, { enumerable: true, value, writable: false });
|
||||
}
|
||||
}
|
||||
104
dev/env/node_modules/ethers/src.ts/utils/rlp-decode.ts
generated
vendored
Executable file
104
dev/env/node_modules/ethers/src.ts/utils/rlp-decode.ts
generated
vendored
Executable file
@@ -0,0 +1,104 @@
|
||||
//See: https://github.com/ethereum/wiki/wiki/RLP
|
||||
|
||||
import { hexlify } from "./data.js";
|
||||
import { assert, assertArgument } from "./errors.js";
|
||||
import { getBytes } from "./data.js";
|
||||
|
||||
import type { BytesLike, RlpStructuredData } from "./index.js";
|
||||
|
||||
|
||||
function hexlifyByte(value: number): string {
|
||||
let result = value.toString(16);
|
||||
while (result.length < 2) { result = "0" + result; }
|
||||
return "0x" + result;
|
||||
}
|
||||
|
||||
function unarrayifyInteger(data: Uint8Array, offset: number, length: number): number {
|
||||
let result = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
result = (result * 256) + data[offset + i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
type Decoded = {
|
||||
result: any;
|
||||
consumed: number;
|
||||
};
|
||||
|
||||
function _decodeChildren(data: Uint8Array, offset: number, childOffset: number, length: number): Decoded {
|
||||
const result: Array<any> = [];
|
||||
|
||||
while (childOffset < offset + 1 + length) {
|
||||
const decoded = _decode(data, childOffset);
|
||||
|
||||
result.push(decoded.result);
|
||||
|
||||
childOffset += decoded.consumed;
|
||||
assert(childOffset <= offset + 1 + length, "child data too short", "BUFFER_OVERRUN", {
|
||||
buffer: data, length, offset
|
||||
});
|
||||
}
|
||||
|
||||
return {consumed: (1 + length), result: result};
|
||||
}
|
||||
|
||||
// returns { consumed: number, result: Object }
|
||||
function _decode(data: Uint8Array, offset: number): { consumed: number, result: any } {
|
||||
assert(data.length !== 0, "data too short", "BUFFER_OVERRUN", {
|
||||
buffer: data, length: 0, offset: 1
|
||||
});
|
||||
|
||||
const checkOffset = (offset: number) => {
|
||||
assert(offset <= data.length, "data short segment too short", "BUFFER_OVERRUN", {
|
||||
buffer: data, length: data.length, offset
|
||||
});
|
||||
};
|
||||
|
||||
// Array with extra length prefix
|
||||
if (data[offset] >= 0xf8) {
|
||||
const lengthLength = data[offset] - 0xf7;
|
||||
checkOffset(offset + 1 + lengthLength);
|
||||
|
||||
const length = unarrayifyInteger(data, offset + 1, lengthLength);
|
||||
checkOffset(offset + 1 + lengthLength + length);
|
||||
|
||||
return _decodeChildren(data, offset, offset + 1 + lengthLength, lengthLength + length);
|
||||
|
||||
} else if (data[offset] >= 0xc0) {
|
||||
const length = data[offset] - 0xc0;
|
||||
checkOffset(offset + 1 + length);
|
||||
|
||||
return _decodeChildren(data, offset, offset + 1, length);
|
||||
|
||||
} else if (data[offset] >= 0xb8) {
|
||||
const lengthLength = data[offset] - 0xb7;
|
||||
checkOffset(offset + 1 + lengthLength);
|
||||
|
||||
const length = unarrayifyInteger(data, offset + 1, lengthLength);
|
||||
checkOffset(offset + 1 + lengthLength + length);
|
||||
|
||||
const result = hexlify(data.slice(offset + 1 + lengthLength, offset + 1 + lengthLength + length));
|
||||
return { consumed: (1 + lengthLength + length), result: result }
|
||||
|
||||
} else if (data[offset] >= 0x80) {
|
||||
const length = data[offset] - 0x80;
|
||||
checkOffset(offset + 1 + length);
|
||||
|
||||
const result = hexlify(data.slice(offset + 1, offset + 1 + length));
|
||||
return { consumed: (1 + length), result: result }
|
||||
}
|
||||
|
||||
return { consumed: 1, result: hexlifyByte(data[offset]) };
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes %%data%% into the structured data it represents.
|
||||
*/
|
||||
export function decodeRlp(_data: BytesLike): RlpStructuredData {
|
||||
const data = getBytes(_data, "data");
|
||||
const decoded = _decode(data, 0);
|
||||
assertArgument(decoded.consumed === data.length, "unexpected junk after rlp payload", "data", _data);
|
||||
return decoded.result;
|
||||
}
|
||||
|
||||
64
dev/env/node_modules/ethers/src.ts/utils/rlp-encode.ts
generated
vendored
Executable file
64
dev/env/node_modules/ethers/src.ts/utils/rlp-encode.ts
generated
vendored
Executable file
@@ -0,0 +1,64 @@
|
||||
//See: https://github.com/ethereum/wiki/wiki/RLP
|
||||
|
||||
import { getBytes } from "./data.js";
|
||||
|
||||
import type { RlpStructuredDataish } from "./rlp.js";
|
||||
|
||||
|
||||
function arrayifyInteger(value: number): Array<number> {
|
||||
const result: Array<number> = [];
|
||||
while (value) {
|
||||
result.unshift(value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _encode(object: Array<any> | string | Uint8Array): Array<number> {
|
||||
if (Array.isArray(object)) {
|
||||
let payload: Array<number> = [];
|
||||
object.forEach(function(child) {
|
||||
payload = payload.concat(_encode(child));
|
||||
});
|
||||
|
||||
if (payload.length <= 55) {
|
||||
payload.unshift(0xc0 + payload.length)
|
||||
return payload;
|
||||
}
|
||||
|
||||
const length = arrayifyInteger(payload.length);
|
||||
length.unshift(0xf7 + length.length);
|
||||
|
||||
return length.concat(payload);
|
||||
|
||||
}
|
||||
|
||||
const data: Array<number> = Array.prototype.slice.call(getBytes(object, "object"));
|
||||
|
||||
if (data.length === 1 && data[0] <= 0x7f) {
|
||||
return data;
|
||||
|
||||
} else if (data.length <= 55) {
|
||||
data.unshift(0x80 + data.length);
|
||||
return data;
|
||||
}
|
||||
|
||||
const length = arrayifyInteger(data.length);
|
||||
length.unshift(0xb7 + length.length);
|
||||
|
||||
return length.concat(data);
|
||||
}
|
||||
|
||||
const nibbles = "0123456789abcdef";
|
||||
|
||||
/**
|
||||
* Encodes %%object%% as an RLP-encoded [[DataHexString]].
|
||||
*/
|
||||
export function encodeRlp(object: RlpStructuredDataish): string {
|
||||
let result = "0x";
|
||||
for (const v of _encode(object)) {
|
||||
result += nibbles[v >> 4];
|
||||
result += nibbles[v & 0xf];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
20
dev/env/node_modules/ethers/src.ts/utils/rlp.ts
generated
vendored
Executable file
20
dev/env/node_modules/ethers/src.ts/utils/rlp.ts
generated
vendored
Executable file
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* The [[link-rlp]] (RLP) encoding is used throughout Ethereum
|
||||
* to serialize nested structures of Arrays and data.
|
||||
*
|
||||
* @_subsection api/utils:Recursive-Length Prefix [about-rlp]
|
||||
*/
|
||||
|
||||
export { decodeRlp } from "./rlp-decode.js";
|
||||
export { encodeRlp } from "./rlp-encode.js";
|
||||
|
||||
/**
|
||||
* An RLP-encoded structure.
|
||||
*/
|
||||
export type RlpStructuredData = string | Array<RlpStructuredData>;
|
||||
|
||||
/**
|
||||
* An RLP-encoded structure, which allows Uint8Array.
|
||||
*/
|
||||
export type RlpStructuredDataish = string | Uint8Array | Array<RlpStructuredDataish>;
|
||||
|
||||
0
dev/env/node_modules/ethers/src.ts/utils/test.txt
generated
vendored
Executable file
0
dev/env/node_modules/ethers/src.ts/utils/test.txt
generated
vendored
Executable file
91
dev/env/node_modules/ethers/src.ts/utils/units.ts
generated
vendored
Executable file
91
dev/env/node_modules/ethers/src.ts/utils/units.ts
generated
vendored
Executable file
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Most interactions with Ethereum requires integer values, which use
|
||||
* the smallest magnitude unit.
|
||||
*
|
||||
* For example, imagine dealing with dollars and cents. Since dollars
|
||||
* are divisible, non-integer values are possible, such as ``$10.77``.
|
||||
* By using the smallest indivisible unit (i.e. cents), the value can
|
||||
* be kept as the integer ``1077``.
|
||||
*
|
||||
* When receiving decimal input from the user (as a decimal string),
|
||||
* the value should be converted to an integer and when showing a user
|
||||
* a value, the integer value should be converted to a decimal string.
|
||||
*
|
||||
* This creates a clear distinction, between values to be used by code
|
||||
* (integers) and values used for display logic to users (decimals).
|
||||
*
|
||||
* The native unit in Ethereum, //ether// is divisible to 18 decimal places,
|
||||
* where each individual unit is called a //wei//.
|
||||
*
|
||||
* @_subsection api/utils:Unit Conversion [about-units]
|
||||
*/
|
||||
import { assertArgument } from "./errors.js";
|
||||
import { FixedNumber } from "./fixednumber.js";
|
||||
import { getNumber } from "./maths.js";
|
||||
|
||||
import type { BigNumberish, Numeric } from "../utils/index.js";
|
||||
|
||||
|
||||
const names = [
|
||||
"wei",
|
||||
"kwei",
|
||||
"mwei",
|
||||
"gwei",
|
||||
"szabo",
|
||||
"finney",
|
||||
"ether",
|
||||
];
|
||||
|
||||
/**
|
||||
* Converts %%value%% into a //decimal string//, assuming %%unit%% decimal
|
||||
* places. The %%unit%% may be the number of decimal places or the name of
|
||||
* a unit (e.g. ``"gwei"`` for 9 decimal places).
|
||||
*
|
||||
*/
|
||||
export function formatUnits(value: BigNumberish, unit?: string | Numeric): string {
|
||||
let decimals = 18;
|
||||
if (typeof(unit) === "string") {
|
||||
const index = names.indexOf(unit);
|
||||
assertArgument(index >= 0, "invalid unit", "unit", unit);
|
||||
decimals = 3 * index;
|
||||
} else if (unit != null) {
|
||||
decimals = getNumber(unit, "unit");
|
||||
}
|
||||
|
||||
return FixedNumber.fromValue(value, decimals, { decimals, width: 512 }).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the //decimal string// %%value%% to a BigInt, assuming
|
||||
* %%unit%% decimal places. The %%unit%% may the number of decimal places
|
||||
* or the name of a unit (e.g. ``"gwei"`` for 9 decimal places).
|
||||
*/
|
||||
export function parseUnits(value: string, unit?: string | Numeric): bigint {
|
||||
assertArgument(typeof(value) === "string", "value must be a string", "value", value);
|
||||
|
||||
let decimals = 18;
|
||||
if (typeof(unit) === "string") {
|
||||
const index = names.indexOf(unit);
|
||||
assertArgument(index >= 0, "invalid unit", "unit", unit);
|
||||
decimals = 3 * index;
|
||||
} else if (unit != null) {
|
||||
decimals = getNumber(unit, "unit");
|
||||
}
|
||||
|
||||
return FixedNumber.fromString(value, { decimals, width: 512 }).value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts %%value%% into a //decimal string// using 18 decimal places.
|
||||
*/
|
||||
export function formatEther(wei: BigNumberish): string {
|
||||
return formatUnits(wei, 18);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the //decimal string// %%ether%% to a BigInt, using 18
|
||||
* decimal places.
|
||||
*/
|
||||
export function parseEther(ether: string): bigint {
|
||||
return parseUnits(ether, 18);
|
||||
}
|
||||
325
dev/env/node_modules/ethers/src.ts/utils/utf8.ts
generated
vendored
Executable file
325
dev/env/node_modules/ethers/src.ts/utils/utf8.ts
generated
vendored
Executable file
@@ -0,0 +1,325 @@
|
||||
/**
|
||||
* Using strings in Ethereum (or any security-basd system) requires
|
||||
* additional care. These utilities attempt to mitigate some of the
|
||||
* safety issues as well as provide the ability to recover and analyse
|
||||
* strings.
|
||||
*
|
||||
* @_subsection api/utils:Strings and UTF-8 [about-strings]
|
||||
*/
|
||||
import { getBytes } from "./data.js";
|
||||
import { assertArgument, assertNormalize } from "./errors.js";
|
||||
|
||||
import type { BytesLike } from "./index.js";
|
||||
|
||||
|
||||
///////////////////////////////
|
||||
|
||||
/**
|
||||
* The stanard normalization forms.
|
||||
*/
|
||||
export type UnicodeNormalizationForm = "NFC" | "NFD" | "NFKC" | "NFKD";
|
||||
|
||||
/**
|
||||
* When using the UTF-8 error API the following errors can be intercepted
|
||||
* and processed as the %%reason%% passed to the [[Utf8ErrorFunc]].
|
||||
*
|
||||
* **``"UNEXPECTED_CONTINUE"``** - a continuation byte was present where there
|
||||
* was nothing to continue.
|
||||
*
|
||||
* **``"BAD_PREFIX"``** - an invalid (non-continuation) byte to start a
|
||||
* UTF-8 codepoint was found.
|
||||
*
|
||||
* **``"OVERRUN"``** - the string is too short to process the expected
|
||||
* codepoint length.
|
||||
*
|
||||
* **``"MISSING_CONTINUE"``** - a missing continuation byte was expected but
|
||||
* not found. The %%offset%% indicates the index the continuation byte
|
||||
* was expected at.
|
||||
*
|
||||
* **``"OUT_OF_RANGE"``** - the computed code point is outside the range
|
||||
* for UTF-8. The %%badCodepoint%% indicates the computed codepoint, which was
|
||||
* outside the valid UTF-8 range.
|
||||
*
|
||||
* **``"UTF16_SURROGATE"``** - the UTF-8 strings contained a UTF-16 surrogate
|
||||
* pair. The %%badCodepoint%% is the computed codepoint, which was inside the
|
||||
* UTF-16 surrogate range.
|
||||
*
|
||||
* **``"OVERLONG"``** - the string is an overlong representation. The
|
||||
* %%badCodepoint%% indicates the computed codepoint, which has already
|
||||
* been bounds checked.
|
||||
*
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
export type Utf8ErrorReason = "UNEXPECTED_CONTINUE" | "BAD_PREFIX" | "OVERRUN" |
|
||||
"MISSING_CONTINUE" | "OUT_OF_RANGE" | "UTF16_SURROGATE" | "OVERLONG";
|
||||
|
||||
|
||||
/**
|
||||
* A callback that can be used with [[toUtf8String]] to analysis or
|
||||
* recovery from invalid UTF-8 data.
|
||||
*
|
||||
* Parsing UTF-8 data is done through a simple Finite-State Machine (FSM)
|
||||
* which calls the ``Utf8ErrorFunc`` if a fault is detected.
|
||||
*
|
||||
* The %%reason%% indicates where in the FSM execution the fault
|
||||
* occurred and the %%offset%% indicates where the input failed.
|
||||
*
|
||||
* The %%bytes%% represents the raw UTF-8 data that was provided and
|
||||
* %%output%% is the current array of UTF-8 code-points, which may
|
||||
* be updated by the ``Utf8ErrorFunc``.
|
||||
*
|
||||
* The value of the %%badCodepoint%% depends on the %%reason%%. See
|
||||
* [[Utf8ErrorReason]] for details.
|
||||
*
|
||||
* The function should return the number of bytes that should be skipped
|
||||
* when control resumes to the FSM.
|
||||
*/
|
||||
export type Utf8ErrorFunc = (reason: Utf8ErrorReason, offset: number, bytes: Uint8Array, output: Array<number>, badCodepoint?: number) => number;
|
||||
|
||||
|
||||
function errorFunc(reason: Utf8ErrorReason, offset: number, bytes: Uint8Array, output: Array<number>, badCodepoint?: number): number {
|
||||
assertArgument(false, `invalid codepoint at offset ${ offset }; ${ reason }`, "bytes", bytes);
|
||||
}
|
||||
|
||||
function ignoreFunc(reason: Utf8ErrorReason, offset: number, bytes: Uint8Array, output: Array<number>, badCodepoint?: number): number {
|
||||
|
||||
// If there is an invalid prefix (including stray continuation), skip any additional continuation bytes
|
||||
if (reason === "BAD_PREFIX" || reason === "UNEXPECTED_CONTINUE") {
|
||||
let i = 0;
|
||||
for (let o = offset + 1; o < bytes.length; o++) {
|
||||
if (bytes[o] >> 6 !== 0x02) { break; }
|
||||
i++;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// This byte runs us past the end of the string, so just jump to the end
|
||||
// (but the first byte was read already read and therefore skipped)
|
||||
if (reason === "OVERRUN") {
|
||||
return bytes.length - offset - 1;
|
||||
}
|
||||
|
||||
// Nothing to skip
|
||||
return 0;
|
||||
}
|
||||
|
||||
function replaceFunc(reason: Utf8ErrorReason, offset: number, bytes: Uint8Array, output: Array<number>, badCodepoint?: number): number {
|
||||
|
||||
// Overlong representations are otherwise "valid" code points; just non-deistingtished
|
||||
if (reason === "OVERLONG") {
|
||||
assertArgument(typeof(badCodepoint) === "number", "invalid bad code point for replacement", "badCodepoint", badCodepoint);
|
||||
output.push(badCodepoint);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Put the replacement character into the output
|
||||
output.push(0xfffd);
|
||||
|
||||
// Otherwise, process as if ignoring errors
|
||||
return ignoreFunc(reason, offset, bytes, output, badCodepoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* A handful of popular, built-in UTF-8 error handling strategies.
|
||||
*
|
||||
* **``"error"``** - throws on ANY illegal UTF-8 sequence or
|
||||
* non-canonical (overlong) codepoints (this is the default)
|
||||
*
|
||||
* **``"ignore"``** - silently drops any illegal UTF-8 sequence
|
||||
* and accepts non-canonical (overlong) codepoints
|
||||
*
|
||||
* **``"replace"``** - replace any illegal UTF-8 sequence with the
|
||||
* UTF-8 replacement character (i.e. ``"\\ufffd"``) and accepts
|
||||
* non-canonical (overlong) codepoints
|
||||
*
|
||||
* @returns: Record<"error" | "ignore" | "replace", Utf8ErrorFunc>
|
||||
*/
|
||||
export const Utf8ErrorFuncs: Readonly<Record<"error" | "ignore" | "replace", Utf8ErrorFunc>> = Object.freeze({
|
||||
error: errorFunc,
|
||||
ignore: ignoreFunc,
|
||||
replace: replaceFunc
|
||||
});
|
||||
|
||||
// http://stackoverflow.com/questions/13356493/decode-utf-8-with-javascript#13691499
|
||||
function getUtf8CodePoints(_bytes: BytesLike, onError?: Utf8ErrorFunc): Array<number> {
|
||||
if (onError == null) { onError = Utf8ErrorFuncs.error; }
|
||||
|
||||
const bytes = getBytes(_bytes, "bytes");
|
||||
|
||||
const result: Array<number> = [];
|
||||
let i = 0;
|
||||
|
||||
// Invalid bytes are ignored
|
||||
while(i < bytes.length) {
|
||||
|
||||
const c = bytes[i++];
|
||||
|
||||
// 0xxx xxxx
|
||||
if (c >> 7 === 0) {
|
||||
result.push(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Multibyte; how many bytes left for this character?
|
||||
let extraLength: null | number = null;
|
||||
let overlongMask: null | number = null;
|
||||
|
||||
// 110x xxxx 10xx xxxx
|
||||
if ((c & 0xe0) === 0xc0) {
|
||||
extraLength = 1;
|
||||
overlongMask = 0x7f;
|
||||
|
||||
// 1110 xxxx 10xx xxxx 10xx xxxx
|
||||
} else if ((c & 0xf0) === 0xe0) {
|
||||
extraLength = 2;
|
||||
overlongMask = 0x7ff;
|
||||
|
||||
// 1111 0xxx 10xx xxxx 10xx xxxx 10xx xxxx
|
||||
} else if ((c & 0xf8) === 0xf0) {
|
||||
extraLength = 3;
|
||||
overlongMask = 0xffff;
|
||||
|
||||
} else {
|
||||
if ((c & 0xc0) === 0x80) {
|
||||
i += onError("UNEXPECTED_CONTINUE", i - 1, bytes, result);
|
||||
} else {
|
||||
i += onError("BAD_PREFIX", i - 1, bytes, result);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Do we have enough bytes in our data?
|
||||
if (i - 1 + extraLength >= bytes.length) {
|
||||
i += onError("OVERRUN", i - 1, bytes, result);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the length prefix from the char
|
||||
let res: null | number = c & ((1 << (8 - extraLength - 1)) - 1);
|
||||
|
||||
for (let j = 0; j < extraLength; j++) {
|
||||
let nextChar = bytes[i];
|
||||
|
||||
// Invalid continuation byte
|
||||
if ((nextChar & 0xc0) != 0x80) {
|
||||
i += onError("MISSING_CONTINUE", i, bytes, result);
|
||||
res = null;
|
||||
break;
|
||||
};
|
||||
|
||||
res = (res << 6) | (nextChar & 0x3f);
|
||||
i++;
|
||||
}
|
||||
|
||||
// See above loop for invalid continuation byte
|
||||
if (res === null) { continue; }
|
||||
|
||||
// Maximum code point
|
||||
if (res > 0x10ffff) {
|
||||
i += onError("OUT_OF_RANGE", i - 1 - extraLength, bytes, result, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reserved for UTF-16 surrogate halves
|
||||
if (res >= 0xd800 && res <= 0xdfff) {
|
||||
i += onError("UTF16_SURROGATE", i - 1 - extraLength, bytes, result, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for overlong sequences (more bytes than needed)
|
||||
if (res <= overlongMask) {
|
||||
i += onError("OVERLONG", i - 1 - extraLength, bytes, result, res);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.push(res);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 byte representation of %%str%%.
|
||||
*
|
||||
* If %%form%% is specified, the string is normalized.
|
||||
*/
|
||||
export function toUtf8Bytes(str: string, form?: UnicodeNormalizationForm): Uint8Array {
|
||||
assertArgument(typeof(str) === "string", "invalid string value", "str", str);
|
||||
|
||||
if (form != null) {
|
||||
assertNormalize(form);
|
||||
str = str.normalize(form);
|
||||
}
|
||||
|
||||
let result: Array<number> = [];
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const c = str.charCodeAt(i);
|
||||
|
||||
if (c < 0x80) {
|
||||
result.push(c);
|
||||
|
||||
} else if (c < 0x800) {
|
||||
result.push((c >> 6) | 0xc0);
|
||||
result.push((c & 0x3f) | 0x80);
|
||||
|
||||
} else if ((c & 0xfc00) == 0xd800) {
|
||||
i++;
|
||||
const c2 = str.charCodeAt(i);
|
||||
|
||||
assertArgument(i < str.length && ((c2 & 0xfc00) === 0xdc00),
|
||||
"invalid surrogate pair", "str", str);
|
||||
|
||||
// Surrogate Pair
|
||||
const pair = 0x10000 + ((c & 0x03ff) << 10) + (c2 & 0x03ff);
|
||||
result.push((pair >> 18) | 0xf0);
|
||||
result.push(((pair >> 12) & 0x3f) | 0x80);
|
||||
result.push(((pair >> 6) & 0x3f) | 0x80);
|
||||
result.push((pair & 0x3f) | 0x80);
|
||||
|
||||
} else {
|
||||
result.push((c >> 12) | 0xe0);
|
||||
result.push(((c >> 6) & 0x3f) | 0x80);
|
||||
result.push((c & 0x3f) | 0x80);
|
||||
}
|
||||
}
|
||||
|
||||
return new Uint8Array(result);
|
||||
};
|
||||
|
||||
//export
|
||||
function _toUtf8String(codePoints: Array<number>): string {
|
||||
return codePoints.map((codePoint) => {
|
||||
if (codePoint <= 0xffff) {
|
||||
return String.fromCharCode(codePoint);
|
||||
}
|
||||
codePoint -= 0x10000;
|
||||
return String.fromCharCode(
|
||||
(((codePoint >> 10) & 0x3ff) + 0xd800),
|
||||
((codePoint & 0x3ff) + 0xdc00)
|
||||
);
|
||||
}).join("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string represented by the UTF-8 data %%bytes%%.
|
||||
*
|
||||
* When %%onError%% function is specified, it is called on UTF-8
|
||||
* errors allowing recovery using the [[Utf8ErrorFunc]] API.
|
||||
* (default: [error](Utf8ErrorFuncs))
|
||||
*/
|
||||
export function toUtf8String(bytes: BytesLike, onError?: Utf8ErrorFunc): string {
|
||||
return _toUtf8String(getUtf8CodePoints(bytes, onError));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTF-8 code-points for %%str%%.
|
||||
*
|
||||
* If %%form%% is specified, the string is normalized.
|
||||
*/
|
||||
export function toUtf8CodePoints(str: string, form?: UnicodeNormalizationForm): Array<number> {
|
||||
return getUtf8CodePoints(toUtf8Bytes(str, form));
|
||||
}
|
||||
|
||||
36
dev/env/node_modules/ethers/src.ts/utils/uuid.ts
generated
vendored
Executable file
36
dev/env/node_modules/ethers/src.ts/utils/uuid.ts
generated
vendored
Executable file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Explain UUID and link to RFC here.
|
||||
*
|
||||
* @_subsection: api/utils:UUID [about-uuid]
|
||||
*/
|
||||
import { getBytes, hexlify } from "./data.js";
|
||||
|
||||
import type { BytesLike } from "./index.js";
|
||||
|
||||
/**
|
||||
* Returns the version 4 [[link-uuid]] for the %%randomBytes%%.
|
||||
*
|
||||
* @see: https://www.ietf.org/rfc/rfc4122.txt (Section 4.4)
|
||||
*/
|
||||
export function uuidV4(randomBytes: BytesLike): string {
|
||||
const bytes = getBytes(randomBytes, "randomBytes");
|
||||
|
||||
// Section: 4.1.3:
|
||||
// - time_hi_and_version[12:16] = 0b0100
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40;
|
||||
|
||||
// Section 4.4
|
||||
// - clock_seq_hi_and_reserved[6] = 0b0
|
||||
// - clock_seq_hi_and_reserved[7] = 0b1
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
||||
|
||||
const value = hexlify(bytes);
|
||||
|
||||
return [
|
||||
value.substring(2, 10),
|
||||
value.substring(10, 14),
|
||||
value.substring(14, 18),
|
||||
value.substring(18, 22),
|
||||
value.substring(22, 34),
|
||||
].join("-");
|
||||
}
|
||||
Reference in New Issue
Block a user