@ninsei-labs/sdk 0.43.1 → 0.44.0
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.
- package/CHANGELOG.md +61 -0
- package/README.md +1 -1
- package/engine/core/deadlineScale.js +59 -0
- package/engine/core/xmrAmount.js +51 -0
- package/engine/monero/arrivalRead.js +95 -0
- package/engine/monero/daemonAddress.js +82 -0
- package/engine/monero/nodeNetwork.js +15 -0
- package/index.d.ts +149 -0
- package/package.json +22 -3
- package/src/adapters/monero.mjs +12 -0
- package/src/codes.mjs +56 -0
- package/src/destination.mjs +46 -0
- package/src/feeEstimate.mjs +80 -0
- package/src/index.mjs +3 -3
- package/src/probes.mjs +73 -0
- package/src/provider/envFile.mjs +106 -0
- package/src/provider/identity.mjs +43 -0
- package/src/provider/sign.mjs +164 -0
- package/src/public/core.d.ts +37 -0
- package/src/public/core.mjs +16 -0
- package/src/public/order.d.ts +12 -0
- package/src/public/order.mjs +5 -0
- package/src/public/provider.d.ts +93 -0
- package/src/public/provider.mjs +39 -0
- package/src/public/xmr.d.ts +155 -0
- package/src/public/xmr.mjs +34 -0
- package/src/recovery.mjs +72 -1
- package/src/sweep-restore.mjs +130 -0
- package/src/sweep.mjs +90 -1
- package/src/version.mjs +1 -1
package/src/sweep.mjs
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
import { SdkError } from "./errors.mjs";
|
|
8
8
|
import { primitives } from "./primitives.mjs";
|
|
9
9
|
import * as engine from "./engine.mjs";
|
|
10
|
+
import { restoreWallet } from "./sweep-restore.mjs";
|
|
11
|
+
import { moneroWalletAdapter } from "./adapters/monero-wallet.mjs";
|
|
12
|
+
import { checkDestination } from "./destination.mjs";
|
|
10
13
|
|
|
11
14
|
// The same boundary as when the file was issued: a passphrase shorter than twelve characters will not open the file.
|
|
12
15
|
const MIN_PASSPHRASE = 12;
|
|
@@ -30,6 +33,23 @@ const walletStep = async (surface, step) => {
|
|
|
30
33
|
}
|
|
31
34
|
};
|
|
32
35
|
|
|
36
|
+
// THE FEE A WALLET REPORTS FOR A SIGNED TRANSACTION. Optional on purpose: the adapter contract does not require
|
|
37
|
+
// it, and an adapter without it is not broken - the answer is simply unknown. The library's own shape is tried
|
|
38
|
+
// (`getFee()` on the transaction or on the signed wrapper), because that is what monero-ts returns; a value that is
|
|
39
|
+
// not a non-negative integer string is refused as unknown rather than trusted.
|
|
40
|
+
const feeOf = async (wallet, signed) => {
|
|
41
|
+
if (!wallet || typeof wallet.fee !== "function") return null;
|
|
42
|
+
let value;
|
|
43
|
+
try {
|
|
44
|
+
value = await wallet.fee(signed);
|
|
45
|
+
} catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
if (value === null || value === undefined) return null;
|
|
49
|
+
const text = String(value);
|
|
50
|
+
return /^\d+$/.test(text) ? text : null;
|
|
51
|
+
};
|
|
52
|
+
|
|
33
53
|
export function createSweep({ http, config }) {
|
|
34
54
|
// THE NAME IS NEEDED BY THE OBJECT ITSELF: `run` calls `api.plan(...)` - the step order is held by the plan and
|
|
35
55
|
// the withdrawal living in one object, not in two copies. During a manual merge of branches the object was left
|
|
@@ -40,7 +60,52 @@ export function createSweep({ http, config }) {
|
|
|
40
60
|
const sessions = new Map();
|
|
41
61
|
let sessionSeq = 0;
|
|
42
62
|
|
|
63
|
+
// THE NETWORK NAME MONERO-TS TAKES. The adapter speaks a string ("stagenet") because that is what a node says and
|
|
64
|
+
// what a human reads; the library wants its own enum. That translation used to be a callback on the sweep page -
|
|
65
|
+
// the last thing a caller had to know about the library to open a wallet.
|
|
66
|
+
const NETWORK_ENUM = { mainnet: "MAINNET", stagenet: "STAGENET", testnet: "TESTNET" };
|
|
67
|
+
|
|
43
68
|
const api = {
|
|
69
|
+
// BUILD THE WITHDRAWAL WALLET from the library namespace the caller has. The page used to pass a `createWallet`
|
|
70
|
+
// callback that mapped the enum itself; that mapping is here now, so a caller passes `moneroTs` and the keys.
|
|
71
|
+
wallet(request = {}) {
|
|
72
|
+
const moneroTs = request.moneroTs;
|
|
73
|
+
if (!moneroTs || typeof moneroTs !== "object") throw new SdkError("bad-input", { field: "moneroTs" });
|
|
74
|
+
if (typeof moneroTs.createWalletFull !== "function") throw new SdkError("bad-input", { field: "moneroTs.createWalletFull" });
|
|
75
|
+
const network = String(request.network || "").trim().toLowerCase();
|
|
76
|
+
const enumName = NETWORK_ENUM[network] || null;
|
|
77
|
+
const enumValue = enumName && moneroTs.MoneroNetworkType ? moneroTs.MoneroNetworkType[enumName] : undefined;
|
|
78
|
+
if (enumValue === undefined || enumValue === null) {
|
|
79
|
+
throw new SdkError("bad-input", { field: "network", known: Object.keys(NETWORK_ENUM), got: request.network });
|
|
80
|
+
}
|
|
81
|
+
if (typeof request.serverUri !== "string" || !request.serverUri) throw new SdkError("bad-input", { field: "serverUri" });
|
|
82
|
+
return moneroWalletAdapter({
|
|
83
|
+
createWallet: (config) => moneroTs.createWalletFull({ ...config, networkType: enumValue }),
|
|
84
|
+
network,
|
|
85
|
+
serverUri: request.serverUri,
|
|
86
|
+
password: request.password,
|
|
87
|
+
keys: request.keys || {},
|
|
88
|
+
restoreHeight: request.restoreHeight,
|
|
89
|
+
expectedAddress: request.expectedAddress || null,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
// WHERE THE WITHDRAWAL GOES. The file names the recipient the swap was made for; this compares the address the
|
|
94
|
+
// person typed with that promise and with the file's network. `matches` is null when the file named nobody -
|
|
95
|
+
// that is not a failure, it is an absence the caller can phrase.
|
|
96
|
+
async destination(request = {}) {
|
|
97
|
+
const contents = contentsOf(request.file);
|
|
98
|
+
if (!contents) throw new SdkError("bad-input", { field: "file" });
|
|
99
|
+
if (!request.passphrase) throw new SdkError("bad-input", { field: "passphrase" });
|
|
100
|
+
if (typeof request.dest !== "string" || !request.dest.trim()) throw new SdkError("bad-input", { field: "dest" });
|
|
101
|
+
let payload = null;
|
|
102
|
+
try { payload = await engine.recoveryFile.decryptFile(JSON.parse(contents), request.passphrase); }
|
|
103
|
+
catch { throw new SdkError("recovery-failed", { step: "decrypt" }); }
|
|
104
|
+
try { engine.recoveryFile.validatePayload(payload); }
|
|
105
|
+
catch { throw new SdkError("recovery-failed", { step: "validate" }); }
|
|
106
|
+
return checkDestination(payload, request.dest);
|
|
107
|
+
},
|
|
108
|
+
|
|
44
109
|
async plan(request) {
|
|
45
110
|
if (!request || typeof request !== "object") throw new SdkError("bad-input", { field: "request" });
|
|
46
111
|
const contents = contentsOf(request.file);
|
|
@@ -145,6 +210,13 @@ export function createSweep({ http, config }) {
|
|
|
145
210
|
}
|
|
146
211
|
},
|
|
147
212
|
|
|
213
|
+
// THE PASS BEFORE SIGNING: set the height, rescan, sync, read the balance, and - when coins exist but none is
|
|
214
|
+
// spendable - re-check the spent status once. The wallet is the same withdrawal adapter `sign` takes; the page
|
|
215
|
+
// no longer decides when a second pass is needed.
|
|
216
|
+
async restore(request) {
|
|
217
|
+
return await restoreWallet(request || {});
|
|
218
|
+
},
|
|
219
|
+
|
|
148
220
|
// THE WITHDRAWAL ITSELF. The wallet arrives as an adapter (the WASM and the worker live with whoever builds the
|
|
149
221
|
// page), while the core holds the order: open -> sync -> look at the free remainder -> SIGN (relay: false) ->
|
|
150
222
|
// send -> close.
|
|
@@ -202,9 +274,26 @@ export function createSweep({ http, config }) {
|
|
|
202
274
|
// zero, and the refusal would look like success.
|
|
203
275
|
if (unlocked <= 0n) throw new SdkError("insufficient-funds", { unlocked: unlocked.toString() });
|
|
204
276
|
const signed = await walletStep("sweepUnlocked", () => wallet.sweepUnlocked({ address: request.to, relay: false }));
|
|
277
|
+
// WHAT THE TRANSFER COSTS, WHEN THE WALLET CAN SAY. A withdrawal moves the whole free remainder, so the fee
|
|
278
|
+
// is not extra money on top - it is taken out of it, and the recipient receives the remainder MINUS the fee.
|
|
279
|
+
// An adapter that cannot report a fee answers null; publishing 0 there would be a claim the package cannot
|
|
280
|
+
// support, and the interface would show a free transfer.
|
|
281
|
+
const fee = await feeOf(wallet, signed);
|
|
282
|
+
const feeValue = fee === null ? null : BigInt(fee);
|
|
205
283
|
const token = "sweep-" + (++sessionSeq);
|
|
206
284
|
sessions.set(token, { wallet, plan, signed });
|
|
207
|
-
return {
|
|
285
|
+
return {
|
|
286
|
+
token,
|
|
287
|
+
amount: unlocked.toString(),
|
|
288
|
+
// The fee in atomic units, or null when the wallet could not report it.
|
|
289
|
+
fee,
|
|
290
|
+
// What the recipient receives: the remainder minus the fee. Null whenever the fee is unknown, and null
|
|
291
|
+
// rather than a negative number when a fee claims more than the remainder (that is a broken report, not
|
|
292
|
+
// a transfer).
|
|
293
|
+
receive: feeValue === null || feeValue >= unlocked ? null : (unlocked - feeValue).toString(),
|
|
294
|
+
to: request.to,
|
|
295
|
+
swept: plan.address,
|
|
296
|
+
};
|
|
208
297
|
} catch (error) {
|
|
209
298
|
// There is no signature - we do not leave an open wallet. Closing must not hide the refusal's reason.
|
|
210
299
|
try { await wallet.close(); } catch { /* the reason matters more than closing */ }
|
package/src/version.mjs
CHANGED
|
@@ -6,4 +6,4 @@
|
|
|
6
6
|
// tools/sync-version.mjs. Do not edit it by hand - set the version in package.json and run `npm run sync:version`.
|
|
7
7
|
// The `version` check in tools/check.mjs refuses a mismatch, and refuses a runtime read of package.json here.
|
|
8
8
|
|
|
9
|
-
export const SDK_VERSION = "0.
|
|
9
|
+
export const SDK_VERSION = "0.44.0";
|