docs: added more jsdocs
This commit is contained in:
parent
01d99a601a
commit
a74b9a557c
@ -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();
|
||||
|
@ -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<null | string>): 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<string> {
|
||||
|
||||
if (typeof(target) === "string") {
|
||||
|
@ -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<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anything that can be used to return or resolve an address.
|
||||
*/
|
||||
export type AddressLike = string | Promise<string> | Addressable;
|
||||
|
||||
/**
|
||||
* An interface for any object which can resolve an ENS name.
|
||||
*/
|
||||
export interface NameResolver {
|
||||
/**
|
||||
* Resolve to the address for the ENS %%name%%.
|
||||
*
|
||||
* Resolves to ``null`` if the name is unconfigued. Use
|
||||
* [[resolveAddress]] (passing this object as %%resolver%%) to
|
||||
* throw for names that are unconfigured.
|
||||
*/
|
||||
resolveName(name: string): Promise<null | string>;
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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<string> {
|
||||
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");
|
||||
|
@ -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 {
|
||||
|
@ -21,18 +21,46 @@ secp256k1.utils.hmacSha256Sync = function(key: Uint8Array, ...messages: Array<Ui
|
||||
return getBytes(computeHmac("sha256", key, concat(messages)));
|
||||
}
|
||||
|
||||
/**
|
||||
* A **SigningKey** provides high-level access to the elliptic curve
|
||||
* cryptography (ECC) operations and key management.
|
||||
*/
|
||||
export class SigningKey {
|
||||
#privateKey: string;
|
||||
|
||||
/**
|
||||
* Creates a new **SigningKey** for %%privateKey%%.
|
||||
*/
|
||||
constructor(privateKey: BytesLike) {
|
||||
assertArgument(dataLength(privateKey) === 32, "invalid private key", "privateKey", "[REDACTED]");
|
||||
this.#privateKey = hexlify(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* The private key.
|
||||
*/
|
||||
get privateKey(): string { return this.#privateKey; }
|
||||
|
||||
/**
|
||||
* The uncompressed public key.
|
||||
*
|
||||
* This will always begin with the prefix ``0x04`` and be 132
|
||||
* characters long (the ``0x`` prefix and 130 hexadecimal nibbles).
|
||||
*/
|
||||
get publicKey(): string { return SigningKey.computePublicKey(this.#privateKey); }
|
||||
|
||||
/**
|
||||
* The compressed public key.
|
||||
*
|
||||
* This will always begin with either the prefix ``0x02`` or ``0x03``
|
||||
* and be 68 characters long (the ``0x`` prefix and 33 hexadecimal
|
||||
* nibbles)
|
||||
*/
|
||||
get compressedPublicKey(): string { return SigningKey.computePublicKey(this.#privateKey, true); }
|
||||
|
||||
/**
|
||||
* Return the signature of the signed %%digest%%.
|
||||
*/
|
||||
sign(digest: BytesLike): Signature {
|
||||
assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
|
||||
|
||||
@ -49,11 +77,27 @@ export class SigningKey {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the [[link-wiki-ecdh]] shared secret between this
|
||||
* private key and the %%other%% key.
|
||||
*
|
||||
* The %%other%% key may be any type of key, a raw public key,
|
||||
* a compressed/uncompressed pubic key or aprivate key.
|
||||
*
|
||||
* Best practice is usually to use a cryptographic hash on the
|
||||
* returned value before using it as a symetric secret.
|
||||
*/
|
||||
computeShardSecret(other: BytesLike): string {
|
||||
const pubKey = SigningKey.computePublicKey(other);
|
||||
return hexlify(secp256k1.getSharedSecret(getBytesCopy(this.#privateKey), pubKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the public key for %%key%%, optionally %%compressed%%.
|
||||
*
|
||||
* The %%key%% may be any type of key, a raw public key, a
|
||||
* compressed/uncompressed public key or private key.
|
||||
*/
|
||||
static computePublicKey(key: BytesLike, compressed?: boolean): string {
|
||||
let bytes = getBytes(key, "key");
|
||||
|
||||
@ -73,6 +117,10 @@ export class SigningKey {
|
||||
return hexlify(point.toRawBytes(compressed));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the public key for the private key which produced the
|
||||
* %%signature%% for the given %%digest%%.
|
||||
*/
|
||||
static recoverPublicKey(digest: BytesLike, signature: SignatureLike): string {
|
||||
assertArgument(dataLength(digest) === 32, "invalid digest length", "digest", digest);
|
||||
|
||||
@ -85,7 +133,17 @@ export class SigningKey {
|
||||
assertArgument(false, "invalid signautre for digest", "signature", signature);
|
||||
}
|
||||
|
||||
static _addPoints(p0: BytesLike, p1: BytesLike, compressed?: boolean): string {
|
||||
/**
|
||||
* Returns the point resulting from adding the ellipic curve points
|
||||
* %%p0%% and %%p1%%.
|
||||
*
|
||||
* This is not a common function most developers should require, but
|
||||
* can be useful for certain privacy-specific techniques.
|
||||
*
|
||||
* For example, it is used by [[HDNodeWallet]] to compute child
|
||||
* addresses from parent public keys and chain codes.
|
||||
*/
|
||||
static addPoints(p0: BytesLike, p1: BytesLike, compressed?: boolean): string {
|
||||
const pub0 = secp256k1.Point.fromHex(SigningKey.computePublicKey(p0).substring(2));
|
||||
const pub1 = secp256k1.Point.fromHex(SigningKey.computePublicKey(p1).substring(2));
|
||||
return "0x" + pub0.add(pub1).toHex(!!compressed)
|
||||
|
@ -98,7 +98,7 @@ export {
|
||||
|
||||
export {
|
||||
Mnemonic,
|
||||
BaseWallet, HDNodeWallet, HDNodeVoidWallet, HDNodeWalletManager,
|
||||
BaseWallet, HDNodeWallet, HDNodeVoidWallet,
|
||||
Wallet,
|
||||
|
||||
defaultPath,
|
||||
@ -141,8 +141,8 @@ export type {
|
||||
} from "./providers/index.js";
|
||||
|
||||
export type {
|
||||
AccessList, AccessListish, AccessListSet,
|
||||
SignedTransaction, TransactionLike
|
||||
AccessList, AccessListish, AccessListEntry,
|
||||
TransactionLike
|
||||
} from "./transaction/index.js";
|
||||
|
||||
export type {
|
||||
|
@ -5,12 +5,31 @@
|
||||
*
|
||||
* @_section: api/providers/thirdparty: Community Providers [thirdparty]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Providers which offer community credentials should extend this
|
||||
* to notify any interested consumers whether community credentials
|
||||
* are in-use.
|
||||
*/
|
||||
export interface CommunityResourcable {
|
||||
/**
|
||||
* Returns true of the instance is connected using the community
|
||||
* credentials.
|
||||
*/
|
||||
isCommunityResource(): boolean;
|
||||
}
|
||||
|
||||
// Show the throttle message only once
|
||||
// Show the throttle message only once per service
|
||||
const shown: Set<string> = 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);
|
||||
|
@ -424,7 +424,7 @@ export class Block implements BlockParams, Iterable<string> {
|
||||
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; }
|
||||
|
@ -14,6 +14,9 @@ function accessSetify(addr: string, storageKeys: Array<string>): { address: stri
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a [[AccessList]] from any ethers-supported access-list structure.
|
||||
*/
|
||||
export function accessListify(value: AccessListish): AccessList {
|
||||
if (Array.isArray(value)) {
|
||||
return (<Array<[ string, Array<string>] | { address: string, storageKeys: Array<string>}>>value).map((set, index) => {
|
||||
|
@ -4,13 +4,21 @@
|
||||
* @_section api/transaction:Transactions [transactions]
|
||||
*/
|
||||
|
||||
/**
|
||||
* Moo
|
||||
*/
|
||||
export type AccessListSet = { address: string, storageKeys: Array<string> };
|
||||
export type AccessList = Array<AccessListSet>;
|
||||
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<string> };
|
||||
|
||||
/**
|
||||
* An ordered collection of [[AccessList]] entries.
|
||||
*/
|
||||
export type AccessList = Array<AccessListEntry>;
|
||||
|
||||
/**
|
||||
* Any ethers-supported access list structure.
|
||||
*/
|
||||
export type AccessListish = AccessList |
|
||||
Array<[ string, Array<string> ]> |
|
||||
Record<string, Array<string>>;
|
||||
@ -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";
|
||||
|
@ -23,27 +23,74 @@ const BN_35 = BigInt(35);
|
||||
const BN_MAX_UINT = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
||||
|
||||
export interface TransactionLike<A = string> {
|
||||
/**
|
||||
* 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<string> {
|
||||
#type: null | number;
|
||||
#to: null | string;
|
||||
@ -327,17 +384,13 @@ export class Transaction implements TransactionLike<string> {
|
||||
#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<string> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
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<string> {
|
||||
|
||||
/**
|
||||
* Return the most "likely" type; currently the highest
|
||||
* supported transaction type
|
||||
* supported transaction type.
|
||||
*/
|
||||
inferType(): number {
|
||||
return <number>(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<number> {
|
||||
|
||||
// Checks that there are no conflicting properties set
|
||||
@ -553,20 +702,49 @@ export class Transaction implements TransactionLike<string> {
|
||||
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<string> {
|
||||
};
|
||||
}
|
||||
|
||||
static from(tx: string | TransactionLike<string>): Transaction {
|
||||
/**
|
||||
* Create a **Transaction** from a serialized transaction or a
|
||||
* Transaction-like object.
|
||||
*/
|
||||
static from(tx?: string | TransactionLike<string>): Transaction {
|
||||
if (tx == null) { return new Transaction(); }
|
||||
|
||||
if (typeof(tx) === "string") {
|
||||
const payload = getBytes(tx);
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* About Errors.
|
||||
*
|
||||
* @_section: api/utils/errors:Errors [errors]
|
||||
* @_section: api/utils/errors:Errors [about-errors]
|
||||
*/
|
||||
|
||||
import { version } from "../_version.js";
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Explain events...
|
||||
*
|
||||
* @_section api/utils/events:Events [events]
|
||||
* @_section api/utils/events:Events [about-events]
|
||||
*/
|
||||
import { defineProperties } from "./properties.js";
|
||||
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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<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] }>{ });
|
||||
}
|
||||
|
||||
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<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.
|
||||
*
|
||||
|
@ -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";
|
||||
|
0
src.ts/utils/test.txt
Normal file
0
src.ts/utils/test.txt
Normal file
@ -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";
|
||||
|
@ -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";
|
||||
|
@ -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<T extends HDNodeLike<T>>(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<HDNodeWallet>(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<HDNodeVoidWallet>(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}`;
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,6 @@ export {
|
||||
|
||||
HDNodeWallet,
|
||||
HDNodeVoidWallet,
|
||||
HDNodeWalletManager,
|
||||
} from "./hdwallet.js";
|
||||
|
||||
export { isCrowdsaleJson, decryptCrowdsaleJson } from "./json-crowdsale.js";
|
||||
|
@ -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<Mnemonic>(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);
|
||||
|
@ -34,6 +34,10 @@ function stall(duration: number): Promise<void> {
|
||||
*/
|
||||
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<HDNodeWallet | Wallet> {
|
||||
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); }
|
||||
|
@ -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";
|
||||
|
@ -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); }
|
||||
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -134,7 +134,7 @@ function loadWords(): Array<string> {
|
||||
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
|
||||
*/
|
||||
|
@ -64,7 +64,7 @@ function loadWords(): Array<string> {
|
||||
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
|
||||
*/
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -61,14 +61,8 @@ function loadWords(locale: string): Array<string> {
|
||||
const wordlists: Record<string, LangZh> = { };
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user