@cashu/cashu-ts
Version:
cashu library for communicating with a cashu mint
1,297 lines (1,189 loc) • 274 kB
TypeScript
import { WeierstrassPoint } from '@noble/curves/abstract/weierstrass.js';
/**
* Immutable, non-negative integer amount value object.
*
* Internal representation is bigint. Use factory methods to instantiate.
*
* @example
*
* Amount.from('21'); // string
* Amount.from(21); // number
* Amount.from(21n); // bigint
* Amount.zero();
* Amount.one();
*/
export declare class Amount {
private readonly value;
private constructor();
/**
* Parse/normalize supported inputs into an Amount.
*
* @throws If input is negative, or if a `number` input exceeds the safe integer limit, or if
* input is not a finite integer.
*/
static from(input: AmountLike): Amount;
static zero(): Amount;
static one(): Amount;
/**
* Internal canonical value.
*/
toBigInt(): bigint;
/**
* Safe conversion to number.
*
* @throws If value exceeds Number.MAX_SAFE_INTEGER.
*/
toNumber(): number;
/**
* Unsafe conversion to number. Precision can be lost above MAX_SAFE_INTEGER.
*/
toNumberUnsafe(): number;
/**
* Canonical decimal representation for logs/JSON string mode.
*/
toString(): string;
/**
* Used by JSON.stringify() to convert Amount to string.
*/
toJSON(): string;
add(other: AmountLike): Amount;
subtract(other: AmountLike): Amount;
multiplyBy(factor: AmountLike): Amount;
divideBy(divisor: AmountLike): Amount;
modulo(divisor: AmountLike): Amount;
/**
* Returns `ceil(this × numerator / denominator)` using integer arithmetic only.
*
* The default denominator of 100 makes common percentage calculations natural. Use a larger
* denominator to express fractional percentages without floats.
*
* @example
*
* amount.ceilPercent(2); // ceil(2% of amount)
* amount.ceilPercent(1, 200); // ceil(0.5% of amount)
* amount.ceilPercent(15, 10); // ceil(1.5% of amount)
*
* @throws If numerator or denominator are not positive integers.
*/
ceilPercent(numerator: number, denominator?: number): Amount;
/**
* Returns `floor(this × numerator / denominator)` using integer arithmetic only.
*
* The natural complement to {@link Amount.ceilPercent} — use when you need the conservative lower
* bound, e.g. "maximum spendable after reserving fees".
*
* @example
*
* amount.floorPercent(98); // floor(98% of amount)
* amount.floorPercent(1, 200); // floor(0.5% of amount)
*
* @throws If numerator or denominator are not positive integers.
*/
floorPercent(numerator: number, denominator?: number): Amount;
/**
* Returns true if this amount is within the inclusive range [min, max].
*
* @example
*
* msats.inRange(data.minSendable, data.maxSendable);
*
* @throws If min > max.
*/
inRange(min: AmountLike, max: AmountLike): boolean;
/**
* Clamps this amount to the inclusive range [min, max].
*
* @example
*
* fee.clamp(MIN_FEE, tokenAmount);
* invoiceAmount.clamp(Amount.from(minSendable), Amount.from(maxSendable));
*
* @throws If min > max.
*/
clamp(min: AmountLike, max: AmountLike): Amount;
/**
* Returns `round(this × numerator / denominator)` using integer arithmetic only.
*
* Useful for proportional rescaling — currency conversion, capacity checks, partial fills —
* without floating-point imprecision or overflow risk.
*
* Uses the identity: `round(a × b / c) = floor((2 × a × b + c) / (2 × c))`
*
* @example
*
* // Scale a 1000-sat amount down by a 3/4 ratio → 750
* Amount.from(1000).scaledBy(3, 4);
*
* // Proportional rescale: if neededAmount is too high, shrink estInvAmount to fit
* estInvAmount.scaledBy(tokenAmount, neededAmount).subtract(1);
*
* @throws If numerator or denominator are zero or negative.
*/
scaledBy(numerator: AmountLike, denominator: AmountLike): Amount;
/**
* Whether this Amount can be safely converted to a number.
*/
isSafeNumber(): boolean;
isZero(): boolean;
equals(other: AmountLike): boolean;
/**
* Compares this Amount with another Amount.
*
* Defines the natural ordering of Amount values. Useful for sorting and ordering logic.
*
* @returns -1 if this < other, 0 if equal, 1 if this > other.
*/
compareTo(other: AmountLike): -1 | 0 | 1;
lessThan(other: AmountLike): boolean;
lessThanOrEqual(other: AmountLike): boolean;
greaterThan(other: AmountLike): boolean;
greaterThanOrEqual(other: AmountLike): boolean;
static min(a: AmountLike, b: AmountLike): Amount;
static max(a: AmountLike, b: AmountLike): Amount;
static sum(values: Iterable<AmountLike>): Amount;
/**
* Tag this {@link Amount} with a currency unit, returning an {@link AmountWithUnit}.
*/
withUnit(unit: string): AmountWithUnit;
}
export declare class AmountError extends CTSError {
constructor(message: string);
}
/**
* All types that can be converted to an {@link Amount} value object.
*/
export declare type AmountLike = number | bigint | string | Amount;
/**
* Immutable {@link Amount} paired with a currency unit.
*
* Binary ops require matching units (throw {@link AmountWithUnitError} otherwise); scalar ops
* preserve the unit.
*
* Lift via {@link Amount.withUnit} / {@link AmountWithUnit.from}, drop via
* {@link AmountWithUnit.toAmount}.
*
* @example
*
* AmountWithUnit.from(100, 'sat');
* Amount.from(21).withUnit('sat');
*/
export declare class AmountWithUnit {
private readonly _amount;
readonly unit: string;
constructor(amount: Amount, unit: string);
static from(value: AmountLike, unit: string): AmountWithUnit;
static zero(unit: string): AmountWithUnit;
static one(unit: string): AmountWithUnit;
/**
* Return the underlying unitless {@link Amount}, dropping the unit guard.
*/
toAmount(): Amount;
toBigInt(): bigint;
toNumber(): number;
/**
* Unit-bearing canonical form, e.g. `"[sat]: 100"`. Used by `String(x)`, template literals,
* `console.log`, and any other string-coercion context.
*
* Leads with `[` (never a digit, sign, or decimal point) so that `parseInt(String(x))` /
* `parseFloat(String(x))` return `NaN` even if the unit itself starts with digits — otherwise a
* unit like `"9999sat"` would let `parseInt` silently extract `9999` from the unit and drop the
* real amount.
*/
toString(): string;
toJSON(): {
amount: string;
unit: string;
};
/* Excluded from this release type: [Symbol.toPrimitive] */
isZero(): boolean;
isSafeNumber(): boolean;
private requireSameUnit;
add(other: AmountWithUnit): AmountWithUnit;
subtract(other: AmountWithUnit): AmountWithUnit;
equals(other: AmountWithUnit): boolean;
compareTo(other: AmountWithUnit): -1 | 0 | 1;
lessThan(other: AmountWithUnit): boolean;
lessThanOrEqual(other: AmountWithUnit): boolean;
greaterThan(other: AmountWithUnit): boolean;
greaterThanOrEqual(other: AmountWithUnit): boolean;
inRange(min: AmountWithUnit, max: AmountWithUnit): boolean;
clamp(min: AmountWithUnit, max: AmountWithUnit): AmountWithUnit;
multiplyBy(factor: AmountLike): AmountWithUnit;
divideBy(divisor: AmountLike): AmountWithUnit;
modulo(divisor: AmountLike): AmountWithUnit;
ceilPercent(numerator: number, denominator?: number): AmountWithUnit;
floorPercent(numerator: number, denominator?: number): AmountWithUnit;
scaledBy(numerator: AmountLike, denominator: AmountLike): AmountWithUnit;
static min(a: AmountWithUnit, b: AmountWithUnit): AmountWithUnit;
static max(a: AmountWithUnit, b: AmountWithUnit): AmountWithUnit;
/**
* Sum a unit-tagged iterable.
*
* - If `unit` is provided, every element must match it; the result has that unit. An empty iterable
* returns `AmountWithUnit.zero(unit)`.
* - If `unit` is omitted, the iterable must be non-empty; the unit is inferred from the first
* element and every subsequent element must match. An empty iterable throws.
*
* @throws {AmountWithUnitError} On unit mismatch, or on empty iterable when `unit` is omitted.
*/
static sum(values: Iterable<AmountWithUnit>, unit?: string): AmountWithUnit;
}
export declare class AmountWithUnitError extends CTSError {
constructor(message: string);
}
/**
* Assert that a Secret is of the expected kind.
*
* @param allowed - NUT-10 Kind(s) allowed.
* @param secret - The Proof secret.
* @returns Parsed Secret if the kind matches.
* @throws If secret kind is not as expected.
*/
export declare function assertSecretKind(allowed: SecretKind | SecretKind[], secret: Secret | string): Secret;
/* Excluded from this release type: assertSigAllInputs */
/**
* AuthManager.
*
* - Owns CAT lifecycle (stores, optional refresh via attached OIDCAuth)
* - Mints and serves BATs (NUT-22)
* - Validates DLEQs for BATs per NUT-12.
* - Supplies serialized BATs for 'Blind-auth' and CAT for 'Clear-auth'
*/
export declare class AuthManager implements AuthProvider {
private readonly mintUrl;
private readonly req;
private readonly logger;
private info?;
private lockChain?;
private inflightRefresh?;
private static readonly MIN_VALID_SECS;
private oidc?;
private tokens;
private pool;
private desiredPoolSize;
private maxPerMint;
private keychain?;
constructor(mintUrl: string, opts?: AuthManagerOptions);
/**
* Attach an OIDCAuth instance so this manager can refresh CATs. Registers a listener to update
* internal CAT/refresh state on new tokens.
*/
attachOIDC(oidc: OIDCAuth): this;
get poolSize(): number;
get poolTarget(): number;
get activeAuthKeysetId(): string | undefined;
get hasCAT(): boolean;
getCAT(): string | undefined;
setCAT(cat: string | undefined): void;
/**
* Ensure a valid CAT is available (refresh if expiring soon). Returns a token safe to send right
* now, or undefined if unobtainable.
*/
ensureCAT(minValidSecs?: number): Promise<string | undefined>;
private validForAtLeast;
private updateFromOIDC;
/**
* Ensure there are enough BAT tokens (topping up if needed)
*
* @param minTokens Minimum tokens needed.
*/
ensure(minTokens: number): Promise<void>;
/**
* Gets a Blind Authentication Token (BAT)
*
* @param {method, path} to Call (not used in our implementation)
* @returns The serialized BAT ready to insert into request header.
*/
getBlindAuthToken({ method, path, }: {
method: 'GET' | 'POST';
path: string;
}): Promise<string>;
/**
* Replace or merge the current BAT pool with previously persisted BATs.
*/
importPool(proofs: Proof[], mode?: 'replace' | 'merge'): void;
/**
* Return a deep-copied snapshot of the current BAT pool (full Proofs, including dleq).
*/
exportPool(): Proof[];
/**
* Extract exp, seconds since epoch, from a JWT access token.
*/
private parseJwtExpSec;
/**
* Simple mutex lock - chains promises in order.
*/
private withLock;
/**
* Initialise mint info and auth keysets/keys as needed.
*/
private init;
/**
* Gets the BAT minting limit: lower of manager limit and Mint’s NUT-22 limit.
*/
private getBatMaxMint;
private getActiveKeys;
/**
* Mint a batch of BATs using the current CAT if the endpoint is protected by NUT-21.
*/
private topUp;
}
export declare type AuthManagerOptions = {
/**
* Hard limit to target when minting BATs in one request. If omitted, we'll read
* `nuts['22'].bat_max_mint` from the mint "/v1/info" endpoint. Values above the
* `ABSOLUTE_MAX_PER_MINT` internal hard cap are clamped.
*/
maxPerMint?: number;
/**
* Desired BAT pool size. We’ll top-up to min(desiredPoolSize, maxPerMint) on demand.
*/
desiredPoolSize?: number;
/**
* Custom request fn (e.g. for tests or host env).
*/
request?: RequestFn;
/**
* Logger.
*/
logger?: Logger;
};
export declare interface AuthProvider {
getBlindAuthToken(input: {
method: 'GET' | 'POST';
path: string;
}): Promise<string>;
ensure?(minTokens: number): Promise<void>;
getCAT(): string | undefined;
setCAT(cat: string | undefined): void;
/**
* Ensure a valid CAT is available, refreshing if expiring soon. Return a token that is safe to
* send right now, or undefined if not obtainable.
*/
ensureCAT?(minValiditySec?: number): Promise<string | undefined>;
}
/**
* Preview of a batched mint transaction created by prepareBatchMint.
*
* @remarks
* Contains JSON-unsafe values (`bigint`, `Uint8Array`). Not intended for direct serialization.
*/
export declare interface BatchMintPreview<TQuote extends Pick<MintQuoteBaseResponse, 'quote' | 'pubkey'> = MintQuoteBaseResponse> {
method: string;
/**
* Batch mint payload to be sent to the mint.
*/
payload: BatchMintRequest;
/**
* Blinding data required to construct proofs (consolidated across all quotes).
*/
outputData: OutputDataLike[];
/**
* Keyset ID used to prepare the outputs.
*/
keysetId: string;
/**
* Mint Quote objects included in this batch.
*/
quotes: TQuote[];
}
/**
* Payload that needs to be sent to the mint when requesting a NUT-29 batched mint.
*/
export declare type BatchMintRequest = {
/**
* Array of Quote IDs received from the mint.
*/
quotes: string[];
/**
* Array of amounts that shall be minted per quote id.
*/
quote_amounts: Amount[];
/**
* Outputs (blinded messages) to be signed by the mint.
*/
outputs: SerializedBlindedMessage[];
/**
* Optional. Signatures for the Public key the quote is locked to (NUT-20) (same order as quote
* ids). If some quotes are unlocked null is expected. Can be omitted if all quotes are unlocked.
*/
signatures?: Array<string | null>;
};
/* Excluded from this release type: bigIntStringify */
/**
* Blind a secret message.
*
* @param secret A UTF-8 byte encoded string.
* @param r Optional. Deterministic blinding scalar to use (eg: for testing / seeded)
* @returns A RawBlindedMessage: {B_, r, secret}
*/
export declare function blindMessage(secret: Uint8Array, r?: bigint): RawBlindedMessage;
export declare type BlindSignature = {
C_: WeierstrassPoint<bigint>;
id: string;
};
/* Excluded from this release type: buildLegacyP2PKSigAllMessage */
/* Excluded from this release type: buildP2PKSigAllMessage */
export declare type CancellerLike = SubscriptionCanceller | Promise<SubscriptionCanceller>;
/**
* Enum for the state of a proof.
*/
export declare const CheckStateEnum: {
readonly UNSPENT: "UNSPENT";
readonly PENDING: "PENDING";
readonly SPENT: "SPENT";
};
export declare type CheckStateEnum = (typeof CheckStateEnum)[keyof typeof CheckStateEnum];
/**
* Payload that needs to be sent to the mint when checking for spendable proofs.
*/
export declare type CheckStatePayload = {
/**
* The Y = hash_to_curve(secret) of the proofs to be checked.
*/
Ys: string[];
};
/**
* Response when checking proofs if they are spendable. Should not rely on this for receiving, since
* it can be easily cheated.
*/
export declare type CheckStateResponse = {
states: ProofState[];
};
export declare type CompleteMeltOptions = {
preferAsync?: boolean;
extraPayload?: Record<string, unknown>;
};
/**
* Computes the SHA-256 hash of a UTF-8 message string.
*
* @param message To hash (UTF-8 encoded before hashing).
* @param asHex Optional: True returns a hex-encoded hash string; otherwise returns raw bytes.
* @returns SHA-256 hash as raw bytes or hex string, depending on `asHex`.
*/
export declare function computeMessageDigest(message: string): Uint8Array;
export declare function computeMessageDigest(message: string, asHex: false): Uint8Array;
export declare function computeMessageDigest(message: string, asHex: true): string;
/**
* Outputs messages to the console based on the specified log level.
*
* Supports placeholder substitution in messages (e.g., `{key}`) using values from the optional
* `context` object. Context keys not used in substitution are appended to the output as additional
* data. Each log message is prefixed with the log level in square brackets (e.g., `[INFO]`).
*
* @example Const logger = new ConsoleLogger(LogLevel.DEBUG); logger.info('User {username} logged
* in', { username: 'alice', ip: '127.0.0.1' }); // Output: [INFO] User alice logged in { ip:
* "127.0.0.1" }
*/
export declare class ConsoleLogger implements Logger {
private minLevel;
constructor(minLevel?: LogLevel);
private should;
private method;
private header;
private flattenContext;
private emit;
error(msg: string, ctx?: Record<string, unknown>): void;
warn(msg: string, ctx?: Record<string, unknown>): void;
info(msg: string, ctx?: Record<string, unknown>): void;
debug(msg: string, ctx?: Record<string, unknown>): void;
trace(msg: string, ctx?: Record<string, unknown>): void;
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
}
export declare function constructUnblindedSignature(blindSig: BlindSignature, r: bigint, secret: Uint8Array, key: WeierstrassPoint<bigint>): UnblindedSignature;
/**
* Usable counters in range is [start, start+count-1]
*
* @example // Start: 5, count: 3 => 5,6,7.
*/
export declare interface CounterRange {
start: number;
count: number;
}
export declare interface CounterSource {
/**
* Reserve n counters for a keyset.
*
* N may be 0. In that case the call MUST NOT mutate state and MUST return { start: currentNext,
* count: 0 }, effectively a read only peek of the cursor.
*/
reserve(keysetId: string, n: number): Promise<CounterRange>;
/**
* Monotonic bump, ensure the next counter is at least minNext.
*/
advanceToAtLeast(keysetId: string, minNext: number): Promise<void>;
/**
* Optional introspection.
*/
snapshot?(): Promise<Record<string, number>>;
/**
* Optional hard set, useful for tests or migrations.
*/
setNext?(keysetId: string, next: number): Promise<void>;
}
/**
* High-level helper to create a fully authenticated wallet session.
*
* @remarks
* Like a dependency injector, it wires AuthManager->Mint->OIDCAuth->Wallet in the correct order.
* Wallet is returned ready to use.
* @param mintUrl URL of the mint to connect to.
* @param options.authPool Optional. Desired BAT pool size and per-request mint cap. Both
* desiredPoolSize and maxPerMint on the AuthManager will be set to this value. Defaults to 10.
* @param options.oidc Optional. Options for OIDCAuth (scope, clientId, logger, etc.)
* @returns {mint, auth, oidc, wallet} — hydrated, ready to use.
* @throws If mint does not require authentication.
*/
export declare function createAuthWallet(mintUrl: string, options?: {
authPool?: number;
oidc?: OIDCAuthOptions;
logger?: Logger;
}): Promise<{
mint: Mint;
auth: AuthManager;
oidc: OIDCAuth;
wallet: Wallet;
}>;
export declare function createBlindSignature(B_: WeierstrassPoint<bigint>, privateKey: Uint8Array, id: string): BlindSignature;
/**
* !!! WARNING !!! Not recommended for production use, due to non-constant time operations See:
* https://github.com/cashubtc/cashu-crypto-ts/pull/2 for more details See:
* https://en.wikipedia.org/wiki/Timing_attack for information about timing attacks.
*/
export declare const createDLEQProof: (B_: WeierstrassPoint<bigint>, a: Uint8Array) => DLEQ;
/**
* Create a shared in-memory {@link CounterSource}.
*
* Use this when multiple {@link Wallet} instances share the same seed and must allocate
* deterministic outputs without overlapping counter ranges. Pass the returned source to each wallet
* via the `counterSource` option.
*
* The source is memory-only — counters do not survive page reloads. Subscribe to
* {@link WalletEvents.countersReserved | wallet.on.countersReserved} to persist counter state to
* your own storage.
*
* @param initial - Optional seed values (`{ [keysetId]: nextCounter }`).
*/
export declare function createEphemeralCounterSource(initial?: Record<string, number>): CounterSource;
/**
* Create an HTLC hash/preimage pair.
*
* @param preimage - Optional. Preimage to use (Default: random preimage)
* @returns Hash and preimage pair.
* @throws If the preimage supplied is not a 64-char hex string.
*/
export declare function createHTLCHash(preimage?: string): {
hash: string;
preimage: string;
};
/**
* Create an HTLC secret.
*
* @remarks
* Use `createHTLCHash()` for hash creation.
* @param hash - The HTLC hash to add to Secret.data.
* @param tags - Optional. Additional P2PK tags.
*/
export declare function createHTLCsecret(hash: string, tags?: string[][]): string;
/**
* Creates new mint keys.
*
* @param pow2height Number of powers of 2 to create (Max 65).
* @param seed (Optional). Seed for key derivation.
* @param options.expiry (optional) expiry of the keyset.
* @param options.input_fee_ppk (optional) Input fee for keyset (in ppk)
* @param options.unit (optional) the unit of the keyset. Default: sat.
* @param options.versionByte (optional) version of the keyset ID. Default: 1.
* @returns KeysetPair object.
* @throws If keyset versionByte is not valid.
*/
export declare function createNewMintKeys(pow2height: IntRange<0, 65>, seed?: Uint8Array, options?: {
expiry?: number;
input_fee_ppk?: number;
unit?: string;
versionByte?: number;
}): KeysetPair;
/**
* Create a P2PK secret.
*
* @param pubkey - The pubkey to add to Secret.data.
* @param tags - Optional. Additional P2PK tags.
* @throws If the sigflag is unrecognised.
*/
export declare function createP2PKsecret(pubkey: string, tags?: string[][]): string;
/**
* Creates a random blinded message.
*
* @remarks
* The secret is a UTF-8 encoded 64-character lowercase hex string, generated from 32 random bytes
* as recommended by NUT-00.
* @returns A RawBlindedMessage: {B_, r, secret}
*/
export declare function createRandomRawBlindedMessage(): RawBlindedMessage;
export declare function createRandomSecretKey(): Uint8Array<ArrayBufferLike>;
/**
* Create a NUT-10 well known secret.
*
* @param kind - The secret kind (P2PK, HTLC, etc)
* @param pubkey - The pubkey to add to Secret.data.
* @param tags - Optional. Additional P2PK tags.
*/
export declare function createSecret(kind: SecretKind, data: string, tags?: string[][]): string;
/* Excluded from this release type: createSecretAndBlindingFactorDeriver */
/**
* Base error for errors raised by cashu-ts itself.
*/
export declare class CTSError extends Error {
readonly cause?: unknown;
constructor(message: string, options?: {
cause?: unknown;
});
}
/**
* Decodes an encoded cashu payment request string into a {@link PaymentRequest}.
*/
export declare function decodePaymentRequest(paymentRequest: string): PaymentRequest_2;
/* Excluded from this release type: dedupeP2PKPubkeys */
/**
* @deprecated Use {@link deriveSecretAndBlindingFactor} to derive both values together.
*/
export declare const deriveBlindingFactor: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
declare type DerivedSecretAndBlindingFactor = {
blindingFactor: Uint8Array;
secret: Uint8Array;
};
/**
* Returns the keyset id of a set of keys.
*
* @param keys Keys object to derive keyset id from.
* @param options.expiry (optional) expiry of the keyset.
* @param options.input_fee_ppk (optional) Input fee for keyset (in ppk)
* @param options.unit (optional) the unit of the keyset. Default: sat.
* @param options.versionByte (optional) version of the keyset ID. Default: 1.
* @param options.isDeprecatedBase64 (optional) version of the keyset ID. Default: false.
* @returns Keyset id of the keys.
* @throws If keyset versionByte is not valid.
*/
export declare function deriveKeysetId(keys: Keys, options?: DeriveKeysetIdOptions): string;
export declare type DeriveKeysetIdOptions = {
expiry?: number;
input_fee_ppk?: number;
unit?: string;
versionByte?: number;
isDeprecatedBase64?: boolean;
};
/**
* Blind a sequence of public keys using ECDH derived tweaks, one tweak per slot.
*
* @remarks
* Security note: "Ehex" must never be reused. Doing so would create linkability and leak privacy.
* The only exception is for SIG_ALL proofs, as all secret tags must match.
*
* This is the Sender side API.
* @param pubkeys Ordered SEC1 compressed pubkeys, [data, ...pubkeys, ...refund]
* @param eBytes Optional. Fixed ephemeral secret key to use (eg for SIG_ALL / testing)
* @returns Blinded pubkeys in the same order, and Ehex as SEC1 compressed hex, 33 bytes.
* @throws If a blinded key is at infinity.
*/
export declare function deriveP2BKBlindedPubkeys(pubkeys: string[], eBytes?: Uint8Array): {
blinded: string[];
Ehex: string;
};
/**
* Derive a blinded secret key per NUT-28.
*
* Unblinds the pubkey (P = P_ - r·G), verifies x-coord against the naturalPub x(P) == x(p·G), then
* choose skStd = (p + rᵢ) mod n if parity(P) == parity(p·G), otherwise skNeg = (-p + rᵢ) mod n.
* Returns skStd if no blindPubkey is provided.
*
* @remarks
* Security note, this operates on long lived secrets. JavaScript BigInt arithmetic in a JIT is not
* guaranteed constant time. Do not expose this function on a server that holds private keys.
* @param privkey Unblinded private key (p), hex or bigint.
* @param rBlind Blinding scalar (r), hex or bigint.
* @param blindPubkey Optional. Blinded pubkey (P_) to match, 33 byte hex.
* @param naturalPub Optional. Pubkey calculated from private key (P = p·G), 33 byte hex.
* @returns Derived blinded secret key as 64 char hex.
* @throws If inputs are out of range, or the derived key would be zero.
*/
export declare function deriveP2BKSecretKey(privkey: string | bigint, rBlind: string | bigint, blindPubkey?: Uint8Array, naturalPub?: Uint8Array): string | null;
/**
* Derive blinded secret keys that correspond to given P2BK blinded pubkeys.
*
* Pubkeys are processed in order, for a proof that is [data, ...pubkeys, ...refund]. Private key
* order does not matter.
*
* @remarks
* Security note, this operates on long lived secrets. JavaScript BigInt arithmetic in a JIT is not
* guaranteed constant time. Do not expose this function on a server that holds private keys.
*
* This is the Receiver side API.
* @param Ehex Ephemeral public key (E) as SEC1 hex.
* @param privateKey Secret key or array of secret keys, hex.
* @param blindPubKey Blinded public key or array of blinded public keys, hex.
* @returns Array of derived secret keys as 64 char hex.
*/
export declare function deriveP2BKSecretKeys(Ehex: string, privateKey: string | string[], blindPubKey: string | string[]): string[];
/**
* @deprecated Use {@link deriveSecretAndBlindingFactor} to derive both values together.
*/
export declare const deriveSecret: (seed: Uint8Array, keysetId: string, counter: number) => Uint8Array;
/**
* Derives the deterministic secret and blinding factor for one counter.
*
* @remarks
* This is the preferred NUT-13 derivation API because deterministic output construction needs both
* values for the same seed, keyset, and counter. For deprecated BIP-32 keysets, deriving both
* values together is faster because it avoids repeating the shared path derivation common to the
* secret and blinding factor.
*
* The function supports legacy base64 keyset IDs, deprecated hex keyset IDs with the `00` prefix,
* and modern hex keyset IDs with the `01` prefix.
* @param seed - Wallet seed used for deterministic derivation.
* @param keysetId - Mint keyset ID that selects the derivation method.
* @param counter - Deterministic counter for the output.
* @returns The derived secret bytes and blinding factor bytes.
* @throws {@link CTSError} If the keyset ID version is unsupported or if derivation produces an
* invalid private key.
*/
export declare function deriveSecretAndBlindingFactor(seed: Uint8Array, keysetId: string, counter: number): {
blindingFactor: Uint8Array;
secret: Uint8Array;
};
export declare function deserializeMintKeys(serializedMintKeys: SerializedMintKeys): RawMintKeys;
/**
* Deserializes proofs from JSON back into typed {@link Proof} objects, restoring `amount` as
* `bigint` without silent precision loss.
*
* - Pass a `string[]` (individual proof JSON strings) when reading from NutZap proof tags or a
* database.
* - Pass a `string` (a JSON array) when reading from a single stored blob e.g. localStorage.
* - Pass a `ProofLike[]` of already-parsed proof objects for legacy data or database rows.
*
* @example
*
* // NutZap proof tags
* const proofs = deserializeProofs(
* event.tags.filter((t) => t[0] === 'proof').map((t) => t[1]),
* );
*
* // localStorage — pass the raw string, no JSON.parse needed
* const proofs = deserializeProofs(localStorage.getItem('proofs') ?? '[]');
*/
export declare function deserializeProofs(json: string | string[] | ProofLike[]): Proof[];
export declare type DeviceStartResponse = {
device_code: string;
user_code: string;
verification_uri: string;
verification_uri_complete?: string;
interval?: number;
expires_in?: number;
};
export declare type DigestInput = Uint8Array | string;
export declare type DLEQ = {
s: Uint8Array;
e: Uint8Array;
r?: bigint;
};
export declare type Enumerate<N extends number, Acc extends number[] = []> = Acc['length'] extends N ? Acc[number] : Enumerate<N, [...Acc, Acc['length']]>;
/**
* Find the private key that can sign for a given compressed public key.
*
* @param pubkey Compressed SEC1 public key (33 bytes, hex-encoded) to match against.
* @param privkeys One or more candidate private keys (hex-encoded).
* @returns The matching private key hex string.
* @throws If no candidate key derives to the expected pubkey.
*/
export declare function findSigningKey(pubkey: string, privkeys: string | string[]): string;
/**
* Get data field value from a secret.
*
* @param secret - The Proof secret.
* @returns - SecretData.data.
*/
export declare function getDataField(secret: Secret | string): string;
/**
* Helper function to decode cashu tokens into an object.
*
* @param token An encoded cashu token (cashuB...)
* @param keysets Array of full keyset ID strings, eg: from `KeyChain.getAllKeysetIds()`
* @returns Cashu token object.
*/
export declare function getDecodedToken(tokenString: string, keysetIds: readonly string[]): Token;
/**
* Decodes a raw binary token (`craw` + `B` + CBOR) into a {@link Token}.
*/
export declare function getDecodedTokenBinary(bytes: Uint8Array): Token;
/**
* Encodes a {@link Token} as a cashu token string.
*/
export declare function getEncodedToken(token: Token, opts?: {
removeDleq?: boolean;
}): string;
/**
* Encodes a {@link Token} as a raw binary token (`craw` + `B` + CBOR).
*/
export declare function getEncodedTokenBinary(token: Token): Uint8Array;
/**
* Get preimage from a witness if present.
*
* @param witness From a Proof.
* @returns Preimage if present.
*/
export declare function getHTLCWitnessPreimage(witness: Proof['witness']): string | undefined;
/**
* Response from mint at /info endpoint.
*/
export declare type GetInfoResponse = {
name: string;
pubkey: string;
version: string;
description?: string;
description_long?: string;
icon_url?: string;
contact: MintContactInfo[];
nuts: {
'4': {
methods: SwapMethod[];
disabled: boolean;
};
'5': {
methods: SwapMethod[];
disabled: boolean;
};
'7'?: {
supported: boolean;
};
'8'?: {
supported: boolean;
};
'9'?: {
supported: boolean;
};
'10'?: {
supported: boolean;
};
'11'?: {
supported: boolean;
};
'12'?: {
supported: boolean;
};
'14'?: {
supported: boolean;
};
'15'?: {
methods: MPPMethod[];
};
'17'?: {
supported: WebSocketSupport[];
};
'19'?: {
ttl: number | null;
cached_endpoints: Array<{
method: 'GET' | 'POST';
path: string;
}>;
};
'20'?: {
supported: boolean;
};
'21'?: {
openid_discovery: string;
client_id: string;
protected_endpoints?: Array<{
method: 'GET' | 'POST';
path: string;
}>;
};
'22'?: {
bat_max_mint: number;
protected_endpoints: Array<{
method: 'GET' | 'POST';
path: string;
}>;
};
'29'?: Nut29Info;
};
motd?: string;
};
/**
* Returns the amounts in the keyset sorted by the order specified.
*
* @param keyset To search in.
* @param order Order to sort the amounts in.
* @returns The amounts in the keyset sorted by the order specified.
*/
export declare function getKeysetAmounts(keyset: Keys, order?: 'asc' | 'desc'): Amount[];
export declare const getKeysetIdInt: (keysetId: string) => bigint;
/**
* NUT-02 Keysets API response (/v1/keysets)
*/
export declare type GetKeysetsResponse = {
/**
* Keysets.
*/
keysets: MintKeyset[];
};
/**
* NUT-01 Keys API response (/v1/keys)
*/
export declare type GetKeysResponse = {
/**
* Keysets.
*/
keysets: MintKeys[];
};
/**
* Returns the expected witness public keys from a NUT-11 P2PK secret.
*
* @remarks
* Does not tell you the pathway (Locktime or Refund MultiSig), only the keys that CAN currently
* sign. If no keys are returned, the proof is unlocked or expired with no refund path.
* @param secretStr - The NUT-11 P2PK secret.
* @returns Array of public keys or empty array.
* @throws If the secret is malformed or not P2PK.
*/
export declare function getP2PKExpectedWitnessPubkeys(secretStr: string | Secret): string[];
/**
* Returns the sigflag from a NUT-11 P2PK secret.
*
* @param secretStr - The NUT-11 P2PK secret.
* @returns The sigflag (`'SIG_INPUTS'` or `'SIG_ALL'`).
* @throws If secret is not P2PK, or if the sigflag tag contains an unrecognised value.
*/
export declare function getP2PKSigFlag(secretStr: string | Secret): SigFlag;
/**
* Gets witness signatures as an array.
*
* @param witness From Proof.
* @returns Array of witness signatures.
*/
export declare function getP2PKWitnessSignatures(witness: Proof['witness']): string[];
export declare function getPubKeyFromPrivKey(privKey: Uint8Array): Uint8Array<ArrayBufferLike>;
/**
* Get the SecretData payload (second element) of a Secret.
*
* @param secret - The Proof secret.
*/
export declare function getSecretData(secret: Secret | string): SecretData;
/**
* Get the kind (first element) of a Secret.
*
* @param secret - The Proof secret.
*/
export declare function getSecretKind(secret: Secret | string): SecretKind;
/**
* Get the values of a tag by key, excluding the key itself.
*
* @param secret - The Proof secret.
* @param key - Tag key to lookup.
* @returns - Array of Tag values or undefined if not present.
*/
export declare function getTag(secret: Secret | string, key: string): string[] | undefined;
/**
* Get the first scalar value of a tag parsed as base-10 integer, or undefined.
*
* @param secret - The Proof secret.
* @param key - Tag key to lookup.
* @returns - Tag value as an integer, undefined if not present or invalid.
*/
export declare function getTagInt(secret: Secret | string, key: string): number | undefined;
/**
* Get all tags from a secret.
*
* @param secret - The Proof secret.
* @returns - Array of tag arrays.
*/
export declare function getTags(secret: Secret | string): string[][];
/**
* Get the first scalar value of a tag as a string, or undefined if missing.
*
* @param secret - The Proof secret.
* @param key - Tag key to lookup.
* @returns - Tag value or undefined if not present.
*/
export declare function getTagScalar(secret: Secret | string, key: string): string | undefined;
/**
* Returns the metadata of a cashu token.
*
* @param token An encoded cashu token (cashuB...)
* @returns Token metadata.
*/
export declare function getTokenMetadata(token: string): TokenMetadata;
/**
* Returns the set of unique public keys that have produced a valid Schnorr signature for a given
* message.
*
* @param signatures - The Schnorr signature(s) (hex-encoded).
* @param message - The message to verify.
* @param pubkeys - The Cashu P2PK public key(s) (hex-encoded, X-only or with 02/03 prefix) to
* check.
* @returns Array of public keys who validly signed, duplicates removed.
*/
export declare function getValidSigners(signatures: string[], message: string, pubkeys: string[]): string[];
/**
* Checks if the provided amount is in the keyset.
*
* @param amount Amount to check.
* @param keyset To search in.
* @returns True if the amount is in the keyset, false otherwise.
*/
export declare function hasCorrespondingKey(amount: AmountLike, keyset: Keys): boolean;
export declare function hash_e(pubkeys: Array<WeierstrassPoint<bigint>>): Uint8Array;
export declare function hashToCurve(secret: Uint8Array): WeierstrassPoint<bigint>;
/**
* Minimal key carrier shape for low level helpers.
*
* Any type with Keyset `id` can be used, including MintKeyset, MintKeys, HasKeysetKeys, Keyset,
* KeysetCache.
*/
export declare type HasKeysetId = {
id: string;
};
/**
* Minimal key carrier shape for low level helpers.
*
* Any type with `id`, and `keys` can be used, including MintKeys, KeysetCache and Keyset.
*/
export declare type HasKeysetKeys = {
id: string;
keys: Keys;
};
/**
* Verifies a pubkey has signed a P2PK Proof.
*
* @param pubkey - The Cashu P2PK public key (hex-encoded, X-only or with 02/03 prefix).
* @param proof - A Cashu proof.
* @param message - Optional. The message that was signed (for SIG_ALL)
* @returns True if one of the signatures is theirs, false otherwise.
*/
export declare function hasP2PKSignedProof(pubkey: string, proof: Proof, message?: string): boolean;
/**
* Check if a secret has a tag with the given key.
*
* @param secret - The Proof secret.
* @param key - Tag key to lookup.
* @returns - True if tag exists, False otherwise.
*/
export declare function hasTag(secret: Secret | string, key: string): boolean;
/**
* Checks that the proof has a valid DLEQ proof according to keyset `keys`
*
* @param proof The proof subject to verification.
* @param keyset Object containing keyset keys (eg: Keyset, MintKeys, KeysetCache)
* @param opts.require Default `true`. When `false`, a proof without a DLEQ payload returns `true`
* (NUT-12 "MUST verify-if-present"). The default flips to `false` in v5.0.
* @returns True if verification succeeded, false otherwise.
* @throws Throws if the proof amount does not match any key in the provided keyset.
*/
export declare function hasValidDleq(proof: Proof, keyset: HasKeysetKeys, opts?: {
require?: boolean;
}): boolean;
/* Excluded from this release type: hexToNumber */
/**
* HTLC witness.
*/
export declare type HTLCWitness = {
/**
* Preimage.
*/
preimage: string;
/**
* An array of signatures in hex format.
*/
signatures?: string[];
};
/**
* This error is thrown when a HTTP response is not 2XX nor a protocol error.
*/
export declare class HttpResponseError extends CTSError {
status: number;
constructor(message: string, status: number, options?: {
cause?: unknown;
});
}
export declare function injectWebSocketImpl(ws: typeof WebSocket): void;
export declare type IntRange<F extends number, T extends number> = Exclude<Enumerate<T>, Enumerate<F>>;
/* Excluded from this release type: invoiceHasAmountInHRP */
/**
* Verify HTLC spending conditions for a single input.
*
* @param proof - The Proof to check.
* @param logger - Optional logger (default: NULL_LOGGER)
* @param message - Optional. The message to sign (for SIG_ALL)
* @returns True if spending conditions are satisfied, false otherwise.
* @throws If verification is impossible.
*/
export declare function isHTLCSpendAuthorised(proof: Proof, logger?: Logger, message?: string): boolean;
/* Excluded from this release type: isObj */
/* Excluded from this release type: isP2PKSigAll */
/**
* Verify P2PK spending conditions for a single input.
*
* @param proof - The Proof to check.
* @param logger - Optional logger (default: NULL_LOGGER)
* @param message - Optional. The message to sign (for SIG_ALL)
* @returns True if the witness threshold was reached, false otherwise.
* @throws If verification is impossible.
*/
export declare function isP2PKSpendAuthorised(proof: Proof, logger?: Logger, message?: string): boolean;
/* Excluded from this release type: isValidHex */
/* Excluded from this release type: joinUrls */
/**
* BigInt-safe JSON parser/stringifier.
*
* @remarks
* - Based on Crockford's JSON reference parser approach (recursive descent), adapted for BigInt.
* - Does not touch the global `JSON` object.
* - Stringifies BigInt as pure JSON numbers (no quotes, no `n`).
*
* Gotchas.
*
* - `s === JSONInt.stringify(JSONInt.parse(s))` is generally true for canonical JSON inputs.
* - `o !== JSONInt.parse(JSONInt.stringify(o))` can happen because:
*
* - BigInt is stringified as an unquoted JSON number token (loss of JS type on parse).
* - `undefined` values are dropped or become `null` in arrays, per JSON rules.
* - Custom `toJSON`/replacer behavior can change output.
*
* There is no consistent way to preserve BigInt type through JSON today, so handling that case is
* up to users. In Cashu-TS, we use the `Amount` VO to normalize numbers.
*/
export declare const JSONInt: JSONIntApi;
export declare interface JSONIntApi {
/**
* Bigint aware JSON parser.
*
* @remarks
* Returns `unknown`, so validate or cast the result to an application-specific type.
*
* Unquoted JSON number tokens are parsed to BigInt when outside the safe integer range, otherwise
* to number.
*/
parse(source: string, reviver?: (this: unknown, key: string, value: unknown) => unknown, options?: {
strict?: boolean;
fallbackTo?: 'number' | 'string' | 'error';
}): unknown;
/**
* BigInt aware JSON stringify.
*
* @remarks
* - BigInt is stringified as an unquoted JSON number token.
* - Parsing the result may yield `number` or `bigint` depending on the value and parse options.
* - Returns `undefined` for top-level values that JSON cannot represent, matching `JSON.stringify`
* behavior.
*/
stringify(value: unknown, replacer?: ((this: unknown, key: string, value: unknown) => unknown) | ReadonlyArray<string | number>, space?: string | number): string | undefined;
}
export declare type JsonRpcReqParams = {
kind: RpcSubKinds;
filters: string[];
subId: string;
};
/**
* Manages all keysets for a Mint. Queries filter by the wallet's unit.
*
* @remarks
* Stores keysets for every unit the mint exposes. Methods like `getKeysets()` and
* `getCheap