@cashu/cashu-ts
Version:
cashu library for communicating with a cashu mint
1,693 lines • 81.3 kB
JavaScript
import { Buffer as G } from "buffer";
import { verifyDLEQProof_reblind as Gt } from "./crypto/client/NUT12.es.js";
import { pointFromHex as ot, hashToCurve as Et } from "./crypto/common.es.js";
import { hexToBytes as Q, bytesToHex as z } from "@noble/curves/abstract/utils";
import { sha256 as Vt } from "@noble/hashes/sha256";
import { signP2PKProofs as St } from "./crypto/client/NUT11.es.js";
import { signMintQuote as Jt } from "./crypto/client/NUT20.es.js";
import { constructProofFromPromise as Xt, serializeProof as Yt, blindMessage as ht } from "./crypto/client.es.js";
import { hexToBytes as Pt, bytesToHex as X, randomBytes as It } from "@noble/hashes/utils";
import { deriveSecret as Zt, deriveBlindingFactor as te } from "./crypto/client/NUT09.es.js";
function ee(n) {
return G.from(n).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
}
function Rt(n) {
return G.from(n, "base64");
}
function xt(n) {
const t = JSON.stringify(n);
return re(G.from(t).toString("base64"));
}
function se(n) {
const t = G.from(ne(n), "base64").toString();
return JSON.parse(t);
}
function ne(n) {
return n.replace(/-/g, "+").replace(/_/g, "/").split("=")[0];
}
function re(n) {
return n.replace(/\+/g, "-").replace(/\//g, "_").split("=")[0];
}
function oe(n) {
return typeof n == "number" || typeof n == "string";
}
function yt(n) {
const t = [];
return gt(n, t), new Uint8Array(t);
}
function gt(n, t) {
if (n === null)
t.push(246);
else if (n === void 0)
t.push(247);
else if (typeof n == "boolean")
t.push(n ? 245 : 244);
else if (typeof n == "number")
Ut(n, t);
else if (typeof n == "string")
Ft(n, t);
else if (Array.isArray(n))
ae(n, t);
else if (n instanceof Uint8Array)
ie(n, t);
else if (
// Defensive: POJO only (null/array handled above)
typeof n == "object" && n !== null && !Array.isArray(n)
)
ce(n, t);
else
throw new Error("Unsupported type");
}
function Ut(n, t) {
if (n < 24)
t.push(n);
else if (n < 256)
t.push(24, n);
else if (n < 65536)
t.push(25, n >> 8, n & 255);
else if (n < 4294967296)
t.push(26, n >> 24, n >> 16 & 255, n >> 8 & 255, n & 255);
else
throw new Error("Unsupported integer size");
}
function ie(n, t) {
const e = n.length;
if (e < 24)
t.push(64 + e);
else if (e < 256)
t.push(88, e);
else if (e < 65536)
t.push(89, e >> 8 & 255, e & 255);
else if (e < 4294967296)
t.push(
90,
e >> 24 & 255,
e >> 16 & 255,
e >> 8 & 255,
e & 255
);
else
throw new Error("Byte string too long to encode");
for (let s = 0; s < n.length; s++)
t.push(n[s]);
}
function Ft(n, t) {
const e = new TextEncoder().encode(n), s = e.length;
if (s < 24)
t.push(96 + s);
else if (s < 256)
t.push(120, s);
else if (s < 65536)
t.push(121, s >> 8 & 255, s & 255);
else if (s < 4294967296)
t.push(
122,
s >> 24 & 255,
s >> 16 & 255,
s >> 8 & 255,
s & 255
);
else
throw new Error("String too long to encode");
for (let r = 0; r < e.length; r++)
t.push(e[r]);
}
function ae(n, t) {
const e = n.length;
if (e < 24)
t.push(128 | e);
else if (e < 256)
t.push(152, e);
else if (e < 65536)
t.push(153, e >> 8, e & 255);
else
throw new Error("Unsupported array length");
for (const s of n)
gt(s, t);
}
function ce(n, t) {
const e = Object.keys(n);
Ut(e.length, t), t[t.length - 1] |= 160;
for (const s of e)
Ft(s, t), gt(n[s], t);
}
function wt(n) {
const t = new DataView(n.buffer, n.byteOffset, n.byteLength);
return it(t, 0).value;
}
function it(n, t) {
if (t >= n.byteLength)
throw new Error("Unexpected end of data");
const e = n.getUint8(t++), s = e >> 5, r = e & 31;
switch (s) {
case 0:
return ue(n, t, r);
case 1:
return he(n, t, r);
case 2:
return le(n, t, r);
case 3:
return de(n, t, r);
case 4:
return fe(n, t, r);
case 5:
return pe(n, t, r);
case 7:
return ye(n, t, r);
default:
throw new Error(`Unsupported major type: ${s}`);
}
}
function V(n, t, e) {
if (e < 24) return { value: e, offset: t };
if (e === 24) return { value: n.getUint8(t++), offset: t };
if (e === 25) {
const s = n.getUint16(t, !1);
return t += 2, { value: s, offset: t };
}
if (e === 26) {
const s = n.getUint32(t, !1);
return t += 4, { value: s, offset: t };
}
if (e === 27) {
const s = n.getUint32(t, !1), r = n.getUint32(t + 4, !1);
return t += 8, { value: s * 2 ** 32 + r, offset: t };
}
throw new Error(`Unsupported length: ${e}`);
}
function ue(n, t, e) {
const { value: s, offset: r } = V(n, t, e);
return { value: s, offset: r };
}
function he(n, t, e) {
const { value: s, offset: r } = V(n, t, e);
return { value: -1 - s, offset: r };
}
function le(n, t, e) {
const { value: s, offset: r } = V(n, t, e);
if (r + s > n.byteLength)
throw new Error("Byte string length exceeds data length");
return { value: new Uint8Array(n.buffer, n.byteOffset + r, s), offset: r + s };
}
function de(n, t, e) {
const { value: s, offset: r } = V(n, t, e);
if (r + s > n.byteLength)
throw new Error("String length exceeds data length");
const o = new Uint8Array(n.buffer, n.byteOffset + r, s);
return { value: new TextDecoder().decode(o), offset: r + s };
}
function fe(n, t, e) {
const { value: s, offset: r } = V(n, t, e), o = [];
let a = r;
for (let i = 0; i < s; i++) {
const c = it(n, a);
o.push(c.value), a = c.offset;
}
return { value: o, offset: a };
}
function pe(n, t, e) {
const { value: s, offset: r } = V(n, t, e), o = {};
let a = r;
for (let i = 0; i < s; i++) {
const c = it(n, a);
if (!oe(c.value))
throw new Error("Invalid key type");
const h = it(n, c.offset);
o[c.value] = h.value, a = h.offset;
}
return { value: o, offset: a };
}
function me(n) {
const t = (n & 31744) >> 10, e = n & 1023, s = n & 32768 ? -1 : 1;
return t === 0 ? s * 2 ** -14 * (e / 1024) : t === 31 ? e ? NaN : s * (1 / 0) : s * 2 ** (t - 15) * (1 + e / 1024);
}
function ye(n, t, e) {
if (e < 24)
switch (e) {
case 20:
return { value: !1, offset: t };
case 21:
return { value: !0, offset: t };
case 22:
return { value: null, offset: t };
case 23:
return { value: void 0, offset: t };
default:
throw new Error(`Unknown simple value: ${e}`);
}
if (e === 24) return { value: n.getUint8(t++), offset: t };
if (e === 25) {
const s = me(n.getUint16(t, !1));
return t += 2, { value: s, offset: t };
}
if (e === 26) {
const s = n.getFloat32(t, !1);
return t += 4, { value: s, offset: t };
}
if (e === 27) {
const s = n.getFloat64(t, !1);
return t += 8, { value: s, offset: t };
}
throw new Error(`Unknown simple or float value: ${e}`);
}
class kt {
constructor(t, e, s, r, o, a, i = !1, c) {
this.transport = t, this.id = e, this.amount = s, this.unit = r, this.mints = o, this.description = a, this.singleUse = i, this.nut10 = c;
}
toRawRequest() {
const t = {};
return this.transport && (t.t = this.transport.map((e) => ({
t: e.type,
a: e.target,
g: e.tags
}))), this.id && (t.i = this.id), this.amount && (t.a = this.amount), this.unit && (t.u = this.unit), this.mints && (t.m = this.mints), this.description && (t.d = this.description), this.singleUse && (t.s = this.singleUse), this.nut10 && (t.nut10 = {
k: this.nut10.kind,
d: this.nut10.data,
t: this.nut10.tags
}), t;
}
toEncodedRequest() {
const t = this.toRawRequest(), e = yt(t);
return "creqA" + G.from(e).toString("base64");
}
getTransport(t) {
return this.transport?.find((e) => e.type === t);
}
static fromRawRequest(t) {
const e = t.t ? t.t.map((r) => ({
type: r.t,
target: r.a,
tags: r.g
})) : void 0, s = t.nut10 ? {
kind: t.nut10.k,
data: t.nut10.d,
tags: t.nut10.t
} : void 0;
return new kt(
e,
t.i,
t.a,
t.u,
t.m,
t.d,
t.s,
s
);
}
static fromEncodedRequest(t) {
if (!t.startsWith("creq"))
throw new Error("unsupported pr: invalid prefix");
if (t[4] !== "A")
throw new Error("unsupported pr version");
const s = t.slice(5), r = Rt(s), o = wt(r);
return this.fromRawRequest(o);
}
}
const ge = "A", we = "cashu";
function R(n, t, e, s) {
if (e) {
const o = Tt(e);
if (o > n)
throw new Error(`Split is greater than total amount: ${o} > ${n}`);
if (e.some((a) => !Nt(a, t)))
throw new Error("Provided amount preferences do not match the amounts of the mint keyset.");
n = n - Tt(e);
} else
e = [];
return Ot(t, "desc").forEach((o) => {
const a = Math.floor(n / o);
for (let i = 0; i < a; ++i) e?.push(o);
n %= o;
}), e.sort((o, a) => o - a);
}
function Mt(n, t, e, s) {
const r = [], o = n.map((h) => h.amount);
Ot(e, "asc").forEach((h) => {
const u = o.filter((f) => f === h).length, l = Math.max(s - u, 0);
for (let f = 0; f < l && !(r.reduce((d, g) => d + g, 0) + h > t); ++f)
r.push(h);
});
const i = t - r.reduce((h, u) => h + u, 0);
return i && R(i, e).forEach((u) => {
r.push(u);
}), r.sort((h, u) => h - u);
}
function Ot(n, t = "desc") {
return t == "desc" ? Object.keys(n).map((e) => parseInt(e)).sort((e, s) => s - e) : Object.keys(n).map((e) => parseInt(e)).sort((e, s) => e - s);
}
function Nt(n, t) {
return n in t;
}
function ke(n) {
return Bt(z(n));
}
function Bt(n) {
return BigInt(`0x${n}`);
}
function be(n) {
return n.toString(16).padStart(64, "0");
}
function vt(n) {
return /^[a-f0-9]*$/i.test(n);
}
function Lt(n) {
return Array.isArray(n) ? n.some((t) => !vt(t.id)) : vt(n.id);
}
function _e(n, t) {
t && (n.proofs = at(n.proofs));
const e = { token: [{ mint: n.mint, proofs: n.proofs }] };
return n.unit && (e.unit = n.unit), n.memo && (e.memo = n.memo), we + ge + xt(e);
}
function Xe(n, t) {
if (Lt(n.proofs) || t?.version === 3) {
if (t?.version === 4)
throw new Error("can not encode to v4 token if proofs contain non-hex keyset id");
return _e(n, t?.removeDleq);
}
return Ae(n, t?.removeDleq);
}
function Ae(n, t) {
if (t && (n.proofs = at(n.proofs)), n.proofs.forEach((c) => {
if (c.dleq && c.dleq.r == null)
throw new Error("Missing blinding factor in included DLEQ proof");
}), Lt(n.proofs))
throw new Error("can not encode to v4 token if proofs contain non-hex keyset id");
const s = Ct(n), r = yt(s), o = "cashu", a = "B", i = ee(r);
return o + a + i;
}
function Ct(n) {
const t = {}, e = n.mint;
for (let r = 0; r < n.proofs.length; r++) {
const o = n.proofs[r];
t[o.id] ? t[o.id].push(o) : t[o.id] = [o];
}
const s = {
m: e,
u: n.unit || "sat",
t: Object.keys(t).map(
(r) => ({
i: Q(r),
p: t[r].map(
(o) => ({
a: o.amount,
s: o.secret,
c: Q(o.C),
...o.dleq && {
d: {
e: Q(o.dleq.e),
s: Q(o.dleq.s),
r: Q(o.dleq.r ?? "00")
}
},
...o.witness && {
w: JSON.stringify(o.witness)
}
})
)
})
)
};
return n.memo && (s.d = n.memo), s;
}
function Qt(n) {
const t = [];
n.t.forEach(
(s) => s.p.forEach((r) => {
t.push({
secret: r.s,
C: z(r.c),
amount: r.a,
id: z(s.i),
...r.d && {
dleq: {
r: z(r.d.r),
s: z(r.d.s),
e: z(r.d.e)
}
},
...r.w && {
witness: r.w
}
});
})
);
const e = { mint: n.m, proofs: t, unit: n.u || "sat" };
return n.d && (e.memo = n.d), e;
}
function Ee(n) {
return ["web+cashu://", "cashu://", "cashu:", "cashu"].forEach((e) => {
n.startsWith(e) && (n = n.slice(e.length));
}), Se(n);
}
function Se(n) {
const t = n.slice(0, 1), e = n.slice(1);
if (t === "A") {
const s = se(e);
if (s.token.length > 1)
throw new Error("Multi entry token are not supported");
const r = s.token[0], o = {
mint: r.mint,
proofs: r.proofs,
unit: s.unit || "sat"
};
return s.memo && (o.memo = s.memo), o;
} else if (t === "B") {
const s = Rt(e), r = wt(s);
return Qt(r);
}
throw new Error("Token version is not supported");
}
function qt(n) {
return Pe(n.keys) === n.id;
}
function Pe(n) {
const t = Object.entries(n).sort((r, o) => +r[0] - +o[0]).map(([, r]) => Q(r)).reduce((r, o) => Ie(r, o), new Uint8Array()), e = Vt(t);
return "00" + G.from(e).toString("hex").slice(0, 14);
}
function Ie(n, t) {
const e = new Uint8Array(n.length + t.length);
return e.set(n), e.set(t, n.length), e;
}
function F(n) {
return typeof n == "object";
}
function T(...n) {
return n.map((t) => t.replace(/(^\/+|\/+$)/g, "")).join("/");
}
function Wt(n) {
return n.replace(/\/$/, "");
}
function W(n) {
return n.reduce((t, e) => t + e.amount, 0);
}
function Ye(n) {
return kt.fromEncodedRequest(n);
}
class Me {
get value() {
return this._value;
}
set value(t) {
this._value = t;
}
get next() {
return this._next;
}
set next(t) {
this._next = t;
}
constructor(t) {
this._value = t, this._next = null;
}
}
class ve {
get first() {
return this._first;
}
set first(t) {
this._first = t;
}
get last() {
return this._last;
}
set last(t) {
this._last = t;
}
get size() {
return this._size;
}
set size(t) {
this._size = t;
}
constructor() {
this._first = null, this._last = null, this._size = 0;
}
enqueue(t) {
const e = new Me(t);
return this._size === 0 || !this._last ? (this._first = e, this._last = e) : (this._last.next = e, this._last = e), this._size++, !0;
}
dequeue() {
if (this._size === 0 || !this._first) return null;
const t = this._first;
return this._first = t.next, t.next = null, this._size--, t.value;
}
}
function at(n) {
return n.map((t) => {
const e = { ...t };
return delete e.dleq, e;
});
}
function jt(n, t) {
if (n.dleq == null)
return !1;
const e = {
e: Q(n.dleq.e),
s: Q(n.dleq.s),
r: Bt(n.dleq.r ?? "00")
};
if (!Nt(n.amount, t.keys))
throw new Error(`undefined key for amount ${n.amount}`);
const s = t.keys[n.amount];
return !!Gt(
new TextEncoder().encode(n.secret),
e,
ot(n.C),
ot(s)
);
}
function qe(...n) {
const t = n.reduce((r, o) => r + o.length, 0), e = new Uint8Array(t);
let s = 0;
for (let r = 0; r < n.length; r++)
e.set(n[r], s), s = s + n[r].length;
return e;
}
function Ze(n) {
const t = new TextEncoder(), e = Ct(n), s = yt(e), r = t.encode("craw"), o = t.encode("B");
return qe(r, o, s);
}
function ts(n) {
const t = new TextDecoder(), e = t.decode(n.slice(0, 4)), s = t.decode(new Uint8Array([n[4]]));
if (e !== "craw" || s !== "B")
throw new Error("not a valid binary token");
const r = n.slice(5), o = wt(r);
return Qt(o);
}
function Tt(n) {
return n.reduce((t, e) => t + e, 0);
}
let ct;
typeof WebSocket < "u" && (ct = WebSocket);
function es(n) {
ct = n;
}
function Te() {
if (ct === void 0)
throw new Error("WebSocket implementation not initialized");
return ct;
}
const M = {
FATAL: "FATAL",
ERROR: "ERROR",
WARN: "WARN",
INFO: "INFO",
DEBUG: "DEBUG",
TRACE: "TRACE"
}, O = {
fatal() {
},
error() {
},
warn() {
},
info() {
},
debug() {
},
trace() {
},
log() {
}
}, Z = class Z {
constructor(t = M.INFO) {
this.minLevel = t;
}
logToConsole(t, e, s) {
if (Z.SEVERITY[t] > Z.SEVERITY[this.minLevel]) return;
const r = `[${t}] `;
let o = e;
const a = /* @__PURE__ */ new Set();
if (s) {
const i = Object.fromEntries(
Object.entries(s).map(([u, l]) => [
u,
l instanceof Error ? { message: l.message, stack: l.stack } : l
])
);
o = e.replace(/\{(\w+)\}/g, (u, l) => {
if (l in i && i[l] !== void 0) {
a.add(l);
const f = i[l];
return typeof f == "string" ? f : typeof f == "number" || typeof f == "boolean" ? f.toString() : f == null ? "" : JSON.stringify(f);
}
return u;
});
const c = Object.fromEntries(
Object.entries(i).filter(([u]) => !a.has(u))
), h = this.getConsoleMethod(t);
Object.keys(c).length > 0 ? h(r + o, c) : h(r + o);
} else
this.getConsoleMethod(t)(r + o);
}
// Note: NOT static as test suite needs to spy on the output
getConsoleMethod(t) {
switch (t) {
case M.FATAL:
case M.ERROR:
return console.error;
case M.WARN:
return console.warn;
case M.INFO:
return console.info;
case M.DEBUG:
return console.debug;
case M.TRACE:
return console.trace;
default:
return console.log;
}
}
// Interface methods
fatal(t, e) {
this.logToConsole(M.FATAL, t, e);
}
error(t, e) {
this.logToConsole(M.ERROR, t, e);
}
warn(t, e) {
this.logToConsole(M.WARN, t, e);
}
info(t, e) {
this.logToConsole(M.INFO, t, e);
}
debug(t, e) {
this.logToConsole(M.DEBUG, t, e);
}
trace(t, e) {
this.logToConsole(M.TRACE, t, e);
}
log(t, e, s) {
this.logToConsole(t, e, s);
}
};
Z.SEVERITY = {
[M.FATAL]: 0,
[M.ERROR]: 1,
[M.WARN]: 2,
[M.INFO]: 3,
[M.DEBUG]: 4,
[M.TRACE]: 5
};
let Kt = Z;
function Ke() {
const n = Date.now();
return {
elapsed: () => Date.now() - n
};
}
class H {
constructor() {
this.connectionMap = /* @__PURE__ */ new Map();
}
static getInstance() {
return H.instance || (H.instance = new H()), H.instance;
}
getConnection(t, e) {
if (this.connectionMap.has(t))
return this.connectionMap.get(t);
const s = new De(t, e);
return this.connectionMap.set(t, s), s;
}
}
class De {
constructor(t, e) {
this.subListeners = {}, this.rpcListeners = {}, this.rpcId = 0, this.onCloseCallbacks = [], this._WS = Te(), this.url = new URL(t), this.messageQueue = new ve(), this._logger = e ?? O;
}
connect() {
return this.connectionPromise || (this.connectionPromise = new Promise((t, e) => {
try {
this.ws = new this._WS(this.url.toString()), this.onCloseCallbacks = [];
} catch (s) {
e(s instanceof Error ? s : new Error(String(s)));
return;
}
this.ws.onopen = () => {
t();
}, this.ws.onerror = () => {
e(new Error("Failed to open WebSocket"));
}, this.ws.onmessage = (s) => {
this.messageQueue.enqueue(s.data), this.handlingInterval || (this.handlingInterval = setInterval(
this.handleNextMessage.bind(this),
0
));
}, this.ws.onclose = (s) => {
this.connectionPromise = void 0, this.onCloseCallbacks.forEach((r) => r(s));
};
})), this.connectionPromise;
}
sendRequest(t, e) {
if (this.ws?.readyState !== 1) {
if (t === "unsubscribe")
return;
throw this._logger.error("Attempted sendRequest, but socket was not open"), new Error("Socket not open");
}
const s = this.rpcId;
this.rpcId++;
const r = JSON.stringify({ jsonrpc: "2.0", method: t, params: e, id: s });
this.ws?.send(r);
}
/**
* @deprecated Use cancelSubscription for JSONRPC compliance.
*/
closeSubscription(t) {
this.ws?.send(JSON.stringify(["CLOSE", t]));
}
addSubListener(t, e) {
(this.subListeners[t] = this.subListeners[t] || []).push(
e
);
}
addRpcListener(t, e, s) {
this.rpcListeners[s] = { callback: t, errorCallback: e };
}
removeRpcListener(t) {
delete this.rpcListeners[t];
}
removeListener(t, e) {
if (this.subListeners[t]) {
if (this.subListeners[t].length === 1) {
delete this.subListeners[t];
return;
}
this.subListeners[t] = this.subListeners[t].filter(
(s) => s !== e
);
}
}
async ensureConnection() {
this.ws?.readyState !== 1 && await this.connect();
}
handleNextMessage() {
if (this.messageQueue.size === 0) {
clearInterval(this.handlingInterval), this.handlingInterval = void 0;
return;
}
const t = this.messageQueue.dequeue();
let e;
try {
if (e = JSON.parse(t), "result" in e && e.id != null)
this.rpcListeners[e.id] && (this.rpcListeners[e.id].callback(), this.removeRpcListener(e.id));
else if ("error" in e && e.id != null)
this.rpcListeners[e.id] && (this.rpcListeners[e.id].errorCallback(new Error(e.error.message)), this.removeRpcListener(e.id));
else if ("method" in e && !("id" in e)) {
const s = e.params?.subId;
if (!s)
return;
if (this.subListeners[s]?.length > 0) {
const r = e;
this.subListeners[s].forEach((o) => o(r.params?.payload));
}
}
} catch (s) {
this._logger.error("Error doing handleNextMessage", { e: s });
return;
}
}
createSubscription(t, e, s) {
if (this.ws?.readyState !== 1)
throw this._logger.error("Attempted createSubscription, but socket was not open"), new Error("Socket is not open");
const r = (Math.random() + 1).toString(36).substring(7);
return this.addRpcListener(
() => {
this.addSubListener(r, e);
},
s,
this.rpcId
), this.sendRequest("subscribe", { ...t, subId: r }), this.rpcId++, r;
}
/**
* Cancels a subscription, sending an unsubscribe request and handling responses.
*
* @param subId The subscription ID to cancel.
* @param callback The original payload callback to remove.
* @param errorCallback Optional callback for unsubscribe errors (defaults to logging).
*/
cancelSubscription(t, e, s) {
this.removeListener(t, e), this.addRpcListener(
() => {
this._logger.info("Unsubscribed {subId}", { subId: t });
},
s || ((r) => this._logger.error("Unsubscribe failed", { e: r })),
this.rpcId
), this.sendRequest("unsubscribe", { subId: t });
}
get activeSubscriptions() {
return Object.keys(this.subListeners);
}
close() {
this.ws && this.ws?.close();
}
onClose(t) {
this.onCloseCallbacks.push(t);
}
}
const ss = {
UNSPENT: "UNSPENT",
PENDING: "PENDING",
SPENT: "SPENT"
}, tt = {
UNPAID: "UNPAID",
PENDING: "PENDING",
PAID: "PAID"
}, pt = {
UNPAID: "UNPAID",
PAID: "PAID",
ISSUED: "ISSUED"
};
var Re = /* @__PURE__ */ ((n) => (n.POST = "post", n.NOSTR = "nostr", n))(Re || {});
class et extends Error {
constructor(t, e) {
super(t), this.status = e, this.name = "HttpResponseError", Object.setPrototypeOf(this, et.prototype);
}
}
class bt extends Error {
constructor(t) {
super(t), this.name = "NetworkError", Object.setPrototypeOf(this, bt.prototype);
}
}
class _t extends et {
constructor(t, e) {
super(e || "Unknown mint operation error", 400), this.code = t, this.name = "MintOperationError", Object.setPrototypeOf(this, _t.prototype);
}
}
let $t = {}, zt = O;
function ns(n) {
$t = n;
}
function xe(n) {
zt = n;
}
async function Ue({
endpoint: n,
requestBody: t,
headers: e,
...s
}) {
const r = t ? JSON.stringify(t) : void 0, o = {
Accept: "application/json, text/plain, */*",
...r ? { "Content-Type": "application/json" } : void 0,
...e
};
let a;
try {
a = await fetch(n, { body: r, headers: o, ...s });
} catch (i) {
throw new bt(i instanceof Error ? i.message : "Network request failed");
}
if (!a.ok) {
let i;
try {
i = await a.json();
} catch {
i = { error: "bad response" };
}
if (a.status === 400 && "code" in i && typeof i.code == "number" && "detail" in i && typeof i.detail == "string")
throw new _t(i.code, i.detail);
let c = "HTTP request failed";
throw "error" in i && typeof i.error == "string" ? c = i.error : "detail" in i && typeof i.detail == "string" && (c = i.detail), new et(c, a.status);
}
try {
return await a.json();
} catch (i) {
throw zt.error("Failed to parse HTTP response", { err: i }), new et("bad response", a.status);
}
}
async function K(n) {
return await Ue({ ...n, ...$t });
}
function lt(n, t) {
return n.state || (t.warn(
"Field 'state' not found in MeltQuoteResponse. Update NUT-05 of mint: https://github.com/cashubtc/nuts/pull/136)"
), typeof n.paid == "boolean" && (n.state = n.paid ? tt.PAID : tt.UNPAID)), n;
}
function Dt(n, t) {
return n.state || (t.warn(
"Field 'state' not found in MintQuoteResponse. Update NUT-04 of mint: https://github.com/cashubtc/nuts/pull/141)"
), typeof n.paid == "boolean" && (n.state = n.paid ? pt.PAID : pt.UNPAID)), n;
}
function Fe(n, t) {
return Array.isArray(n?.contact) && n?.contact.length > 0 && (n.contact = n.contact.map((e) => Array.isArray(e) && e.length === 2 && typeof e[0] == "string" && typeof e[1] == "string" ? (t.warn(
"Mint returned deprecated 'contact' field: Update NUT-06: https://github.com/cashubtc/nuts/pull/117"
), { method: e[0], info: e[1] }) : e)), n;
}
class mt {
constructor(t) {
this._mintInfo = t, t.nuts[22] && (this._protectedEnpoints = {
cache: {},
apiReturn: t.nuts[22].protected_endpoints.map((e) => ({
method: e.method,
regex: new RegExp(e.path)
}))
});
}
isSupported(t) {
switch (t) {
case 4:
case 5:
return this.checkMintMelt(t);
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 14:
case 20:
return this.checkGenericNut(t);
case 17:
return this.checkNut17();
case 15:
return this.checkNut15();
default:
throw new Error("nut is not supported by cashu-ts");
}
}
requiresBlindAuthToken(t) {
if (!this._protectedEnpoints)
return !1;
if (typeof this._protectedEnpoints.cache[t] == "boolean")
return this._protectedEnpoints.cache[t];
const e = this._protectedEnpoints.apiReturn.some((s) => s.regex.test(t));
return this._protectedEnpoints.cache[t] = e, e;
}
checkGenericNut(t) {
return this._mintInfo.nuts[t]?.supported ? { supported: !0 } : { supported: !1 };
}
checkMintMelt(t) {
const e = this._mintInfo.nuts[t];
return e && e.methods.length > 0 && !e.disabled ? { disabled: !1, params: e.methods } : { disabled: !0, params: e.methods };
}
checkNut17() {
return this._mintInfo.nuts[17] && this._mintInfo.nuts[17].supported.length > 0 ? { supported: !0, params: this._mintInfo.nuts[17].supported } : { supported: !1 };
}
checkNut15() {
return this._mintInfo.nuts[15] && this._mintInfo.nuts[15].methods.length > 0 ? { supported: !0, params: this._mintInfo.nuts[15].methods } : { supported: !1 };
}
get contact() {
return this._mintInfo.contact;
}
get description() {
return this._mintInfo.description;
}
get description_long() {
return this._mintInfo.description_long;
}
get name() {
return this._mintInfo.name;
}
get pubkey() {
return this._mintInfo.pubkey;
}
get nuts() {
return this._mintInfo.nuts;
}
get version() {
return this._mintInfo.version;
}
get motd() {
return this._mintInfo.motd;
}
}
class D {
/**
* @param _mintUrl Requires mint URL to create this object.
* @param _customRequest If passed, use custom request implementation for network communication
* with the mint.
* @param [authTokenGetter] A function that is called by the CashuMint instance to obtain a NUT-22
* BlindedAuthToken (e.g. from a database or localstorage)
*/
constructor(t, e, s, r) {
this._mintUrl = t, this._customRequest = e, this._checkNut22 = !1, this._mintUrl = Wt(t), this._customRequest = e, s && (this._checkNut22 = !0, this._authTokenGetter = s), this._logger = r?.logger ?? O, xe(this._logger);
}
//TODO: v3 - refactor CashuMint to take two or less args.
get mintUrl() {
return this._mintUrl;
}
/**
* Fetches mints info at the /info endpoint.
*
* @param mintUrl
* @param customRequest
*/
static async getInfo(t, e, s) {
const r = s ?? O, a = await (e || K)({
endpoint: T(t, "/v1/info")
});
return Fe(a, r);
}
/**
* Fetches mints info at the /info endpoint.
*/
async getInfo() {
return D.getInfo(this._mintUrl, this._customRequest, this._logger);
}
async getLazyMintInfo() {
if (this._mintInfo)
return this._mintInfo;
const t = await D.getInfo(this._mintUrl, this._customRequest);
return this._mintInfo = new mt(t), this._mintInfo;
}
/**
* Performs a swap operation with ecash inputs and outputs.
*
* @param mintUrl
* @param swapPayload Payload containing inputs and outputs.
* @param customRequest
* @returns Signed outputs.
*/
static async swap(t, e, s, r) {
const o = s || K, a = r ? { "Blind-auth": r } : {}, i = await o({
endpoint: T(t, "/v1/swap"),
method: "POST",
requestBody: e,
headers: a
});
if (!F(i) || !Array.isArray(i?.signatures))
throw new Error(i.detail ?? "bad response");
return i;
}
/**
* Performs a swap operation with ecash inputs and outputs.
*
* @param swapPayload Payload containing inputs and outputs.
* @returns Signed outputs.
*/
async swap(t) {
const e = await this.handleBlindAuth("/v1/swap");
return D.swap(this._mintUrl, t, this._customRequest, e);
}
/**
* Requests a new mint quote from the mint.
*
* @param mintUrl
* @param mintQuotePayload Payload for creating a new mint quote.
* @param customRequest
* @returns The mint will create and return a new mint quote containing a payment request for the
* specified amount and unit.
*/
static async createMintQuote(t, e, s, r, o) {
const a = o ?? O, i = s || K, c = r ? { "Blind-auth": r } : {}, h = await i({
endpoint: T(t, "/v1/mint/quote/bolt11"),
method: "POST",
requestBody: e,
headers: c
});
return Dt(h, a);
}
/**
* Requests a new mint quote from the mint.
*
* @param mintQuotePayload Payload for creating a new mint quote.
* @returns The mint will create and return a new mint quote containing a payment request for the
* specified amount and unit.
*/
async createMintQuote(t) {
const e = await this.handleBlindAuth("/v1/mint/quote/bolt11");
return D.createMintQuote(
this._mintUrl,
t,
this._customRequest,
e
);
}
/**
* Gets an existing mint quote from the mint.
*
* @param mintUrl
* @param quote Quote ID.
* @param customRequest
* @returns The mint will create and return a Lightning invoice for the specified amount.
*/
static async checkMintQuote(t, e, s, r, o) {
const a = o ?? O, i = s || K, c = r ? { "Blind-auth": r } : {}, h = await i({
endpoint: T(t, "/v1/mint/quote/bolt11", e),
method: "GET",
headers: c
});
return Dt(h, a);
}
/**
* Gets an existing mint quote from the mint.
*
* @param quote Quote ID.
* @returns The mint will create and return a Lightning invoice for the specified amount.
*/
async checkMintQuote(t) {
const e = await this.handleBlindAuth(`/v1/mint/quote/bolt11/${t}`);
return D.checkMintQuote(this._mintUrl, t, this._customRequest, e);
}
/**
* Mints new tokens by requesting blind signatures on the provided outputs.
*
* @param mintUrl
* @param mintPayload Payload containing the outputs to get blind signatures on.
* @param customRequest
* @returns Serialized blinded signatures.
*/
static async mint(t, e, s, r) {
const o = s || K, a = r ? { "Blind-auth": r } : {}, i = await o({
endpoint: T(t, "/v1/mint/bolt11"),
method: "POST",
requestBody: e,
headers: a
});
if (!F(i) || !Array.isArray(i?.signatures))
throw new Error("bad response");
return i;
}
/**
* Mints new tokens by requesting blind signatures on the provided outputs.
*
* @param mintPayload Payload containing the outputs to get blind signatures on.
* @returns Serialized blinded signatures.
*/
async mint(t) {
const e = await this.handleBlindAuth("/v1/mint/bolt11");
return D.mint(this._mintUrl, t, this._customRequest, e);
}
/**
* Requests a new melt quote from the mint.
*
* @param mintUrl
* @param MeltQuotePayload
* @returns
*/
static async createMeltQuote(t, e, s, r, o) {
const a = o ?? O, i = s || K, c = r ? { "Blind-auth": r } : {}, h = await i({
endpoint: T(t, "/v1/melt/quote/bolt11"),
method: "POST",
requestBody: e,
headers: c
}), u = lt(h, a);
if (!F(u) || typeof u?.amount != "number" || typeof u?.fee_reserve != "number" || typeof u?.quote != "string")
throw new Error("bad response");
return u;
}
/**
* Requests a new melt quote from the mint.
*
* @param MeltQuotePayload
* @returns
*/
async createMeltQuote(t) {
const e = await this.handleBlindAuth("/v1/melt/quote/bolt11");
return D.createMeltQuote(
this._mintUrl,
t,
this._customRequest,
e
);
}
/**
* Gets an existing melt quote.
*
* @param mintUrl
* @param quote Quote ID.
* @returns
*/
static async checkMeltQuote(t, e, s, r, o) {
const a = o ?? O, i = s || K, c = r ? { "Blind-auth": r } : {}, h = await i({
endpoint: T(t, "/v1/melt/quote/bolt11", e),
method: "GET",
headers: c
}), u = lt(h, a);
if (!F(u) || typeof u?.amount != "number" || typeof u?.fee_reserve != "number" || typeof u?.quote != "string" || typeof u?.state != "string" || !Object.values(tt).includes(u.state))
throw new Error("bad response");
return u;
}
/**
* Gets an existing melt quote.
*
* @param quote Quote ID.
* @returns
*/
async checkMeltQuote(t) {
const e = await this.handleBlindAuth(`/v1/melt/quote/bolt11/${t}`);
return D.checkMeltQuote(this._mintUrl, t, this._customRequest, e);
}
/**
* Requests the mint to pay for a Bolt11 payment request by providing ecash as inputs to be spent.
* The inputs contain the amount and the fee_reserves for a Lightning payment. The payload can
* also contain blank outputs in order to receive back overpaid Lightning fees.
*
* @param mintUrl
* @param meltPayload
* @param customRequest
* @returns
*/
static async melt(t, e, s, r, o) {
const a = o ?? O, i = s || K, c = r ? { "Blind-auth": r } : {}, h = await i({
endpoint: T(t, "/v1/melt/bolt11"),
method: "POST",
requestBody: e,
headers: c
}), u = lt(h, a);
if (!F(u) || typeof u?.state != "string" || !Object.values(tt).includes(u.state))
throw new Error("bad response");
return u;
}
/**
* Ask mint to perform a melt operation. This pays a lightning invoice and destroys tokens
* matching its amount + fees.
*
* @param meltPayload
* @returns
*/
async melt(t) {
const e = await this.handleBlindAuth("/v1/melt/bolt11");
return D.melt(this._mintUrl, t, this._customRequest, e);
}
/**
* Checks if specific proofs have already been redeemed.
*
* @param mintUrl
* @param checkPayload
* @param customRequest
* @returns Redeemed and unredeemed ordered list of booleans.
*/
static async check(t, e, s) {
const o = await (s || K)({
endpoint: T(t, "/v1/checkstate"),
method: "POST",
requestBody: e
});
if (!F(o) || !Array.isArray(o?.states))
throw new Error("bad response");
return o;
}
/**
* Get the mints public keys.
*
* @param mintUrl
* @param keysetId Optional param to get the keys for a specific keyset. If not specified, the
* keys from all active keysets are fetched.
* @param customRequest
* @returns
*/
static async getKeys(t, e, s) {
e && (e = e.replace(/\//g, "_").replace(/\+/g, "-"));
const o = await (s || K)({
endpoint: e ? T(t, "/v1/keys", e) : T(t, "/v1/keys")
});
if (!F(o) || !Array.isArray(o.keysets))
throw new Error("bad response");
return o;
}
/**
* Get the mints public keys.
*
* @param keysetId Optional param to get the keys for a specific keyset. If not specified, the
* keys from all active keysets are fetched.
* @returns The mints public keys.
*/
async getKeys(t, e) {
return await D.getKeys(
e || this._mintUrl,
t,
this._customRequest
);
}
/**
* Get the mints keysets in no specific order.
*
* @param mintUrl
* @param customRequest
* @returns All the mints past and current keysets.
*/
static async getKeySets(t, e) {
return (e || K)({ endpoint: T(t, "/v1/keysets") });
}
/**
* Get the mints keysets in no specific order.
*
* @returns All the mints past and current keysets.
*/
async getKeySets() {
return D.getKeySets(this._mintUrl, this._customRequest);
}
/**
* Checks if specific proofs have already been redeemed.
*
* @param checkPayload
* @returns Redeemed and unredeemed ordered list of booleans.
*/
async check(t) {
return D.check(this._mintUrl, t, this._customRequest);
}
static async restore(t, e, s) {
const o = await (s || K)({
endpoint: T(t, "/v1/restore"),
method: "POST",
requestBody: e
});
if (!F(o) || !Array.isArray(o?.outputs) || !Array.isArray(o?.signatures))
throw new Error("bad response");
return o;
}
async restore(t) {
return D.restore(this._mintUrl, t, this._customRequest);
}
/**
* Tries to establish a websocket connection with the websocket mint url according to NUT-17.
*/
async connectWebSocket() {
if (this.ws)
await this.ws.ensureConnection();
else {
const t = new URL(this._mintUrl), e = "v1/ws";
t.pathname && (t.pathname.endsWith("/") ? t.pathname += e : t.pathname += "/" + e), this.ws = H.getInstance().getConnection(
`${t.protocol === "https:" ? "wss" : "ws"}://${t.host}${t.pathname}`
);
try {
await this.ws.connect();
} catch (s) {
throw this._logger.error("Failed to connect to WebSocket...", { e: s }), new Error("Failed to connect to WebSocket...");
}
}
}
/**
* Closes a websocket connection.
*/
disconnectWebSocket() {
this.ws && this.ws.close();
}
get webSocketConnection() {
return this.ws;
}
async handleBlindAuth(t) {
if (!this._checkNut22)
return;
if ((await this.getLazyMintInfo()).requiresBlindAuthToken(t)) {
if (!this._authTokenGetter)
throw new Error("Can not call a protected endpoint without authProofGetter");
return this._authTokenGetter();
}
}
}
class dt {
constructor(t, e, s) {
this.amount = t, this.B_ = e, this.id = s;
}
getSerializedBlindedMessage() {
return { amount: this.amount, B_: this.B_.toHex(!0), id: this.id };
}
}
function ft(n) {
return typeof n == "function";
}
class N {
constructor(t, e, s) {
this.secret = s, this.blindingFactor = e, this.blindedMessage = t;
}
toProof(t, e) {
let s;
t.dleq && (s = {
s: Pt(t.dleq.s),
e: Pt(t.dleq.e),
r: this.blindingFactor
});
const r = {
id: t.id,
amount: t.amount,
C_: ot(t.C_)
}, o = ot(e.keys[t.amount]), a = Xt(r, this.blindingFactor, this.secret, o);
return {
...Yt(a),
...s && {
dleq: {
s: X(s.s),
e: X(s.e),
r: be(s.r ?? BigInt(0))
}
}
};
}
static createP2PKData(t, e, s, r) {
return R(e, s.keys, r).map((a) => this.createSingleP2PKData(t, a, s.id));
}
static createSingleP2PKData(t, e, s) {
const r = Array.isArray(t.pubkey) ? t.pubkey : [t.pubkey], o = Math.max(1, Math.min(t.requiredSignatures || 1, r.length)), a = Math.max(
1,
Math.min(t.requiredRefundSignatures || 1, t.refundKeys ? t.refundKeys.length : 1)
), i = [
"P2PK",
{
nonce: X(It(32)),
data: r[0],
// Primary key
tags: []
}
];
t.locktime && i[1].tags.push(["locktime", String(t.locktime)]), r.length > 1 && (i[1].tags.push(["pubkeys", ...r.slice(1)]), o > 1 && i[1].tags.push(["n_sigs", String(o)])), t.refundKeys && (i[1].tags.push(["refund", ...t.refundKeys]), a > 1 && i[1].tags.push(["n_sigs_refund", String(a)]));
const c = JSON.stringify(i), h = new TextEncoder().encode(c), { r: u, B_: l } = ht(h);
return new N(
new dt(e, l, s).getSerializedBlindedMessage(),
u,
h
);
}
static createRandomData(t, e, s) {
return R(t, e.keys, s).map((o) => this.createSingleRandomData(o, e.id));
}
static createSingleRandomData(t, e) {
const s = X(It(32)), r = new TextEncoder().encode(s), { r: o, B_: a } = ht(r);
return new N(
new dt(t, a, e).getSerializedBlindedMessage(),
o,
r
);
}
static createDeterministicData(t, e, s, r, o) {
return R(t, r.keys, o).map(
(i, c) => this.createSingleDeterministicData(i, e, s + c, r.id)
);
}
static createSingleDeterministicData(t, e, s, r) {
const o = Zt(e, r, s), a = X(o), i = new TextEncoder().encode(a), c = ke(te(e, r, s)), { r: h, B_: u } = ht(i, c);
return new N(
new dt(t, u, r).getSerializedBlindedMessage(),
h,
i
);
}
}
const Oe = 3, Ne = "sat";
class rs {
/**
* @param mint Cashu mint instance is used to make api calls.
* @param options.unit Optionally set unit (default is 'sat')
* @param options.keys Public keys from the mint (will be fetched from mint if not provided)
* @param options.keysets Keysets from the mint (will be fetched from mint if not provided)
* @param options.mintInfo Mint info from the mint (will be fetched from mint if not provided)
* @param options.denominationTarget Target number proofs per denomination (default: see @constant
* DEFAULT_DENOMINATION_TARGET)
* @param options.bip39seed BIP39 seed for deterministic secrets.
* @param options.keepFactory A function that will be used by all parts of the library that
* produce proofs to be kept (change, etc.). This can lead to poor performance, in which case
* the seed should be directly provided.
*/
constructor(t, e) {
this._keys = /* @__PURE__ */ new Map(), this._keysets = [], this._seed = void 0, this._unit = Ne, this._mintInfo = void 0, this._denominationTarget = Oe, this.mint = t, this._logger = e?.logger ?? O;
let s = [];
if (e?.keys && !Array.isArray(e.keys) ? s = [e.keys] : e?.keys && Array.isArray(e?.keys) && (s = e?.keys), s && s.forEach((r) => this._keys.set(r.id, r)), e?.unit && (this._unit = e?.unit), e?.keysets && (this._keysets = e.keysets), e?.mintInfo && (this._mintInfo = new mt(e.mintInfo)), e?.denominationTarget && (this._denominationTarget = e.denominationTarget), e?.bip39seed) {
if (e.bip39seed instanceof Uint8Array) {
this._seed = e.bip39seed;
return;
}
throw new Error("bip39seed must be a valid UInt8Array");
}
e?.keepFactory && (this._keepFactory = e.keepFactory);
}
get unit() {
return this._unit;
}
get keys() {
return this._keys;
}
get keysetId() {
if (!this._keysetId)
throw new Error("No keysetId set");
return this._keysetId;
}
set keysetId(t) {
this._keysetId = t;
}
get keysets() {
return this._keysets;
}
get mintInfo() {
if (!this._mintInfo)
throw new Error("Mint info not loaded");
return this._mintInfo;
}
/**
* Get information about the mint.
*
* @returns Mint info.
*/
async getMintInfo() {
const t = await this.mint.getInfo();
return this._mintInfo = new mt(t), this._mintInfo;
}
/**
* Get stored information about the mint or request it if not loaded.
*
* @returns Mint info.
*/
async lazyGetMintInfo() {
return this._mintInfo ? this._mintInfo : await this.getMintInfo();
}
/**
* Load mint information, keysets and keys. This function can be called if no keysets are passed
* in the constructor.
*/
async loadMint() {
await this.getMintInfo(), await this.getKeySets(), await this.getKeys();
}
/**
* Choose a keyset to activate based on the lowest input fee.
*
* Note: this function will filter out deprecated base64 keysets.
*
* @param keysets Keysets to choose from.
* @returns Active keyset.
*/
getActiveKeyset(t) {
let e = t.filter((r) => r.active && r.unit === this._unit);
e = e.filter((r) => r.id.startsWith("00"));
const s = e.sort(
(r, o) => (r.input_fee_ppk ?? 0) - (o.input_fee_ppk ?? 0)
)[0];
if (!s)
throw new Error("No active keyset found");
return s;
}
/**
* Get keysets from the mint with the unit of the wallet.
*
* @returns Keysets with wallet's unit.
*/
async getKeySets() {
const e = (await this.mint.getKeySets()).keysets.filter((s) => s.unit === this._unit);
return this._keysets = e, this._keysets;
}
/**
* Get all active keys from the mint and set the keyset with the lowest fees as the active wallet
* keyset.
*
* @returns Keyset.
*/
async getAllKeys() {
const t = await this.mint.getKeys();
return t.keysets.forEach((e) => {
if (!qt(e))
throw new Error(`Couldn't verify keyset ID ${e.id}`);
}), this._keys = new Map(t.keysets.map((e) => [e.id, e])), this.keysetId = this.getActiveKeyset(this._keysets).id, t.keysets;
}
/**
* Get public keys from the mint. If keys were already fetched, it will return those.
*
* If `keysetId` is set, it will fetch and return that specific keyset. Otherwise, we select an
* active keyset with the unit of the wallet.
*
* @param keysetId Optional keysetId to get keys for.
* @param forceRefresh? If set to true, it will force refresh the keyset from the mint.
* @returns Keyset.
*/
async getKeys(t, e) {
if ((!(this._keysets.length > 0) || e) && await this.getKeySets(), t || (t = this.getActiveKeyset(this._keysets).id), !this._keysets.find((s) => s.id === t) && (await this.getKeySets(), !this._keysets.find((s) => s.id === t)))
throw new Error(`could not initialize keys. No keyset with id '${t}' found`);
if (!this._keys.get(t)) {
const s = await this.mint.getKeys(t);
if (!qt(s.keysets[0]))
throw new Error(`Couldn't verify keyset ID ${s.keysets[0].id}`);
this._keys.set(t, s.keysets[0]);
}
return this.keysetId = t, this._keys.get(t);
}
/**
* Receive an encoded or raw Cashu token (only supports single tokens. It will only process the
* first token in the token array)
*
* @param {string | Token} token - Cashu token, either as string or decoded.
* @param {ReceiveOptions} [options] - Optional configuration for token processing.
* @returns New token with newly created proofs, token entries that had errors.
*/
async receive(t, e) {
const { requireDleq: s, keysetId: r, outputAmounts: o, counter: a, pubkey: i, privkey: c, outputData: h, p2pk: u } = e || {};
typeof t == "string" && (t = Ee(t));
const l = await this.getKeys(r);
if (s && t.proofs.some((P) => !jt(P, l)))
throw new Error("Token contains proofs with invalid DLEQ");
const f = W(t.proofs) - this.getFeesForProofs(t.proofs);
let d;
h ? d = { send: h } : this._keepFactory && (d = { send: this._keepFactory });
const g = this.createSwapPayload(
f,
t.proofs,
l,
o,
a,
i,
c,
d,
u
), { signatures: S } = await this.mint.swap(g.payload), b = g.outputData.map((P, k) => P.toProof(S[k], l)), A = [];
return g.sortedIndices.forEach((P, k) => {
A[P] = b[k];
}), A;
}
/**
* Send proofs of a given amount, by providing at least the required amount of proofs.
*
* @param amount Amount to send.
* @param proofs Array of proofs (accumulated amount of proofs must be >= than amount)
* @param {SendOptions} [options] - Optional parameters for configuring the send operation.
* @returns {SendResponse}
*/
async send(t, e, s) {
const {
offline: r,
includeFees: o,
includeDleq: a,
keysetId: i,
outputAmounts: c,
pubkey: h,
privkey: u,
outputData: l
} = s || {};
if (a && (e = e.filter((S) => S.dleq != null)), W(e) < t)
throw new Error("Not enough funds available to send");
const { keep: f, send: d } = this.selectProofsToSend(
e,
t,
s?.includeFees
), g = o ? this.getFeesForProofs(d) : 0;
if (!r && (W(d) != t + g || // if the exact amount cannot be selected
c || h || u || i || l)) {
const S = await this.swap(t, e, s), { keep: b, send: A } = S, P = S.serialized;
return { keep: b, send: A, serialized: P };
}
if (W(d) < t + g)
throw new Error("Not enough funds available to send");
return { keep: f, send: d };
}
/**
* Selects proofs to send based on amount and fee inclusion.
*
* @remarks
* Uses an adapted Randomized Greedy with Local Improvement (RGLI) algorithm, which has a time
* complexity O(n log n) and space complexity O(n).
* @param proofs Array of Proof objects available to select from.
* @param amountToSend The target amount to send.
* @param includeFees Optional boolean to include fees; Default: false.
* @returns SendResponse containing proofs to keep and proofs to send.
* @see https://crypto.ethz.ch/publications/files/Przyda02.pdf
*/
selectProofsToSend(t, e, s = !1) {
const u = Ke();
let l = null, f = 1 / 0, d = 0, g = 0;
const S = (m, p) => m - (s ? Math.ceil(p / 1e3) : 0), b = (m) => {
const p = [...m];
for (let w