Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,5 @@ export { IWalletAccount } from './src/wallet-account.js'
export { NotImplementedError, SignerError } from './src/errors.js'

export { ISigner } from './src/signer.js'

export { IDisposable } from './src/disposable.js'
26 changes: 26 additions & 0 deletions src/disposable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2024 Tether Operations Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
'use strict'

import { NotImplementedError } from './errors.js'

/** @interface */
export class IDisposable {
/**
* Disposes the object along with all its data (cleaning up any sensitive field from memory).
*/
dispose () {
throw new NotImplementedError('dispose()')
}
}
43 changes: 39 additions & 4 deletions src/signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,44 @@

import { NotImplementedError } from './errors.js'

import { IDisposable } from './disposable.js'

/** @typedef {import('./wallet-account.js').KeyPair} KeyPair */

/**
* A minimal, cross-chain signer interface.
*
* @interface
*/
export class ISigner {
export class ISigner extends IDisposable {
/**
* Whether the signer supports account derivation via {@link derive}.
*
* @type {boolean}
*/
get isDerivable () {
throw new NotImplementedError('isDerivable')
}

/**
* The signer's key pair, or null if the signer does not allow retrieving
* key material (e.g. hardware signers).
*
* @type {KeyPair | null}
*/
get keyPair () {
throw new NotImplementedError('keyPair')
}

/**
* The BIP 0044 derivation path.
*
* @type {string | null}
*/
get path () {
throw new NotImplementedError('path')
}

/**
* Derive a child signer using a relative path (e.g., "0'/0/0").
*
Expand All @@ -42,9 +74,12 @@ export class ISigner {
}

/**
* Disposes the signer and clears any secret material from memory.
* Signs a message.
*
* @param {string} message - The message to sign.
* @returns {Promise<string>} The message's signature.
*/
dispose () {
throw new NotImplementedError('dispose()')
async sign (message) {
throw new NotImplementedError('sign(message)')
}
}
22 changes: 9 additions & 13 deletions src/wallet-account.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,33 +29,29 @@ import { NotImplementedError } from './errors.js'
* @property {Uint8Array | null} privateKey - The private key (null if the account has been disposed).
*/

/** @typedef {import('./disposable.js').IDisposable} IDisposable */

/**
* @interface
* @implements {IDisposable}
* @template [TSignedTransaction=unknown]
*/
export class IWalletAccount extends IWalletAccountReadOnly {
/**
* The derivation path's index of this account.
*
* @type {number}
*/
get index () {
throw new NotImplementedError('index')
}

/**
* The derivation path of this account (see [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)).
* The derivation path of this account (see [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)),
* or null if the account's signer is not bound to a BIP-44 position (e.g. private-key signers).
*
* @type {string}
* @type {string | null}
*/
get path () {
throw new NotImplementedError('path')
}

/**
* The account's key pair.
* The account's key pair, or null if the account's signer does not allow
* retrieving key material (e.g. hardware signers).
*
* @type {KeyPair}
* @type {KeyPair | null}
*/
get keyPair () {
throw new NotImplementedError('keyPair')
Expand Down
9 changes: 8 additions & 1 deletion src/wallet-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { NotImplementedError } from './errors.js'

/** @typedef {import('./wallet-account.js').IWalletAccount} IWalletAccount */
/** @typedef {import('./signer.js').ISigner} ISigner */
/** @typedef {import('./errors.js').SignerError} SignerError */
/** @typedef {import('./disposable.js').IDisposable} IDisposable */

/**
* @typedef {Object} WalletConfig
Expand All @@ -32,14 +34,18 @@ import { NotImplementedError } from './errors.js'
* @property {bigint} fast - The fee rate for transaction sent with fast priority.
*/

/** @abstract */
/**
* @abstract
* @implements {IDisposable}
*/
export default class WalletManager {
/**
* Creates a new wallet manager from a BIP-39 seed.
*
* @overload
* @param {string | Uint8Array} seed - The BIP-39 seed phrase or raw seed bytes.
* @param {WalletConfig} [config] - The wallet configuration.
* @throws {Error} If the seed phrase is invalid.
*/

/**
Expand All @@ -48,6 +54,7 @@ export default class WalletManager {
* @overload
* @param {ISigner} signer - The default signer.
* @param {WalletConfig} [config] - The wallet configuration.
* @throws {SignerError} If the default signer does not support account derivation.
*/
constructor (seedOrSigner, config = {}) {
if (typeof seedOrSigner === 'string') {
Expand Down
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export { default as WalletAccountReadOnly, IWalletAccountReadOnly } from "./src/
export { IWalletAccount } from "./src/wallet-account.js";
export { NotImplementedError, SignerError } from "./src/errors.js";
export { ISigner } from "./src/signer.js";
export { IDisposable } from "./src/disposable.js";
7 changes: 7 additions & 0 deletions types/src/disposable.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/** @interface */
export class IDisposable {
/**
* Disposes the object along with all its data (cleaning up any sensitive field from memory).
*/
dispose(): void;
}
30 changes: 27 additions & 3 deletions types/src/signer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
*
* @interface
*/
export class ISigner {
export class ISigner extends IDisposable {
/**
* Whether the signer supports account derivation via {@link derive}.
*
* @type {boolean}
*/
get isDerivable(): boolean;
/**
* The signer's key pair, or null if the signer does not allow retrieving
* key material (e.g. hardware signers).
*
* @type {KeyPair | null}
*/
get keyPair(): KeyPair | null;
/**
* The BIP 0044 derivation path.
*
* @type {string | null}
*/
get path(): string | null;
/**
* Derive a child signer using a relative path (e.g., "0'/0/0").
*
Expand All @@ -19,8 +38,13 @@ export class ISigner {
*/
getAddress(): Promise<string>;
/**
* Disposes the signer and clears any secret material from memory.
* Signs a message.
*
* @param {string} message - The message to sign.
* @returns {Promise<string>} The message's signature.
*/
dispose(): void;
sign(message: string): Promise<string>;
}
export type KeyPair = import("./wallet-account.js").KeyPair;
export type SignerError = import("./errors.js").SignerError;
import { IDisposable } from "./disposable.js";
32 changes: 14 additions & 18 deletions types/src/wallet-account.d.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
/** @interface */
export interface IWalletAccount<TSignedTransaction = unknown> extends IWalletAccountReadOnly {
/**
* @interface
* @implements {IDisposable}
*/
export interface IWalletAccount<TSignedTransaction = unknown> extends IWalletAccountReadOnly, IDisposable {
/**
* The derivation path's index of this account.
* The derivation path of this account (see [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)),
* or null if the account's signer is not bound to a BIP-44 position (e.g. private-key signers).
*
* @type {number}
* @type {string | null}
*/
get index(): number;
get path(): string | null;
/**
* The derivation path of this account (see [BIP-44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki)).
* The account's key pair, or null if the account's signer does not allow
* retrieving key material (e.g. hardware signers).
*
* @type {string}
* @type {KeyPair | null}
*/
get path(): string;
/**
* The account's key pair.
*
* @type {KeyPair}
*/
get keyPair(): KeyPair;
get keyPair(): KeyPair | null;
/**
* Signs a message.
*
Expand Down Expand Up @@ -69,10 +68,6 @@ export interface IWalletAccount<TSignedTransaction = unknown> extends IWalletAcc
* @returns {Promise<IWalletAccountReadOnly>} The read-only account.
*/
toReadOnlyAccount(): Promise<IWalletAccountReadOnly>;
/**
* Disposes the wallet account, erasing the private key from the memory.
*/
dispose(): void;
}
export type Transaction = import("./wallet-account-read-only.js").Transaction;
export type TransactionResult = import("./wallet-account-read-only.js").TransactionResult;
Expand All @@ -89,3 +84,4 @@ export type KeyPair = {
privateKey: Uint8Array | null;
};
import { IWalletAccountReadOnly } from './wallet-account-read-only.js';
export type IDisposable = import("./disposable.js").IDisposable;
10 changes: 8 additions & 2 deletions types/src/wallet-manager.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/** @abstract */
export default abstract class WalletManager {
/**
* @abstract
* @implements {IDisposable}
*/
export default abstract class WalletManager implements IDisposable {
/**
* Returns a random [BIP-39](https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki) seed phrase.
*
Expand All @@ -19,13 +22,15 @@ export default abstract class WalletManager {
*
* @param {string | Uint8Array} seed - The BIP-39 seed phrase or raw seed bytes.
* @param {WalletConfig} [config] - The wallet configuration.
* @throws {Error} If the seed phrase is invalid.
*/
constructor(seed: string | Uint8Array, config?: WalletConfig);
/**
* Creates a new wallet manager from a default signer.
*
* @param {ISigner} signer - The default signer.
* @param {WalletConfig} [config] - The wallet configuration.
* @throws {SignerError} If the default signer does not support account derivation.
*/
constructor(signer: ISigner, config?: WalletConfig);
/** @private */
Expand Down Expand Up @@ -174,3 +179,4 @@ export type FeeRates = {
*/
fast: bigint;
};
import { IDisposable } from "./disposable.js";