From a74b9a557ce96e357c0370020dbee32bf06bc5b4 Mon Sep 17 00:00:00 2001 From: Richard Moore Date: Fri, 2 Dec 2022 21:23:13 -0500 Subject: [PATCH] docs: added more jsdocs --- src.ts/address/address.ts | 28 ++++ src.ts/address/checks.ts | 17 ++- src.ts/address/index.ts | 38 +++++- src.ts/crypto/hmac.ts | 12 ++ src.ts/crypto/index.ts | 5 +- src.ts/crypto/keccak.ts | 6 + src.ts/crypto/pbkdf2.ts | 15 +++ src.ts/crypto/random.ts | 11 ++ src.ts/crypto/ripemd160.ts | 1 + src.ts/crypto/scrypt.ts | 40 +++++- src.ts/crypto/sha2.ts | 2 + src.ts/crypto/signing-key.ts | 60 ++++++++- src.ts/ethers.ts | 6 +- src.ts/providers/community.ts | 21 ++- src.ts/providers/provider.ts | 2 +- src.ts/transaction/accesslist.ts | 3 + src.ts/transaction/index.ts | 22 ++- src.ts/transaction/transaction.ts | 214 +++++++++++++++++++++++++++--- src.ts/utils/base58.ts | 2 +- src.ts/utils/base64.ts | 4 +- src.ts/utils/data.ts | 2 +- src.ts/utils/errors.ts | 2 +- src.ts/utils/events.ts | 2 +- src.ts/utils/fetch.ts | 2 +- src.ts/utils/fixednumber.ts | 2 +- src.ts/utils/maths.ts | 2 +- src.ts/utils/properties.ts | 28 ++-- src.ts/utils/rlp.ts | 2 +- src.ts/utils/test.txt | 0 src.ts/utils/units.ts | 2 +- src.ts/utils/utf8.ts | 2 +- src.ts/wallet/hdwallet.ts | 181 ++++++++++++++++++++++++- src.ts/wallet/index.ts | 1 - src.ts/wallet/mnemonic.ts | 48 +++++++ src.ts/wallet/wallet.ts | 27 ++++ src.ts/wordlists/index.ts | 2 + src.ts/wordlists/lang-cz.ts | 6 +- src.ts/wordlists/lang-en.ts | 2 +- src.ts/wordlists/lang-es.ts | 2 +- src.ts/wordlists/lang-fr.ts | 2 +- src.ts/wordlists/lang-it.ts | 2 +- src.ts/wordlists/lang-ja.ts | 2 +- src.ts/wordlists/lang-ko.ts | 2 +- src.ts/wordlists/lang-pt.ts | 2 +- src.ts/wordlists/lang-zh.ts | 10 +- 45 files changed, 763 insertions(+), 81 deletions(-) create mode 100644 src.ts/utils/test.txt diff --git a/src.ts/address/address.ts b/src.ts/address/address.ts index b28f63d37..7115e5971 100644 --- a/src.ts/address/address.ts +++ b/src.ts/address/address.ts @@ -81,6 +81,27 @@ function fromBase36(value: string): bigint { return result; } +/** + * Returns a normalized and checksumed address for %%address%%. + * This accepts non-checksum addresses, checksum addresses and + * [[getIcapAddress]] formats. + * + * The checksum in Ethereum uses the capitalization (upper-case + * vs lower-case) of the characters within an address to encode + * its checksum, which offers, on average, a checksum of 15-bits. + * + * If %%address%% contains both upper-case and lower-case, it is + * assumed to already be a checksum address and its checksum is + * validated, and if the address fails its expected checksum an + * error is thrown. + * + * If you wish the checksum of %%address%% to be ignore, it should + * be converted to lower-case (i.e. ``.toLowercase()``) before + * being passed in. This should be a very rare situation though, + * that you wish to bypass the safegaurds in place to protect + * against an address that has been incorrectly copied from another + * source. + */ export function getAddress(address: string): string { assertArgument(typeof(address) === "string", "invalid address", "address", address); @@ -112,6 +133,13 @@ export function getAddress(address: string): string { assertArgument(false, "invalid address", "address", address); } +/** + * The [ICAP Address format](link-icap) format is an early checksum + * format which attempts to be compatible with the banking + * industry [IBAN format](link-wiki-iban] for bank accounts. + * + * It is no longer common or a recommended format. + */ export function getIcapAddress(address: string): string { //let base36 = _base16To36(getAddress(address).substring(2)).toUpperCase(); let base36 = BigInt(getAddress(address)).toString(36).toUpperCase(); diff --git a/src.ts/address/checks.ts b/src.ts/address/checks.ts index c5492aeea..49b8ac496 100644 --- a/src.ts/address/checks.ts +++ b/src.ts/address/checks.ts @@ -5,10 +5,17 @@ import { getAddress } from "./address.js"; import type { Addressable, AddressLike, NameResolver } from "./index.js"; +/** + * Returns true if %%value%% is an object which implements the + * [[Addressable]] interface. + */ export function isAddressable(value: any): value is Addressable { return (value && typeof(value.getAddress) === "function"); } +/** + * Returns true if %%value%% is a valid address. + */ export function isAddress(value: any): boolean { try { getAddress(value); @@ -26,8 +33,14 @@ async function checkAddress(target: any, promise: Promise): Promi return getAddress(result); } -// Resolves an Ethereum address, ENS name or Addressable object, -// throwing if the result is null. +/** + * Resolves to an address for the %%target%%, which may be any + * supported address type, an [[Addressable]] or a Promise which + * resolves to an address. + * + * If an ENS name is provided, but that name has not been correctly + * configured a [[UnconfiguredNameError]] is thrown. + */ export function resolveAddress(target: AddressLike, resolver?: null | NameResolver): string | Promise { if (typeof(target) === "string") { diff --git a/src.ts/address/index.ts b/src.ts/address/index.ts index c572c99ab..f148aed99 100644 --- a/src.ts/address/index.ts +++ b/src.ts/address/index.ts @@ -1,17 +1,51 @@ /** - * Addresses in Ethereum can be of several formats. These functions - * help convert between them, checksum them, etc. + * Addresses are a fundamental part of interacting with Ethereum. They + * represent the gloabal identity of Externally Owned Accounts (accounts + * backed by a private key) and contracts. + * + * The Ethereum Naming Service (ENS) provides an interconnected ecosystem + * of contracts, standards and libraries which enable looking up an + * address for an ENS name. + * + * These functions help convert between various formats, validate + * addresses and safely resolve ENS names. * * @_section: api/address:Addresses [addresses] */ +null; + +/** + * An interface for objects which have an address, and can + * resolve it asyncronously. + * + * This allows objects such as [[Signer]] or [[Contract]] to + * be used most places an address can be, for example getting + * the [balance](Provider-getBalance). + */ export interface Addressable { + /** + * Get the object address. + */ getAddress(): Promise; } +/** + * Anything that can be used to return or resolve an address. + */ export type AddressLike = string | Promise | Addressable; +/** + * An interface for any object which can resolve an ENS name. + */ export interface NameResolver { + /** + * Resolve to the address for the ENS %%name%%. + * + * Resolves to ``null`` if the name is unconfigued. Use + * [[resolveAddress]] (passing this object as %%resolver%%) to + * throw for names that are unconfigured. + */ resolveName(name: string): Promise; } diff --git a/src.ts/crypto/hmac.ts b/src.ts/crypto/hmac.ts index b85f1ea73..763bc5337 100644 --- a/src.ts/crypto/hmac.ts +++ b/src.ts/crypto/hmac.ts @@ -1,3 +1,11 @@ +/** + * An **HMAC** enables verification that a given key was used + * to authenticate a payload. + * + * See: [[link-wiki-hmac]] + * + * @_subsection: api/crypto:HMAC [about-hmac] + */ import { createHmac } from "./crypto.js"; import { getBytes, hexlify } from "../utils/index.js"; @@ -12,6 +20,10 @@ const _computeHmac = function(algorithm: "sha256" | "sha512", key: Uint8Array, d let __computeHmac = _computeHmac; +/** + * Return the HMAC for %%data%% using the %%key%% key with the underlying + * %%algo%% used for compression. + */ export function computeHmac(algorithm: "sha256" | "sha512", _key: BytesLike, _data: BytesLike): string { const key = getBytes(_key, "key"); const data = getBytes(_data, "data"); diff --git a/src.ts/crypto/index.ts b/src.ts/crypto/index.ts index 11044fd45..e8cb26fad 100644 --- a/src.ts/crypto/index.ts +++ b/src.ts/crypto/index.ts @@ -1,9 +1,12 @@ /** - * About Crypto? + * A fundamental building block of Ethereum is the underlying + * cryptographic primitives. * * @_section: api/crypto:Cryptographic Functions [crypto] */ +null + // We import all these so we can export lock() import { computeHmac } from "./hmac.js"; import { keccak256 } from "./keccak.js"; diff --git a/src.ts/crypto/keccak.ts b/src.ts/crypto/keccak.ts index eb8ba6c69..171724814 100644 --- a/src.ts/crypto/keccak.ts +++ b/src.ts/crypto/keccak.ts @@ -1,3 +1,9 @@ +/** + * Cryptographic hashing functions + * + * @_subsection: api/crypto:Hash Functions [about-crypto-hashing] + */ + import { keccak_256 } from "@noble/hashes/sha3"; import { getBytes, hexlify } from "../utils/index.js"; diff --git a/src.ts/crypto/pbkdf2.ts b/src.ts/crypto/pbkdf2.ts index 729158e72..825d57451 100644 --- a/src.ts/crypto/pbkdf2.ts +++ b/src.ts/crypto/pbkdf2.ts @@ -1,3 +1,11 @@ +/** + * A **Password-Based Key-Derivation Function** is designed to create + * a sequence of bytes suitible as a **key** from a human-rememberable + * password. + * + * @_subsection: api/crypto:Passwords [about-pbkdf] + */ + import { pbkdf2Sync } from "./crypto.js"; import { getBytes, hexlify } from "../utils/index.js"; @@ -13,6 +21,13 @@ const _pbkdf2 = function(password: Uint8Array, salt: Uint8Array, iterations: num let __pbkdf2 = _pbkdf2; +/** + * Return the [[link-pbkdf2]] for %%keylen%% bytes for %%password%% using + * the %%salt%% and using %%iterations%% of %%algo%%. + * + * This PBKDF is outdated and should not be used in new projects, but is + * required to decrypt older files. + */ export function pbkdf2(_password: BytesLike, _salt: BytesLike, iterations: number, keylen: number, algo: "sha256" | "sha512"): string { const password = getBytes(_password, "password"); const salt = getBytes(_salt, "salt"); diff --git a/src.ts/crypto/random.ts b/src.ts/crypto/random.ts index 90daff63d..0dcd84f46 100644 --- a/src.ts/crypto/random.ts +++ b/src.ts/crypto/random.ts @@ -1,3 +1,11 @@ +/** + * A **Cryptographically Secure Random Value** is one that has been + * generated with additional care take to prevent side-channels + * from allowing others to detect it and prevent others from through + * coincidence generate the same values. + * + * @_subsection: api/crypto:Random Values [about-crypto-random] + */ import { randomBytes as crypto_random } from "./crypto.js"; let locked = false; @@ -8,6 +16,9 @@ const _randomBytes = function(length: number): Uint8Array { let __randomBytes = _randomBytes; +/** + * Return %%length%% bytes of cryptographically secure random data. + */ export function randomBytes(length: number): Uint8Array { return __randomBytes(length); } diff --git a/src.ts/crypto/ripemd160.ts b/src.ts/crypto/ripemd160.ts index 52c4e4908..5069c2d1e 100644 --- a/src.ts/crypto/ripemd160.ts +++ b/src.ts/crypto/ripemd160.ts @@ -16,6 +16,7 @@ let __ripemd160: (data: Uint8Array) => BytesLike = _ripemd160; /** * Compute the cryptographic RIPEMD-160 hash of %%data%%. * + * @_docloc: api/crypto:Hash Functions * @returns DataHexstring */ export function ripemd160(_data: BytesLike): string { diff --git a/src.ts/crypto/scrypt.ts b/src.ts/crypto/scrypt.ts index 8170a3817..0dad1b3ea 100644 --- a/src.ts/crypto/scrypt.ts +++ b/src.ts/crypto/scrypt.ts @@ -4,7 +4,12 @@ import { getBytes, hexlify as H } from "../utils/index.js"; import type { BytesLike } from "../utils/index.js"; - +/** + * A callback during long-running operations to update any + * UI or provide programatic access to the progress. + * + * The %%percent%% is a value between ``0`` and ``1``. + */ export type ProgressCallback = (percent: number) => void; @@ -21,6 +26,30 @@ let __scryptAsync: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, let __scryptSync: (passwd: Uint8Array, salt: Uint8Array, N: number, r: number, p: number, dkLen: number) => BytesLike = _scryptSync +/** + * The [[link-wiki-scrypt]] uses a memory and cpu hard method of + * derivation to increase the resource cost to brute-force a password + * for a given key. + * + * This means this algorithm is intentionally slow, and can be tuned to + * become slower. As computation and memory speed improve over time, + * increasing the difficulty maintains the cost of an attacker. + * + * For example, if a target time of 5 seconds is used, a legitimate user + * which knows their password requires only 5 seconds to unlock their + * account. A 6 character password has 68 billion possibilities, which + * would require an attacker to invest over 10,000 years of CPU time. This + * is of course a crude example (as password generally aren't random), + * but demonstrates to value of imposing large costs to decryption. + * + * For this reason, if building a UI which involved decrypting or + * encrypting datsa using scrypt, it is recommended to use a + * [[ProgressCallback]] (as event short periods can seem lik an eternity + * if the UI freezes). Including the phrase //"decrypting"// in the UI + * can also help, assuring the user their waiting is for a good reason. + * + * @_docloc: api/crypto:Passwords + */ export async function scrypt(_passwd: BytesLike, _salt: BytesLike, N: number, r: number, p: number, dkLen: number, progress?: ProgressCallback): Promise { const passwd = getBytes(_passwd, "passwd"); const salt = getBytes(_salt, "salt"); @@ -34,6 +63,15 @@ scrypt.register = function(func: (passwd: Uint8Array, salt: Uint8Array, N: numbe } Object.freeze(scrypt); +/** + * Provides a synchronous variant of [[scrypt]]. + * + * This will completely lock up and freeze the UI in a browser and will + * prevent any event loop from progressing. For this reason, it is + * preferred to use the [async variant](scrypt). + * + * @_docloc: api/crypto:Passwords + */ export function scryptSync(_passwd: BytesLike, _salt: BytesLike, N: number, r: number, p: number, dkLen: number): string { const passwd = getBytes(_passwd, "passwd"); const salt = getBytes(_salt, "salt"); diff --git a/src.ts/crypto/sha2.ts b/src.ts/crypto/sha2.ts index c799def09..95c5dd032 100644 --- a/src.ts/crypto/sha2.ts +++ b/src.ts/crypto/sha2.ts @@ -22,6 +22,7 @@ let locked256 = false, locked512 = false; /** * Compute the cryptographic SHA2-256 hash of %%data%%. * + * @_docloc: api/crypto:Hash Functions * @returns DataHexstring */ export function sha256(_data: BytesLike): string { @@ -40,6 +41,7 @@ Object.freeze(sha256); /** * Compute the cryptographic SHA2-512 hash of %%data%%. * + * @_docloc: api/crypto:Hash Functions * @returns DataHexstring */ export function sha512(_data: BytesLike): string { diff --git a/src.ts/crypto/signing-key.ts b/src.ts/crypto/signing-key.ts index b00e983d4..a7f65d515 100644 --- a/src.ts/crypto/signing-key.ts +++ b/src.ts/crypto/signing-key.ts @@ -21,18 +21,46 @@ secp256k1.utils.hmacSha256Sync = function(key: Uint8Array, ...messages: Array = new Set(); + +/** + * Displays a warning in tht console when the community resource is + * being used too heavily by the app, recommending the developer + * acquire their own credentials instead of using the community + * credentials. + * + * The notification will only occur once per service. + */ export function showThrottleMessage(service: string): void { if (shown.has(service)) { return; } shown.add(service); diff --git a/src.ts/providers/provider.ts b/src.ts/providers/provider.ts index d1c65a5bc..3dbae7970 100644 --- a/src.ts/providers/provider.ts +++ b/src.ts/providers/provider.ts @@ -424,7 +424,7 @@ export class Block implements BlockParams, Iterable { get length(): number { return this.#transactions.length; } /** - * The [date](link-js-data) this block was included at. + * The [[link-js-date]] this block was included at. */ get date(): null | Date { if (this.timestamp == null) { return null; } diff --git a/src.ts/transaction/accesslist.ts b/src.ts/transaction/accesslist.ts index 328dbc58e..1b4ddf3c9 100644 --- a/src.ts/transaction/accesslist.ts +++ b/src.ts/transaction/accesslist.ts @@ -14,6 +14,9 @@ function accessSetify(addr: string, storageKeys: Array): { address: stri }; } +/** + * Returns a [[AccessList]] from any ethers-supported access-list structure. + */ export function accessListify(value: AccessListish): AccessList { if (Array.isArray(value)) { return (] | { address: string, storageKeys: Array}>>value).map((set, index) => { diff --git a/src.ts/transaction/index.ts b/src.ts/transaction/index.ts index 56515c613..89458e43a 100644 --- a/src.ts/transaction/index.ts +++ b/src.ts/transaction/index.ts @@ -4,13 +4,21 @@ * @_section api/transaction:Transactions [transactions] */ -/** - * Moo - */ -export type AccessListSet = { address: string, storageKeys: Array }; -export type AccessList = Array; +null; -// Input allows flexibility in describing an access list +/** + * A single [[AccessList]] entry of storage keys (slots) for an address. + */ +export type AccessListEntry = { address: string, storageKeys: Array }; + +/** + * An ordered collection of [[AccessList]] entries. + */ +export type AccessList = Array; + +/** + * Any ethers-supported access list structure. + */ export type AccessListish = AccessList | Array<[ string, Array ]> | Record>; @@ -20,4 +28,4 @@ export { accessListify } from "./accesslist.js"; export { computeAddress, recoverAddress } from "./address.js"; export { Transaction } from "./transaction.js"; -export type { SignedTransaction, TransactionLike } from "./transaction.js"; +export type { TransactionLike } from "./transaction.js"; diff --git a/src.ts/transaction/transaction.ts b/src.ts/transaction/transaction.ts index 52e40cb38..a874a881b 100644 --- a/src.ts/transaction/transaction.ts +++ b/src.ts/transaction/transaction.ts @@ -23,27 +23,74 @@ const BN_35 = BigInt(35); const BN_MAX_UINT = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); export interface TransactionLike { + /** + * The type. + */ type?: null | number; + /** + * The recipient address or ``null`` for an ``init`` transaction. + */ to?: null | A; + + /** + * The sender. + */ from?: null | A; + /** + * The nonce. + */ nonce?: null | number; + /** + * The maximum amount of gas that can be used. + */ gasLimit?: null | BigNumberish; + + /** + * The gas price for legacy and berlin transactions. + */ gasPrice?: null | BigNumberish; + /** + * The maximum priority fee per gas for london transactions. + */ maxPriorityFeePerGas?: null | BigNumberish; + + /** + * The maximum total fee per gas for london transactions. + */ maxFeePerGas?: null | BigNumberish; + /** + * The data. + */ data?: null | string; + + /** + * The value (in wei) to send. + */ value?: null | BigNumberish; + + /** + * The chain ID the transaction is valid on. + */ chainId?: null | BigNumberish; + /** + * The transaction hash. + */ hash?: null | string; + /** + * The signature provided by the sender. + */ signature?: null | SignatureLike; + /** + * The access list for berlin and london transactions. + */ accessList?: null | AccessListish; } @@ -305,14 +352,24 @@ function _serializeEip2930(tx: TransactionLike, sig?: Signature): string { return concat([ "0x01", encodeRlp(fields)]); } +/** + * A transactions which has been signed. + */ + /* export interface SignedTransaction extends Transaction { type: number; typeName: string; from: string; signature: Signature; } +*/ - +/** + * A **Transaction** describes an operation to be executed on + * Ethereum by an Externally Owned Account (EOA). It includes + * who (the [[to]] address), what (the [[data]]) and how much (the + * [[value]] in ether) the operation should entail. + */ export class Transaction implements TransactionLike { #type: null | number; #to: null | string; @@ -327,17 +384,13 @@ export class Transaction implements TransactionLike { #sig: null | Signature; #accessList: null | AccessList; - // A type of null indicates the type will be populated automatically + /** + * The transaction type. + * + * If null, the type will be automatically inferred based on + * explicit properties. + */ get type(): null | number { return this.#type; } - get typeName(): null | string { - switch (this.type) { - case 0: return "legacy"; - case 1: return "eip-2930"; - case 2: return "eip-1559"; - } - - return null; - } set type(value: null | number | string) { switch (value) { case null: @@ -357,17 +410,46 @@ export class Transaction implements TransactionLike { } } + /** + * The name of the transaction type. + */ + get typeName(): null | string { + switch (this.type) { + case 0: return "legacy"; + case 1: return "eip-2930"; + case 2: return "eip-1559"; + } + + return null; + } + + /** + * The ``to`` address for the transaction or ``null`` if the + * transaction is an ``init`` transaction. + */ get to(): null | string { return this.#to; } set to(value: null | string) { this.#to = (value == null) ? null: getAddress(value); } + /** + * The transaction nonce. + */ get nonce(): number { return this.#nonce; } set nonce(value: BigNumberish) { this.#nonce = getNumber(value, "value"); } + /** + * The gas limit. + */ get gasLimit(): bigint { return this.#gasLimit; } set gasLimit(value: BigNumberish) { this.#gasLimit = getBigInt(value); } + /** + * The gas price. + * + * On legacy networks this defines the fee that will be paid. On + * EIP-1559 networks, this should be ``null``. + */ get gasPrice(): null | bigint { const value = this.#gasPrice; if (value == null && (this.type === 0 || this.type === 1)) { return BN_0; } @@ -377,6 +459,10 @@ export class Transaction implements TransactionLike { this.#gasPrice = (value == null) ? null: getBigInt(value, "gasPrice"); } + /** + * The maximum priority fee per unit of gas to pay. On legacy + * networks this should be ``null``. + */ get maxPriorityFeePerGas(): null | bigint { const value = this.#maxPriorityFeePerGas; if (value == null) { @@ -389,6 +475,10 @@ export class Transaction implements TransactionLike { this.#maxPriorityFeePerGas = (value == null) ? null: getBigInt(value, "maxPriorityFeePerGas"); } + /** + * The maximum total fee per unit of gas to pay. On legacy + * networks this should be ``null``. + */ get maxFeePerGas(): null | bigint { const value = this.#maxFeePerGas; if (value == null) { @@ -401,22 +491,41 @@ export class Transaction implements TransactionLike { this.#maxFeePerGas = (value == null) ? null: getBigInt(value, "maxFeePerGas"); } + /** + * The transaction data. For ``init`` transactions this is the + * deployment code. + */ get data(): string { return this.#data; } set data(value: BytesLike) { this.#data = hexlify(value); } + /** + * The amount of ether to send in this transactions. + */ get value(): bigint { return this.#value; } set value(value: BigNumberish) { this.#value = getBigInt(value, "value"); } + /** + * The chain ID this transaction is valid on. + */ get chainId(): bigint { return this.#chainId; } set chainId(value: BigNumberish) { this.#chainId = getBigInt(value); } + /** + * If signed, the signature for this transaction. + */ get signature(): null | Signature { return this.#sig || null; } set signature(value: null | SignatureLike) { this.#sig = (value == null) ? null: Signature.from(value); } + /** + * The access list. + * + * An access list permits discounted (but pre-paid) access to + * bytecode and state variable access within contract execution. + */ get accessList(): null | AccessList { const value = this.#accessList || null; if (value == null) { @@ -429,6 +538,9 @@ export class Transaction implements TransactionLike { this.#accessList = (value == null) ? null: accessListify(value); } + /** + * Creates a new Transaction with default values. + */ constructor() { this.#type = null; this.#to = null; @@ -444,29 +556,57 @@ export class Transaction implements TransactionLike { this.#accessList = null; } + /** + * The transaction hash, if signed. Otherwise, ``null``. + */ get hash(): null | string { if (this.signature == null) { return null; } return keccak256(this.serialized); } + /** + * The pre-image hash of this transaction. + * + * This is the digest that a [[Signer]] must sign to authorize + * this transaction. + */ get unsignedHash(): string { return keccak256(this.unsignedSerialized); } + /** + * The sending address, if signed. Otherwise, ``null``. + */ get from(): null | string { if (this.signature == null) { return null; } return recoverAddress(this.unsignedHash, this.signature); } + /** + * The public key of the sender, if signed. Otherwise, ``null``. + */ get fromPublicKey(): null | string { if (this.signature == null) { return null; } return SigningKey.recoverPublicKey(this.unsignedHash, this.signature); } - isSigned(): this is SignedTransaction { + /** + * Returns true if signed. + * + * This provides a Type Guard that properties requiring a signed + * transaction are non-null. + */ + isSigned(): this is (Transaction & { type: number, typeName: string, from: string, signature: Signature }) { + //isSigned(): this is SignedTransaction { return this.signature != null; } + /** + * The serialized transaction. + * + * This throws if the transaction is unsigned. For the pre-image, + * use [[unsignedSerialized]]. + */ get serialized(): string { assert(this.signature != null, "cannot serialize unsigned transaction; maybe you meant .unsignedSerialized", "UNSUPPORTED_OPERATION", { operation: ".serialized"}); @@ -482,6 +622,12 @@ export class Transaction implements TransactionLike { assert(false, "unsupported transaction type", "UNSUPPORTED_OPERATION", { operation: ".serialized" }); } + /** + * The transaction pre-image. + * + * The hash of this is the digest which needs to be signed to + * authorize this transaction. + */ get unsignedSerialized(): string { switch (this.inferType()) { case 0: @@ -497,13 +643,16 @@ export class Transaction implements TransactionLike { /** * Return the most "likely" type; currently the highest - * supported transaction type + * supported transaction type. */ inferType(): number { return (this.inferTypes().pop()); } - // Validates properties and lists possible types this transaction adheres to + /** + * Validates the explicit properties and returns a list of compatible + * transaction types. + */ inferTypes(): Array { // Checks that there are no conflicting properties set @@ -553,20 +702,49 @@ export class Transaction implements TransactionLike { return types; } + /** + * Returns true if this transaction is a legacy transaction (i.e. + * ``type === 0``). + * + * This provides a Type Guard that the related properties are + * non-null. + */ isLegacy(): this is (Transaction & { type: 0, gasPrice: bigint }) { return (this.type === 0); } + + /** + * Returns true if this transaction is berlin hardform transaction (i.e. + * ``type === 1``). + * + * This provides a Type Guard that the related properties are + * non-null. + */ isBerlin(): this is (Transaction & { type: 1, gasPrice: bigint, accessList: AccessList }) { return (this.type === 1); } + + /** + * Returns true if this transaction is london hardform transaction (i.e. + * ``type === 2``). + * + * This provides a Type Guard that the related properties are + * non-null. + */ isLondon(): this is (Transaction & { type: 2, accessList: AccessList, maxFeePerGas: bigint, maxPriorityFeePerGas: bigint}) { return (this.type === 2); } + /** + * Create a copy of this transaciton. + */ clone(): Transaction { return Transaction.from(this); } + /** + * Return a JSON-friendly object. + */ toJSON(): any { const s = (v: null | bigint) => { if (v == null) { return null; } @@ -590,7 +768,13 @@ export class Transaction implements TransactionLike { }; } - static from(tx: string | TransactionLike): Transaction { + /** + * Create a **Transaction** from a serialized transaction or a + * Transaction-like object. + */ + static from(tx?: string | TransactionLike): Transaction { + if (tx == null) { return new Transaction(); } + if (typeof(tx) === "string") { const payload = getBytes(tx); diff --git a/src.ts/utils/base58.ts b/src.ts/utils/base58.ts index b00e30afd..eac103477 100644 --- a/src.ts/utils/base58.ts +++ b/src.ts/utils/base58.ts @@ -9,7 +9,7 @@ * issue most schemes that use Base58 choose specific high-order values * to ensure non-zero prefixes. * - * @_subsection: api/utils:Base58 Encoding [base58] + * @_subsection: api/utils:Base58 Encoding [about-base58] */ import { getBytes } from "./data.js"; diff --git a/src.ts/utils/base64.ts b/src.ts/utils/base64.ts index 351df99a5..2863fc027 100644 --- a/src.ts/utils/base64.ts +++ b/src.ts/utils/base64.ts @@ -1,10 +1,10 @@ /** - * [Base64 encoding](link-wiki-base64) using 6-bit words to encode + * [Base64 encoding](link-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 [base64] + * @_subsection: api/utils:Base64 Encoding [about-base64] */ import { getBytes, getBytesCopy } from "./data.js"; diff --git a/src.ts/utils/data.ts b/src.ts/utils/data.ts index ba1d47f65..0ca6f49d9 100644 --- a/src.ts/utils/data.ts +++ b/src.ts/utils/data.ts @@ -2,7 +2,7 @@ * Some data helpers. * * - * @_subsection api/utils:Data Helpers [data] + * @_subsection api/utils:Data Helpers [about-data] */ import { assert, assertArgument } from "./errors.js"; diff --git a/src.ts/utils/errors.ts b/src.ts/utils/errors.ts index 49357370c..2e047d730 100644 --- a/src.ts/utils/errors.ts +++ b/src.ts/utils/errors.ts @@ -1,7 +1,7 @@ /** * About Errors. * - * @_section: api/utils/errors:Errors [errors] + * @_section: api/utils/errors:Errors [about-errors] */ import { version } from "../_version.js"; diff --git a/src.ts/utils/events.ts b/src.ts/utils/events.ts index 85eebc7f2..a68289824 100644 --- a/src.ts/utils/events.ts +++ b/src.ts/utils/events.ts @@ -1,7 +1,7 @@ /** * Explain events... * - * @_section api/utils/events:Events [events] + * @_section api/utils/events:Events [about-events] */ import { defineProperties } from "./properties.js"; diff --git a/src.ts/utils/fetch.ts b/src.ts/utils/fetch.ts index bef1c7be5..783957033 100644 --- a/src.ts/utils/fetch.ts +++ b/src.ts/utils/fetch.ts @@ -1,7 +1,7 @@ /** * Explain fetching here... * - * @_section api/utils/fetching:Fetching Web Content [fetching] + * @_section api/utils/fetching:Fetching Web Content [about-fetch] */ import { decodeBase64, encodeBase64 } from "./base64.js"; import { hexlify } from "./data.js"; diff --git a/src.ts/utils/fixednumber.ts b/src.ts/utils/fixednumber.ts index ebf6313f2..a80806dbd 100644 --- a/src.ts/utils/fixednumber.ts +++ b/src.ts/utils/fixednumber.ts @@ -1,7 +1,7 @@ /** * About fixed-point math... * - * @_section: api/utils/fixed-point-math:Fixed-Point Maths [fixed-point-math] + * @_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"; diff --git a/src.ts/utils/maths.ts b/src.ts/utils/maths.ts index f5ca09d1c..1f38b1e2f 100644 --- a/src.ts/utils/maths.ts +++ b/src.ts/utils/maths.ts @@ -1,7 +1,7 @@ /** * Some mathematic operations. * - * @_subsection: api/utils:Math Helpers [maths] + * @_subsection: api/utils:Math Helpers [about-maths] */ import { hexlify, isBytesLike } from "./data.js"; import { assert, assertArgument } from "./errors.js"; diff --git a/src.ts/utils/properties.ts b/src.ts/utils/properties.ts index f6bfe773b..60c2541e8 100644 --- a/src.ts/utils/properties.ts +++ b/src.ts/utils/properties.ts @@ -1,22 +1,9 @@ /** * Property helper functions. * - * @_subsection api/utils:Properties [properties] + * @_subsection api/utils:Properties [about-properties] */ -/** - * Resolves to a new object that is a copy of %%value%%, but with all - * values resolved. - */ -export async function resolveProperties(value: { [ P in keyof T ]: T[P] | Promise}): Promise { - const keys = Object.keys(value); - const results = await Promise.all(keys.map((k) => Promise.resolve(value[k]))); - return results.reduce((accum: any, v, index) => { - accum[keys[index]] = v; - return accum; - }, <{ [ P in keyof T]: T[P] }>{ }); -} - 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++) { @@ -39,6 +26,19 @@ function checkType(value: any, type: string, name: string): void { throw error; } +/** + * Resolves to a new object that is a copy of %%value%%, but with all + * values resolved. + */ +export async function resolveProperties(value: { [ P in keyof T ]: T[P] | Promise}): Promise { + const keys = Object.keys(value); + const results = await Promise.all(keys.map((k) => Promise.resolve(value[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. * diff --git a/src.ts/utils/rlp.ts b/src.ts/utils/rlp.ts index dbd423eeb..5a8e32886 100644 --- a/src.ts/utils/rlp.ts +++ b/src.ts/utils/rlp.ts @@ -2,7 +2,7 @@ * The [[link-rlp]] (RLP) encoding is used throughout Ethereum * to serialize nested structures of Arrays and data. * - * @_subsection api/utils:Recursive-Length Prefix [rlp] + * @_subsection api/utils:Recursive-Length Prefix [about-rlp] */ export { decodeRlp } from "./rlp-decode.js"; diff --git a/src.ts/utils/test.txt b/src.ts/utils/test.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src.ts/utils/units.ts b/src.ts/utils/units.ts index ea7a8c412..5d110960c 100644 --- a/src.ts/utils/units.ts +++ b/src.ts/utils/units.ts @@ -17,7 +17,7 @@ * 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 [units] + * @_subsection api/utils:Unit Conversion [about-units] */ import { assertArgument } from "./errors.js"; import { FixedNumber } from "./fixednumber.js"; diff --git a/src.ts/utils/utf8.ts b/src.ts/utils/utf8.ts index 8fee360e2..eee664619 100644 --- a/src.ts/utils/utf8.ts +++ b/src.ts/utils/utf8.ts @@ -4,7 +4,7 @@ * safety issues as well as provide the ability to recover and analyse * strings. * - * @_subsection api/utils:Strings and UTF-8 [strings] + * @_subsection api/utils:Strings and UTF-8 [about-strings] */ import { getBytes } from "./data.js"; import { assertArgument, assertNormalize } from "./errors.js"; diff --git a/src.ts/wallet/hdwallet.ts b/src.ts/wallet/hdwallet.ts index 902c38c83..23d7f426e 100644 --- a/src.ts/wallet/hdwallet.ts +++ b/src.ts/wallet/hdwallet.ts @@ -27,7 +27,9 @@ import type { Wordlist } from "../wordlists/index.js"; import type { KeystoreAccount } from "./json-keystore.js"; - +/** + * The default derivation path for Ethereum HD Nodes. (i.e. ``"m/44'/60'/0'/0/0"``) + */ export const defaultPath: string = "m/44'/60'/0'/0/0"; @@ -111,18 +113,67 @@ function derivePath>(node: T, path: string): T { return result; } +/** + * An **HDNodeWallet** is a [[Signer]] backed by the private key derived + * from an HD Node using the [[link-bip-32]] stantard. + * + * An HD Node forms a hierarchal structure with each HD Node having a + * private key and the ability to derive child HD Nodes, defined by + * a path indicating the index of each child. + */ export class HDNodeWallet extends BaseWallet { + /** + * The compressed public key. + */ readonly publicKey!: string; + /** + * The fingerprint. + * + * A fingerprint allows quick qay to detect parent and child nodes, + * but developers should be prepared to deal with collisions as it + * is only 4 bytes. + */ readonly fingerprint!: string; + + /** + * The parent fingerprint. + */ readonly parentFingerprint!: string; + /** + * The mnemonic used to create this HD Node, if available. + * + * Sources such as extended keys do not encode the mnemonic, in + * which case this will be ``null``. + */ readonly mnemonic!: null | Mnemonic; + /** + * The chaincode, which is effectively a public key used + * to derive children. + */ readonly chainCode!: string; + /** + * The derivation path of this wallet. + * + * Since extended keys do not provider full path details, this + * may be ``null``, if instantiated from a source that does not + * enocde it. + */ readonly path!: null | string; + + /** + * The child index of this wallet. Values over ``2 *\* 31`` indicate + * the node is hardened. + */ readonly index!: number; + + /** + * The depth of this wallet, which is the number of components + * in its path. + */ readonly depth!: number; /** @@ -187,6 +238,12 @@ export class HDNodeWallet extends BaseWallet { return encryptKeystoreJsonSync(this.#account(), password); } + /** + * The extended key. + * + * This key will begin with the prefix ``xpriv`` and can be used to + * reconstruct this HD Node to derive its children. + */ get extendedKey(): string { // We only support the mainnet values for now, but if anyone needs // testnet values, let me know. I believe current sentiment is that @@ -203,14 +260,28 @@ export class HDNodeWallet extends BaseWallet { ])); } + /** + * Returns true if this wallet has a path, providing a Type Guard + * that the path is non-null. + */ hasPath(): this is { path: string } { return (this.path != null); } + /** + * Returns a neutered HD Node, which removes the private details + * of an HD Node. + * + * A neutered node has no private key, but can be used to derive + * child addresses and other public data about the HD Node. + */ neuter(): HDNodeVoidWallet { return new HDNodeVoidWallet(_guard, this.address, this.publicKey, this.parentFingerprint, this.chainCode, this.path, this.index, this.depth, this.provider); } + /** + * Return the child for %%index%%. + */ deriveChild(_index: Numeric): HDNodeWallet { const index = getNumber(_index, "index"); assertArgument(index <= 0xffffffff, "invalid index", "index", index); @@ -230,6 +301,9 @@ export class HDNodeWallet extends BaseWallet { } + /** + * Return the HDNode for %%path%% from this node. + */ derivePath(path: string): HDNodeWallet { return derivePath(this, path); } @@ -247,6 +321,13 @@ export class HDNodeWallet extends BaseWallet { "m", 0, 0, mnemonic, null); } + /** + * Creates a new HD Node from %%extendedKey%%. + * + * If the %%extendedKey%% will either have a prefix or ``xpub`` or + * ``xpriv``, returning a neutered HD Node ([[HDNodeVoidWallet]]) + * or full HD Node ([[HDNodeWallet) respectively. + */ static fromExtendedKey(extendedKey: string): HDNodeWallet | HDNodeVoidWallet { const bytes = toArray(decodeBase58(extendedKey)); // @TODO: redact @@ -278,6 +359,9 @@ export class HDNodeWallet extends BaseWallet { assertArgument(false, "invalid extended key prefix", "extendedKey", "[ REDACTED ]"); } + /** + * Creates a new random HDNode. + */ static createRandom(password?: string, path?: string, wordlist?: Wordlist): HDNodeWallet { if (password == null) { password = ""; } if (path == null) { path = defaultPath; } @@ -286,11 +370,17 @@ export class HDNodeWallet extends BaseWallet { return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); } + /** + * Create am HD Node from %%mnemonic%%. + */ static fromMnemonic(mnemonic: Mnemonic, path?: string): HDNodeWallet { if (!path) { path = defaultPath; } return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); } + /** + * Creates an HD Node from a mnemonic %%phrase%%. + */ static fromPhrase(phrase: string, password?: string, path?: string, wordlist?: Wordlist): HDNodeWallet { if (password == null) { password = ""; } if (path == null) { path = defaultPath; } @@ -299,21 +389,67 @@ export class HDNodeWallet extends BaseWallet { return HDNodeWallet.#fromSeed(mnemonic.computeSeed(), mnemonic).derivePath(path); } + /** + * Creates an HD Node from a %%seed%%. + */ static fromSeed(seed: BytesLike): HDNodeWallet { return HDNodeWallet.#fromSeed(seed, null); } } +/** + * A **HDNodeVoidWallet** cannot sign, but provides access to + * the children nodes of a [[link-bip-32]] HD wallet addresses. + * + * The can be created by using an extended ``xpub`` key to + * [[HDNodeWallet_fromExtendedKey]] or by + * [nuetering](HDNodeWallet-neuter) a [[HDNodeWallet]]. + */ export class HDNodeVoidWallet extends VoidSigner { + /** + * The compressed public key. + */ readonly publicKey!: string; + /** + * The fingerprint. + * + * A fingerprint allows quick qay to detect parent and child nodes, + * but developers should be prepared to deal with collisions as it + * is only 4 bytes. + */ readonly fingerprint!: string; + + /** + * The parent node fingerprint. + */ readonly parentFingerprint!: string; + /** + * The chaincode, which is effectively a public key used + * to derive children. + */ readonly chainCode!: string; + /** + * The derivation path of this wallet. + * + * Since extended keys do not provider full path details, this + * may be ``null``, if instantiated from a source that does not + * enocde it. + */ readonly path!: null | string; + + /** + * The child index of this wallet. Values over ``2 *\* 31`` indicate + * the node is hardened. + */ readonly index!: number; + + /** + * The depth of this wallet, which is the number of components + * in its path. + */ readonly depth!: number; /** @@ -336,6 +472,12 @@ export class HDNodeVoidWallet extends VoidSigner { this.parentFingerprint, this.chainCode, this.path, this.index, this.depth, provider); } + /** + * The extended key. + * + * This key will begin with the prefix ``xpub`` and can be used to + * reconstruct this neutered key to derive its children addresses. + */ get extendedKey(): string { // We only support the mainnet values for now, but if anyone needs // testnet values, let me know. I believe current sentiment is that @@ -355,8 +497,15 @@ export class HDNodeVoidWallet extends VoidSigner { ])); } + /** + * Returns true if this wallet has a path, providing a Type Guard + * that the path is non-null. + */ hasPath(): this is { path: string } { return (this.path != null); } + /** + * Return the child for %%index%%. + */ deriveChild(_index: Numeric): HDNodeVoidWallet { const index = getNumber(_index, "index"); assertArgument(index <= 0xffffffff, "invalid index", "index", index); @@ -369,7 +518,7 @@ export class HDNodeVoidWallet extends VoidSigner { } const { IR, IL } = ser_I(index, this.chainCode, this.publicKey, null); - const Ki = SigningKey._addPoints(IL, this.publicKey, true); + const Ki = SigningKey.addPoints(IL, this.publicKey, true); const address = computeAddress(Ki); @@ -378,11 +527,15 @@ export class HDNodeVoidWallet extends VoidSigner { } + /** + * Return the signer for %%path%% from this node. + */ derivePath(path: string): HDNodeVoidWallet { return derivePath(this, path); } } +/* export class HDNodeWalletManager { #root: HDNodeWallet; @@ -397,10 +550,34 @@ export class HDNodeWalletManager { return this.#root.deriveChild((index == null) ? 0: index); } } +*/ +/** + * Returns the [[link-bip-32]] path for the acount at %%index%%. + * + * This is the pattern used by wallets like Ledger. + * + * There is also an [alternate pattern](getIndexedAccountPath) used by + * some software. + */ export function getAccountPath(_index: Numeric): string { const index = getNumber(_index, "index"); assertArgument(index >= 0 && index < HardenedBit, "invalid account index", "index", index); return `m/44'/60'/${ index }'/0/0`; } +/** + * Returns the path using an alternative pattern for deriving accounts, + * at %%index%%. + * + * This derivation path uses the //index// component rather than the + * //account// component to derive sequential accounts. + * + * This is the pattern used by wallets like MetaMask. + */ +export function getIndexedAccountPath(_index: Numeric): string { + const index = getNumber(_index, "index"); + assertArgument(index >= 0 && index < HardenedBit, "invalid account index", "index", index); + return `m/44'/60'/0'/0/${ index}`; +} + diff --git a/src.ts/wallet/index.ts b/src.ts/wallet/index.ts index d0cd7f3fa..55ee75f23 100644 --- a/src.ts/wallet/index.ts +++ b/src.ts/wallet/index.ts @@ -26,7 +26,6 @@ export { HDNodeWallet, HDNodeVoidWallet, - HDNodeWalletManager, } from "./hdwallet.js"; export { isCrowdsaleJson, decryptCrowdsaleJson } from "./json-crowdsale.js"; diff --git a/src.ts/wallet/mnemonic.ts b/src.ts/wallet/mnemonic.ts index 2473e8efe..fc8dafb65 100644 --- a/src.ts/wallet/mnemonic.ts +++ b/src.ts/wallet/mnemonic.ts @@ -101,11 +101,32 @@ function entropyToMnemonic(entropy: Uint8Array, wordlist?: null | Wordlist): str const _guard = { }; +/** + * A **Mnemonic** wraps all properties required to compute [[link-bip39]] + * seeds and convert between phrases and entropy. + */ export class Mnemonic { + /** + * The mnemonic phrase of 12, 15, 18, 21 or 24 words. + * + * Use the [[wordlist]] ``split`` method to get the individual words. + */ readonly phrase!: string; + + /** + * The password used for this mnemonic. If no password is used this + * is the empty string (i.e. ``""``) as per the specification. + */ readonly password!: string; + + /** + * The wordlist for this mnemonic. + */ readonly wordlist!: Wordlist; + /** + * The underlying entropy which the mnemonic encodes. + */ readonly entropy!: string; /** @@ -118,11 +139,20 @@ export class Mnemonic { defineProperties(this, { phrase, password, wordlist, entropy }); } + /** + * Returns the seed for the mnemonic. + */ computeSeed(): string { const salt = toUtf8Bytes("mnemonic" + this.password, "NFKD"); return pbkdf2(toUtf8Bytes(this.phrase, "NFKD"), salt, 2048, 64, "sha512"); } + /** + * Creates a new Mnemonic for the %%phrase%%. + * + * The default %%password%% is the empty string and the default + * wordlist is the [English wordlists](LangEn). + */ static fromPhrase(phrase: string, password?: null | string, wordlist?: null | Wordlist): Mnemonic { // Normalize the case and space; throws if invalid const entropy = mnemonicToEntropy(phrase, wordlist); @@ -130,21 +160,39 @@ export class Mnemonic { return new Mnemonic(_guard, entropy, phrase, password, wordlist); } + /** + * Create a new **Mnemonic** from the %%entropy%%. + * + * The default %%password%% is the empty string and the default + * wordlist is the [English wordlists](LangEn). + */ static fromEntropy(_entropy: BytesLike, password?: null | string, wordlist?: null | Wordlist): Mnemonic { const entropy = getBytes(_entropy, "entropy"); const phrase = entropyToMnemonic(entropy, wordlist); return new Mnemonic(_guard, hexlify(entropy), phrase, password, wordlist); } + /** + * Returns the phrase for %%mnemonic%%. + */ static entropyToPhrase(_entropy: BytesLike, wordlist?: null | Wordlist): string { const entropy = getBytes(_entropy, "entropy"); return entropyToMnemonic(entropy, wordlist); } + /** + * Returns the entropy for %%phrase%%. + */ static phraseToEntropy(phrase: string, wordlist?: null | Wordlist): string { return mnemonicToEntropy(phrase, wordlist); } + /** + * Returns true if %%phrase%% is a valid [[link-bip39]] phrase. + * + * This checks all the provided words belong to the %%wordlist%%, + * that the length is valid and the checksum is correct. + */ static isValidMnemonic(phrase: string, wordlist?: null | Wordlist): boolean { try { mnemonicToEntropy(phrase, wordlist); diff --git a/src.ts/wallet/wallet.ts b/src.ts/wallet/wallet.ts index 56bad579d..355f51e9d 100644 --- a/src.ts/wallet/wallet.ts +++ b/src.ts/wallet/wallet.ts @@ -34,6 +34,10 @@ function stall(duration: number): Promise { */ export class Wallet extends BaseWallet { + /** + * Create a new wallet for the %%privateKey%%, optionally connected + * to %%provider%%. + */ constructor(key: string | SigningKey, provider?: null | Provider) { let signingKey = (typeof(key) === "string") ? new SigningKey(key): key; super(signingKey, provider); @@ -90,6 +94,13 @@ export class Wallet extends BaseWallet { return wallet; } + /** + * Creates (asynchronously) a **Wallet** by decrypting the %%json%% + * with %%password%%. + * + * If %%progress%% is provided, it is called periodically during + * decryption so that any UI can be updated. + */ static async fromEncryptedJson(json: string, password: Uint8Array | string, progress?: ProgressCallback): Promise { let account: null | CrowdsaleAccount | KeystoreAccount = null; if (isKeystoreJson(json)) { @@ -105,6 +116,13 @@ export class Wallet extends BaseWallet { return Wallet.#fromAccount(account); } + /** + * Creates a **Wallet** by decrypting the %%json%% with %%password%%. + * + * The [[fromEncryptedJson]] method is preferred, as this method + * will lock up and freeze the UI during decryption, which may take + * some time. + */ static fromEncryptedJsonSync(json: string, password: Uint8Array | string): HDNodeWallet | Wallet { let account: null | CrowdsaleAccount | KeystoreAccount = null; if (isKeystoreJson(json)) { @@ -118,12 +136,21 @@ export class Wallet extends BaseWallet { return Wallet.#fromAccount(account); } + /** + * Creates a new random [[HDNodeWallet]] using the avavilable + * [cryptographic random source](randomBytes). + * + * If there is no crytographic random source, this will throw. + */ static createRandom(provider?: null | Provider): HDNodeWallet { const wallet = HDNodeWallet.createRandom(); if (provider) { return wallet.connect(provider); } return wallet; } + /** + * Creates a [[HDNodeWallet]] for %%phrase%%. + */ static fromPhrase(phrase: string, provider?: Provider): HDNodeWallet { const wallet = HDNodeWallet.fromPhrase(phrase); if (provider) { return wallet.connect(provider); } diff --git a/src.ts/wordlists/index.ts b/src.ts/wordlists/index.ts index fa8185199..6d5d537b4 100644 --- a/src.ts/wordlists/index.ts +++ b/src.ts/wordlists/index.ts @@ -22,3 +22,5 @@ export { LangEn } from "./lang-en.js"; export { WordlistOwl } from "./wordlist-owl.js"; export { WordlistOwlA } from "./wordlist-owla.js"; + +export { wordlists } from "./wordlists.js"; diff --git a/src.ts/wordlists/lang-cz.ts b/src.ts/wordlists/lang-cz.ts index bfc0306e2..ea9326d05 100644 --- a/src.ts/wordlists/lang-cz.ts +++ b/src.ts/wordlists/lang-cz.ts @@ -6,7 +6,7 @@ const checksum = "0x25f44555f4af25b51a711136e1c7d6e50ce9f8917d39d6b1f076b2bb4d2f let wordlist: null | LangCz = null; /** - * The [[link-bip-39]] Wordlist for the Czech (cz) language. + * The [[link-bip39-cz]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ @@ -15,8 +15,8 @@ export class LangCz extends WordlistOwl { /** * Creates a new instance of the Czech language Wordlist. * - * This should be unnecessary most of the time as the exported - * [[langCz]] should suffice. + * Using the constructor should be unnecessary, instead use the + * [[wordlist]] singleton method. */ constructor() { super("cz", words, checksum); } diff --git a/src.ts/wordlists/lang-en.ts b/src.ts/wordlists/lang-en.ts index 4b7a48055..06ef91f43 100644 --- a/src.ts/wordlists/lang-en.ts +++ b/src.ts/wordlists/lang-en.ts @@ -6,7 +6,7 @@ const checksum = "0x3c8acc1e7b08d8e76f9fda015ef48dc8c710a73cb7e0f77b2c18a9b5a7ad let wordlist: null | LangEn = null; /** - * The [[link-bip-39]] Wordlist for the English (en) language. + * The [[link-bip39-en]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-es.ts b/src.ts/wordlists/lang-es.ts index 5dd87e82c..6803693db 100644 --- a/src.ts/wordlists/lang-es.ts +++ b/src.ts/wordlists/lang-es.ts @@ -7,7 +7,7 @@ const checksum = "0xf74fb7092aeacdfbf8959557de22098da512207fb9f109cb526994938cf4 let wordlist: null | LangEs = null; /** - * The [[link-bip-39]] Wordlist for the Spanish (es) language. + * The [[link-bip39-es]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-fr.ts b/src.ts/wordlists/lang-fr.ts index b364279c8..bc0bfaf77 100644 --- a/src.ts/wordlists/lang-fr.ts +++ b/src.ts/wordlists/lang-fr.ts @@ -7,7 +7,7 @@ const checksum = "0x51deb7ae009149dc61a6bd18a918eb7ac78d2775726c68e598b92d002519 let wordlist: null | LangFr = null; /** - * The [[link-bip-39]] Wordlist for the French (fr) language. + * The [[link-bip39-fr]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-it.ts b/src.ts/wordlists/lang-it.ts index ec8f4706a..2d2c8a123 100644 --- a/src.ts/wordlists/lang-it.ts +++ b/src.ts/wordlists/lang-it.ts @@ -6,7 +6,7 @@ const checksum = "0x5c1362d88fd4cf614a96f3234941d29f7d37c08c5292fde03bf62c2db6ff let wordlist: null | LangIt = null; /** - * The [[link-bip-39]] Wordlist for the Italian (it) language. + * The [[link-bip39-it]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-ja.ts b/src.ts/wordlists/lang-ja.ts index 5c21ca113..59e975fad 100644 --- a/src.ts/wordlists/lang-ja.ts +++ b/src.ts/wordlists/lang-ja.ts @@ -134,7 +134,7 @@ function loadWords(): Array { let wordlist: null | LangJa = null; /** - * The [[link-bip-39]] Wordlist for the Japanese (ja) language. + * The [[link-bip39-ja]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-ko.ts b/src.ts/wordlists/lang-ko.ts index 8fc3c2f47..b677bff49 100644 --- a/src.ts/wordlists/lang-ko.ts +++ b/src.ts/wordlists/lang-ko.ts @@ -64,7 +64,7 @@ function loadWords(): Array { let wordlist: null | LangKo = null; /** - * The [[link-bip-39]] Wordlist for the Korean (ko) language. + * The [[link-bip39-ko]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-pt.ts b/src.ts/wordlists/lang-pt.ts index 0000cf27f..5e880ac41 100644 --- a/src.ts/wordlists/lang-pt.ts +++ b/src.ts/wordlists/lang-pt.ts @@ -6,7 +6,7 @@ const checksum = "0x2219000926df7b50d8aa0a3d495826b988287df4657fbd100e6fe596c8f7 let wordlist: null | LangPt = null; /** - * The [[link-bip-39]] Wordlist for the Portuguese (pt) language. + * The [[link-bip39-pt]] for [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */ diff --git a/src.ts/wordlists/lang-zh.ts b/src.ts/wordlists/lang-zh.ts index 4ceb4bb7c..acad865a6 100644 --- a/src.ts/wordlists/lang-zh.ts +++ b/src.ts/wordlists/lang-zh.ts @@ -61,14 +61,8 @@ function loadWords(locale: string): Array { const wordlists: Record = { }; /** - * The [[link-bip-39]] Wordlist for the Chinese language. - * - * This Wordlist supports both simplified and traditional - * character set, depending on which is specified in the - * constructor. - * - * For the ``zh_cn`` language use ``"cn"`` and for the ``zh_tw`` - * langauge, use ``"tw"``. + * The [[link-bip39-zh_cn]] and [[link-bip39-zh_tw]] for + * [mnemonic phrases](link-bip-39). * * @_docloc: api/wordlists */