@ninsei-labs/sdk 0.41.0 → 0.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,318 @@
1
+ // DECLARATIONS FOR THE `./xmr` ENTRY: the Monero side for a consumer that is not the browser.
2
+ //
3
+ // src/public/xmr.mjs only NAMES what the engine already offers; these declarations NAME the same thing for a
4
+ // TypeScript consumer. Every signature is copied from the modules the barrel re-exports
5
+ // (`../../engine/monero/address.js`, `../../engine/atomic/halves.js`, `../../engine/atomic/dleq2.js`,
6
+ // `../../engine/monero/halvesSweep.js`, `../../engine/core/sellWindow.js`), and a symbol the barrel does not
7
+ // re-export is not declared here.
8
+ //
9
+ // THE PRIMITIVES ARE HANDED IN, NOT IMPORTED. These modules pull no dependencies of their own: the curve and hash
10
+ // libraries arrive as arguments, so the types below describe the MEMBERS THE ENGINE CALLS. A real
11
+ // `@noble/curves`/`@noble/hashes` object satisfies them.
12
+
13
+ /** The Monero networks this package can build an address for, and check one against. */
14
+ export type MoneroNetwork = "mainnet" | "stagenet" | "testnet";
15
+
16
+ /** The three shapes of a Monero address. */
17
+ export type MoneroAddressKind = "primary" | "integrated" | "subaddress";
18
+
19
+ /**
20
+ * Network and kind -> prefix byte, the reverse table an address is assembled from.
21
+ *
22
+ * `fakechain` (monerod's own name for regtest) is deliberately absent: the withdrawal page picks that network up
23
+ * from the node, and there is no prefix byte to build an address with.
24
+ */
25
+ export declare const PREFIX_BY_NETWORK: Readonly<Record<MoneroNetwork, Readonly<Record<MoneroAddressKind, number>>>>;
26
+
27
+ /** The prefix byte of one network and kind. An unknown network or kind is refused, not guessed. */
28
+ export declare function prefixFor(network: MoneroNetwork, kind?: MoneroAddressKind): number;
29
+
30
+ /**
31
+ * The quick SHAPE check: base58 alphabet, 95 (plain) or 106 (integrated) characters, and a first letter that
32
+ * belongs to a Monero prefix. It does NOT verify the checksum - any typo inside passes here. The real check is
33
+ * `checkAddress`; this one only screens out obvious garbage before the heavy decode.
34
+ */
35
+ export declare function formatCheck(addr: string | null | undefined): boolean;
36
+
37
+ /** The network an address's first letter names, or null when the shape is not a Monero address at all. */
38
+ export declare function networkFromShape(addr: string | null | undefined): MoneroNetwork | null;
39
+
40
+ /** Whether the address is shaped as a stagenet one. */
41
+ export declare function isStagenetShaped(addr: string | null | undefined): boolean;
42
+
43
+ /**
44
+ * Block-based Monero base58: a block is a NUMBER, full 8-byte blocks come first (11 characters each) and the
45
+ * partial remainder goes LAST, so this is not bitcoin-base58 and `bs58` does not fit.
46
+ */
47
+ export declare function decodeMoneroBase58(str: string): Uint8Array;
48
+
49
+ /** The inverse of `decodeMoneroBase58`: bytes to a Monero base58 string. */
50
+ export declare function encodeMoneroBase58(bytes: Uint8Array | readonly number[]): string;
51
+
52
+ /** The address payload without its checksum: prefix + public spend + public view [+ payment id]. */
53
+ export interface AddressPayloadRequest {
54
+ readonly network: MoneroNetwork;
55
+ readonly kind?: MoneroAddressKind;
56
+ /** The 32-byte public spend key, hex. */
57
+ readonly spendPub: string;
58
+ /** The 32-byte public view key, hex. */
59
+ readonly viewPub: string;
60
+ /** The 8-byte payment id, hex: required for `kind: "integrated"`. */
61
+ readonly paymentId?: string;
62
+ }
63
+
64
+ /** The address payload: prefix, both public keys, and the payment id when the address is integrated. */
65
+ export declare function addressPayload(request: AddressPayloadRequest): Uint8Array;
66
+
67
+ /** keccak256 over bytes. The address checksum is its first four bytes, as in Monero itself. */
68
+ export type Keccak256Bytes = (bytes: Uint8Array) => Uint8Array;
69
+
70
+ /** What assembling an address needs: keccak256, and a Buffer shim for implementations taking only their own. */
71
+ export interface AddressKeysDeps {
72
+ readonly keccak256: Keccak256Bytes;
73
+ readonly Buffer?: { readonly from: (bytes: Uint8Array) => Uint8Array };
74
+ }
75
+
76
+ /** The address string built from public keys, with the checksum appended. */
77
+ export declare function addressFromKeys(request: AddressPayloadRequest, deps: AddressKeysDeps): string;
78
+
79
+ /**
80
+ * The full check: format, block decode, keccak256 checksum and network.
81
+ *
82
+ * `verified` says whether the checksum could be verified at all: without the Monero bundle the answer is
83
+ * `ok: true, verified: false` with the reason named, never a false "checked". A refusal always names WHY.
84
+ */
85
+ export type AddressCheck =
86
+ | { readonly ok: false; readonly reason: string }
87
+ | { readonly ok: true; readonly verified: false; readonly network: MoneroNetwork | null; readonly reason: string }
88
+ | { readonly ok: true; readonly verified: true; readonly network: MoneroNetwork; readonly kind: MoneroAddressKind; readonly bytes: number };
89
+
90
+ /** The full check of one address. Refuses an address of another network when `expectNetwork` names one. */
91
+ export declare function checkAddress(addr: string, options?: { readonly expectNetwork?: MoneroNetwork }): Promise<AddressCheck>;
92
+
93
+ /** An address of the right network, built from the wallet's own keys. Needs the Monero bundle for keccak256. */
94
+ export declare function addressForNetwork(request: AddressPayloadRequest): Promise<string>;
95
+
96
+ /** A curve point, as the halves and the DLEQ modules call it. */
97
+ export interface CurvePoint {
98
+ readonly multiply: (scalar: bigint) => CurvePoint;
99
+ readonly add: (other: CurvePoint) => CurvePoint;
100
+ readonly toHex: () => string;
101
+ }
102
+
103
+ /** The curve members the halves module calls. */
104
+ export interface CurvePrimitive {
105
+ readonly Point: {
106
+ readonly BASE: CurvePoint;
107
+ /** The group order: the smaller of the two is the limit a half must fit into. */
108
+ readonly Fn: { readonly ORDER: bigint };
109
+ readonly fromHex: (hex: string) => CurvePoint;
110
+ };
111
+ }
112
+
113
+ /** The five primitives the halves module is handed. */
114
+ export interface HalvesPrimitives {
115
+ readonly ed25519: CurvePrimitive;
116
+ readonly secp256k1: CurvePrimitive;
117
+ readonly keccak256: Keccak256Bytes;
118
+ readonly randomBytes: (length: number) => Uint8Array;
119
+ }
120
+
121
+ /** The halves module: one half of a key per side, public on both curves. */
122
+ export interface Halves {
123
+ /** The smaller of the two group orders: a half must fit into both groups. */
124
+ readonly LIMIT: bigint;
125
+ readonly ED_ORDER: bigint;
126
+ readonly SECP_ORDER: bigint;
127
+ /** The domain strings of the spend half and the view half: quoting and payout take them from here. */
128
+ readonly HALF_DOMAIN: string;
129
+ readonly VIEW_HALF_DOMAIN: string;
130
+ /** A uniform scalar below the limit, drawn by rejection sampling. */
131
+ readonly newHalf: () => bigint;
132
+ /**
133
+ * The half derived from a secret and the order binding, so the same order always gives the same half and only
134
+ * the secret holder can compute it.
135
+ */
136
+ readonly halfFromSeed: (seedBytes: Uint8Array, binding: string | number | bigint, domain?: string) => bigint;
137
+ /** The public halves on both curves. */
138
+ readonly publicHalves: (half: bigint | string | number) => { readonly ed: string; readonly secp: string };
139
+ /** The sum of two halves, privately. */
140
+ readonly combineHalves: (a: bigint | string | number, b: bigint | string | number) => bigint;
141
+ /** The sum of two halves, publicly (point addition). */
142
+ readonly combinePublic: (edHexA: string, edHexB: string) => string;
143
+ }
144
+
145
+ /** Creates the halves module from the five primitives. Each one is required, and its absence is named. */
146
+ export declare function createHalves(primitives: HalvesPrimitives): Halves;
147
+
148
+ /** A curve point, as the DLEQ module calls it. */
149
+ export interface Dleq2Point {
150
+ readonly multiply: (scalar: bigint) => Dleq2Point;
151
+ readonly add: (other: Dleq2Point) => Dleq2Point;
152
+ readonly subtract: (other: Dleq2Point) => Dleq2Point;
153
+ readonly equals: (other: Dleq2Point) => boolean;
154
+ readonly toHex: () => string;
155
+ /** True when the point is in the prime-order subgroup (`[l]*P = 0`), not merely free of a small component. */
156
+ readonly isTorsionFree: () => boolean;
157
+ /** Builds the multiplication table of the given window size at the first multiplication. */
158
+ readonly precompute: (windowSize: number) => void;
159
+ }
160
+
161
+ /** The curve members the DLEQ module calls. */
162
+ export interface Dleq2CurvePrimitive {
163
+ readonly Point: {
164
+ readonly BASE: Dleq2Point;
165
+ readonly Fn: { readonly ORDER: bigint };
166
+ readonly fromHex: (hex: string) => Dleq2Point;
167
+ };
168
+ }
169
+
170
+ /** The primitives the DLEQ module is handed. SHA3-512 is mandatory: the challenge is its wide reduction. */
171
+ export interface Dleq2Primitives {
172
+ readonly ed25519: Dleq2CurvePrimitive;
173
+ readonly secp256k1: Dleq2CurvePrimitive;
174
+ readonly keccak256: Keccak256Bytes;
175
+ /** SHA3-512, NOT keccak256: the challenge is a 64-byte hash read least-significant byte first. */
176
+ readonly sha3_512: (bytes: Uint8Array) => Uint8Array;
177
+ readonly randomBytes: (length: number) => Uint8Array;
178
+ }
179
+
180
+ /** The OR proof of one bit on both curves. Scalars and points travel as hex, so the proof survives JSON. */
181
+ export interface Dleq2Signature {
182
+ readonly eA: string;
183
+ readonly eB: string;
184
+ readonly a0: string;
185
+ readonly a1: string;
186
+ readonly b0: string;
187
+ readonly b1: string;
188
+ }
189
+
190
+ /** The proof of one bit: the bit's commitments on both curves and the ring signature over them. */
191
+ export interface Dleq2ProofPart {
192
+ readonly cA: string;
193
+ readonly cB: string;
194
+ readonly sig: Dleq2Signature;
195
+ }
196
+
197
+ /** The proof: the point X on both curves, and one part per bit of the smaller group order. */
198
+ export interface Dleq2Proof {
199
+ readonly XA: string;
200
+ readonly XB: string;
201
+ readonly proofs: readonly Dleq2ProofPart[];
202
+ }
203
+
204
+ /** One bit prepared for proving: the bit, its commitments and its randomness. The half is NOT here. */
205
+ export interface Dleq2PreparedBit {
206
+ readonly i: number;
207
+ readonly bit: 0 | 1;
208
+ readonly cA: string;
209
+ readonly cB: string;
210
+ readonly rA: string;
211
+ readonly rB: string;
212
+ }
213
+
214
+ /** Step 1's output: the points and one prepared bit per bit. Depends on the running sum, so it is one piece. */
215
+ export interface Dleq2Prepared {
216
+ readonly XA: string;
217
+ readonly XB: string;
218
+ readonly context: string;
219
+ readonly bits: readonly Dleq2PreparedBit[];
220
+ }
221
+
222
+ /** The verifier's answer. A refusal NAMES the bit and the reason; it never throws. */
223
+ export interface Dleq2Verdict {
224
+ readonly ok: boolean;
225
+ readonly reason?: string;
226
+ }
227
+
228
+ /** One of the two curves the module holds, with the alternate base point its ring signature uses. */
229
+ export interface Dleq2Curve {
230
+ readonly name: string;
231
+ readonly P: Dleq2CurvePrimitive["Point"];
232
+ readonly order: bigint;
233
+ readonly G: Dleq2Point;
234
+ readonly Galt: Dleq2Point;
235
+ }
236
+
237
+ /** The DLEQ module: proving is assembled from three steps, and the steps can be run apart. */
238
+ export interface Dleq2 {
239
+ /** The bit length of the smaller order: the number of proof parts an honest proof carries. */
240
+ readonly BITS: number;
241
+ readonly curves: { readonly A: Dleq2Curve; readonly B: Dleq2Curve };
242
+ /** The whole proof. `onBit` reports progress every 16 bits and at the last. */
243
+ readonly prove: (x: bigint | string | number, context: string, onBit?: (done: number, total: number) => void) => Dleq2Proof;
244
+ /** Verifies a proof of untrusted shape: form is checked before counting, and every refusal is named. */
245
+ readonly verify: (proof: unknown, context: string) => Dleq2Verdict;
246
+ /** Step 1: the commitments. Sequential by construction. */
247
+ readonly prepareProof: (x: bigint | string | number, context: string) => Dleq2Prepared;
248
+ /** Step 2: one bit, on the prepared data alone - it needs neither the half nor the other bits. */
249
+ readonly proveBit: (part: Dleq2PreparedBit, context: string) => Dleq2ProofPart;
250
+ /** Step 3: the parts are put in bit order, and a missing bit is refused. */
251
+ readonly assembleProof: (prepared: Dleq2Prepared, parts: readonly Dleq2ProofPart[]) => Dleq2Proof;
252
+ /** The strict subgroup check. secp256k1 has cofactor 1 and passes without a check. */
253
+ readonly torsionFree: (curve: Dleq2Curve, point: Dleq2Point) => boolean;
254
+ }
255
+
256
+ /** Creates the DLEQ module from the five primitives. Without SHA3-512 it refuses immediately. */
257
+ export declare function createDleq2(primitives: Dleq2Primitives): Dleq2;
258
+
259
+ /**
260
+ * What sweeping from a revealed half is handed: the half maths, this side's two halves as the file carries them,
261
+ * the counterparty's public spend key and view half, and the half the counterparty revealed.
262
+ */
263
+ export interface SpendKeysFromRevealedHalfRequest {
264
+ readonly halves: Halves;
265
+ /** This side's spend half, from the file: it arrives as a string and is converted before any arithmetic. */
266
+ readonly ownSpendHalf: string | number | bigint;
267
+ /** This side's view half, from the file. */
268
+ readonly ownViewHalf: string | number | bigint;
269
+ /** The counterparty's public spend key, hex: the point the order carried. */
270
+ readonly otherSpendPoint: string;
271
+ /** The counterparty's view half, hex. */
272
+ readonly otherViewHalf: string | number | bigint;
273
+ /** The revealed half, hex. */
274
+ readonly revealedHalf: string | number | bigint;
275
+ }
276
+
277
+ /** The two private keys a sweep writes with, in Monero notation: 64 hex, least significant byte first. */
278
+ export interface SpendKeys {
279
+ readonly privateSpendKey: string;
280
+ readonly privateViewKey: string;
281
+ }
282
+
283
+ /**
284
+ * The spend and view keys a revealed half yields, or a THROW when the half does not belong to this order.
285
+ *
286
+ * The commitment only proves that the HASH of the revealed half matched the order, and ed25519 does not exist in
287
+ * the EVM, so the check that `(s_a + s_b)*G` equals the order's public spend key happens HERE, before any
288
+ * synchronisation: a half from another order, or a substituted one, yields no keys.
289
+ */
290
+ export declare function spendKeysFromRevealedHalf(request: SpendKeysFromRevealedHalfRequest): SpendKeys;
291
+
292
+ /**
293
+ * A Monero-notation key read as a number: 64 hex, least significant byte first. A key that is not in that notation
294
+ * is refused rather than reinterpreted - `BigInt("0x" + key)` silently reverses the byte order and gives another
295
+ * scalar.
296
+ */
297
+ export declare function scalarFromMoneroHex(hex: string): bigint;
298
+
299
+ /**
300
+ * The XMR sell window: the SOFT on-screen promise (how long the provider holds the price) and the HARD contract
301
+ * window, which goes into the order binding and can never be shorter than the contract's own.
302
+ */
303
+ export declare const SELL_WINDOW: {
304
+ /** The provider's on-screen promise, in seconds. */
305
+ readonly softSec: number;
306
+ /** The contract readiness window, in seconds: ten Monero confirmations, and the maximum that goes on chain. */
307
+ readonly hardSec: number;
308
+ };
309
+
310
+ /**
311
+ * How long the sell window lasts, given the named pool state: while the FULL expected amount is not visible in
312
+ * the pool the promise is extended to the contract maximum, and the contract window is a CEILING on it.
313
+ */
314
+ export declare function sellWindowSec(request?: {
315
+ readonly poolSeen?: boolean;
316
+ readonly soft?: number;
317
+ readonly hard?: number;
318
+ }): number;
@@ -0,0 +1,29 @@
1
+ // THE XMR SIDE, FOR A CONSUMER THAT IS NOT THE BROWSER.
2
+ //
3
+ // A node that quotes, signs and later reveals a half key needs the same arithmetic the page uses: an address
4
+ // built from two public points, the half keys, the DLEQ proof, the maker's sell window. All of it lives in the
5
+ // engine next to this file; this entry only NAMES it, so a node can depend on a released version of this
6
+ // package instead of reaching into another repository's source. Nothing is re-implemented here, and nothing
7
+ // here is browser-specific - these modules touch no window, document or storage.
8
+
9
+ export {
10
+ PREFIX_BY_NETWORK,
11
+ addressForNetwork,
12
+ addressFromKeys,
13
+ addressPayload,
14
+ checkAddress,
15
+ decodeMoneroBase58,
16
+ encodeMoneroBase58,
17
+ formatCheck,
18
+ isStagenetShaped,
19
+ networkFromShape,
20
+ prefixFor,
21
+ } from "../../engine/monero/address.js";
22
+
23
+ export { createHalves } from "../../engine/atomic/halves.js";
24
+ export { createDleq2 } from "../../engine/atomic/dleq2.js";
25
+
26
+ // THE SWEEP SIDE: the spend keys a revealed half yields, and the scalar a Monero hex string carries.
27
+ export { scalarFromMoneroHex, spendKeysFromRevealedHalf } from "../../engine/monero/halvesSweep.js";
28
+
29
+ export { SELL_WINDOW, sellWindowSec } from "../../engine/core/sellWindow.js";
@@ -0,0 +1,9 @@
1
+ // THE PACKAGE VERSION AS A VALUE, BAKED INTO THE CODE.
2
+ //
3
+ // A consumer that has to agree with a peer on a minor line - a node refusing a client built from a different
4
+ // minor - needs the number at runtime, and a page cannot fetch it: package.json is not part of a bundle and need
5
+ // not be served beside it. So the number is a constant, and this file is GENERATED from package.json by
6
+ // tools/sync-version.mjs. Do not edit it by hand - set the version in package.json and run `npm run sync:version`.
7
+ // The `version` check in tools/check.mjs refuses a mismatch, and refuses a runtime read of package.json here.
8
+
9
+ export const SDK_VERSION = "0.42.1";