@ninsei-labs/sdk 0.39.2 → 0.40.0-develop.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.
- package/CHANGELOG.md +63 -0
- package/README.md +4 -1
- package/engine/atomic/dleq2.js +86 -111
- package/engine/atomic/halfenc.js +58 -72
- package/engine/atomic/halves.js +31 -53
- package/engine/atomic/order-client.js +15 -16
- package/engine/atomic/order.js +44 -62
- package/engine/atomic/orderContext.js +26 -50
- package/engine/atomic/orderHalfCommit.js +17 -18
- package/engine/core/chainSource.js +54 -69
- package/engine/core/clock.js +11 -12
- package/engine/core/config.js +554 -449
- package/engine/core/finality.js +34 -53
- package/engine/core/format.js +35 -36
- package/engine/core/preSignGuards.js +109 -117
- package/engine/core/rfqSource.js +57 -75
- package/engine/core/sellWindow.js +23 -24
- package/engine/core/store.js +20 -21
- package/engine/core/swap.js +242 -243
- package/engine/evm/amounts.js +50 -51
- package/engine/evm/asyncExec.js +577 -0
- package/engine/evm/asyncLegs.js +308 -0
- package/engine/evm/asyncRoute.js +179 -0
- package/engine/evm/auth.js +41 -48
- package/engine/evm/claimGas.js +28 -47
- package/engine/evm/depositor.js +39 -43
- package/engine/evm/dex.js +266 -267
- package/engine/evm/escrow.js +113 -129
- package/engine/evm/factory.js +71 -77
- package/engine/evm/fees.js +41 -42
- package/engine/evm/funding.js +160 -162
- package/engine/evm/gasReserve.js +67 -36
- package/engine/evm/index.js +46 -53
- package/engine/evm/injected.js +34 -42
- package/engine/evm/permit.js +129 -56
- package/engine/evm/prices.js +30 -45
- package/engine/evm/session.js +188 -189
- package/engine/mock/chain.js +17 -18
- package/engine/monero/address.js +33 -48
- package/engine/monero/halvesSweep.js +25 -43
- package/engine/monero/node.js +14 -15
- package/engine/monero/scan.js +82 -95
- package/engine/monero/swapKeys.js +30 -33
- package/engine/monero/unlock.js +17 -20
- package/engine/monero/wallet.js +32 -42
- package/engine/recovery/recoveryFile.js +154 -161
- package/engine/recovery/restore.js +50 -61
- package/engine/sdk/bridge.js +244 -234
- package/engine/ui/dom.js +4 -5
- package/engine/ui/walletModal.js +20 -31
- package/index.d.ts +26 -0
- package/package.json +1 -1
- package/src/adapters/evm-wallet.mjs +16 -0
- package/src/adapters/monero-wallet.mjs +1 -1
- package/src/adapters/monero.mjs +1 -1
- package/src/adapters/node-test-evm-wallet.mjs +5 -5
- package/src/codes.mjs +5 -0
- package/src/config.mjs +1 -1
- package/src/engine.mjs +4 -0
- package/src/index.mjs +16 -2
- package/src/legs/cow-spec.mjs +254 -0
- package/src/legs/cow.mjs +315 -0
- package/src/legs/evm.mjs +86 -2
- package/src/legs/kyber-spec.mjs +265 -0
- package/src/legs/kyber.mjs +425 -0
- package/src/legs/shape.mjs +35 -0
- package/src/legs/uniswaprelay-spec.mjs +541 -0
- package/src/legs/uniswaprelay.mjs +422 -0
- package/src/legs/uniswapx-spec.mjs +329 -0
- package/src/legs/uniswapx.mjs +362 -0
- package/src/quotes.mjs +23 -5
- package/src/swaps.mjs +18 -1
|
@@ -1,83 +1,76 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/recovery/restore.js for the SDK package.
|
|
2
|
-
// Edit the source under www/js
|
|
3
|
-
// tools/check-sdk-engine.mjs reddens on any drift, so a stale copy cannot ship silently.
|
|
2
|
+
// Edit the source under www/js and regenerate the mirror; a drift guard reddens on any difference.
|
|
4
3
|
// Everything below this header is identical to the source.
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// «восстановилось» означало бы показать пользователю чужой кошелёк и потерять его средства;
|
|
14
|
-
// 2) на сервер (watch-only кошелёк) уходит ТОЛЬКО view key с адресом и высотой. Ключ траты,
|
|
15
|
-
// мнемоника и seed остаются в браузере - это граница продукта, поэтому она вынесена в
|
|
16
|
-
// отдельную функцию и проверяется отдельным тестом (tools/check-recovery.mjs).
|
|
5
|
+
// The reverse recovery-file path: file -> wallet that can claim the XMR.
|
|
6
|
+
// The forward path (file generation) is in recoveryFile.js. Here is why the file exists: recovery when the tab is
|
|
7
|
+
// lost. Two rules are pinned by CODE, not by a comment:
|
|
8
|
+
// 1) the wallet is restored from the mnemonic, and the restored address MUST match the address in the file. No
|
|
9
|
+
// match - the file is corrupted or substituted, and we must not continue;
|
|
10
|
+
// 2) only the view key, with the address and height, goes to the server (watch-only). The spend key, mnemonic
|
|
11
|
+
// and seed stay in the browser, a product boundary checked by a separate test.
|
|
17
12
|
|
|
18
13
|
import { createSwapWallet } from "../monero/wallet.js";
|
|
19
14
|
import { decryptFile, validatePayload } from "./recoveryFile.js";
|
|
20
15
|
import { swapAddressFromHalves, watchViewSeedHex } from "../monero/swapKeys.js";
|
|
21
16
|
|
|
22
|
-
//
|
|
17
|
+
// The restored wallet plus everything needed for the scan: height and format warnings.
|
|
23
18
|
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// просмотра - сумма двух половин просмотра), а трата становится возможной только после раскрытия.
|
|
19
|
+
// TWO DIFFERENT SCHEMES, BOTH LIVE. A v2 file carries a MNEMONIC (a full wallet is built from it). A v3 file
|
|
20
|
+
// carries HALVES, and has NO full spend key: each side has only its half, the second is revealed on chain at ETH
|
|
21
|
+
// claim. So a v3 file builds a VIEWING wallet; spending becomes possible only after the reveal.
|
|
28
22
|
//
|
|
29
|
-
// deps
|
|
30
|
-
//
|
|
23
|
+
// deps is needed for testability: in the browser the libraries come from the vendored bundle (loaded dynamically,
|
|
24
|
+
// as a static bundle import would break Node runs), in tests they are passed in.
|
|
31
25
|
export async function restoreWalletFromPayload(payload, deps = {}) {
|
|
32
26
|
const { warnings } = validatePayload(payload);
|
|
33
27
|
const m = payload.moneroWallet;
|
|
34
28
|
const network = m.network || payload.moneroNetwork;
|
|
35
29
|
if (payload.version >= 3) return await restoreFromHalves(payload, warnings, deps);
|
|
36
30
|
|
|
37
|
-
//
|
|
38
|
-
//
|
|
31
|
+
// The mnemonic is passed explicitly: monero-js checks its checksum and refuses a broken phrase
|
|
32
|
+
// (in wallet.js this is now an error, not a silent new-wallet generation).
|
|
39
33
|
const wallet = await createSwapWallet({ seed: m.mnemonic, network });
|
|
40
34
|
|
|
41
35
|
if (wallet.address !== m.address) {
|
|
42
|
-
throw new Error("
|
|
36
|
+
throw new Error("the address from the file does not match the one derived from the mnemonic: the file is corrupted or altered");
|
|
43
37
|
}
|
|
44
|
-
//
|
|
45
|
-
//
|
|
38
|
+
// The public keys in the file are a second check on the same thing: the address is built from them, so a
|
|
39
|
+
// mismatch means the file was edited by hand.
|
|
46
40
|
for (const [field, value] of [
|
|
47
41
|
["publicSpendKey", wallet.publicSpendKey],
|
|
48
42
|
["publicViewKey", wallet.publicViewKey],
|
|
49
43
|
]) {
|
|
50
44
|
if (m[field] && m[field] !== value) {
|
|
51
|
-
throw new Error(
|
|
45
|
+
throw new Error(`key ${field} from the file does not match the one derived from the mnemonic: the file was altered`);
|
|
52
46
|
}
|
|
53
47
|
}
|
|
54
48
|
|
|
55
49
|
return {
|
|
56
50
|
wallet,
|
|
57
51
|
payload,
|
|
58
|
-
//
|
|
52
|
+
// This is why the height is stored: without it the wallet scans Monero history from scratch (tens of hours).
|
|
59
53
|
restoreHeight: Number.isFinite(m.restoreHeight) && m.restoreHeight > 0 ? m.restoreHeight : 0,
|
|
60
54
|
warnings,
|
|
61
55
|
};
|
|
62
56
|
}
|
|
63
57
|
|
|
64
|
-
//
|
|
58
|
+
// RESTORATION FROM HALVES (v3 file). What is here and what is not:
|
|
59
|
+
// present: INTEGRITY CHECK. The address is built from our halves and THEIR points and must match the file
|
|
60
|
+
// address - the same role as the address/mnemonic comparison in v2. No match - the file is corrupted or
|
|
61
|
+
// substituted, and we must not continue;
|
|
62
|
+
// present: the VIEW KEY - the sum of our view half and the counterparty half. It lets the wallet SEE arrivals;
|
|
63
|
+
// NOT present: the spend key. It cannot be assembled: the second spend half is not in the file. It comes from
|
|
64
|
+
// the chain when the counterparty claims the ETH - then spending becomes possible (sweepSpendSecret verifies
|
|
65
|
+
// it against the order public key).
|
|
65
66
|
//
|
|
66
|
-
// есть: ПРОВЕРКА ЦЕЛОСТНОСТИ. Адрес собирается из своих половин и ЧУЖИХ ТОЧЕК и обязан совпасть с адресом
|
|
67
|
-
// в файле - ровно та же роль, что у сравнения адреса с мнемоникой в ветке v2. Не совпал - файл повреждён или
|
|
68
|
-
// подменён, и продолжать нельзя;
|
|
69
|
-
// есть: КЛЮЧ ПРОСМОТРА - сумма своей половины просмотра и половины контрагента (она в файле и не секрет).
|
|
70
|
-
// Им кошелёк ВИДИТ поступления на общий адрес;
|
|
71
|
-
// НЕТ: ключ траты. Его нельзя собрать: второй половины траты в файле нет, и это не упущение, а свойство
|
|
72
|
-
// схемы. Она придёт из цепи, когда контрагент заберёт ETH, - вот тогда трата и станет возможной
|
|
73
|
-
// (sweepSpendSecret сверяет её с публичным ключом ордера).
|
|
74
67
|
export async function restoreFromHalves(payload, warnings = [], deps = {}) {
|
|
75
68
|
const m = payload.moneroWallet;
|
|
76
69
|
const network = m.network || payload.moneroNetwork;
|
|
77
70
|
const libs = deps.halves && deps.addressFromKeys
|
|
78
71
|
? { halves: deps.halves, addressFromKeys: deps.addressFromKeys, keccak256: deps.keccak256 }
|
|
79
72
|
: await (async () => {
|
|
80
|
-
const vendor = await import("../../assets/vendors/atomic/atomic-browser.js?v=
|
|
73
|
+
const vendor = await import("../../assets/vendors/atomic/atomic-browser.js?v=2c038c56");
|
|
81
74
|
const { createHalves } = await import("../atomic/halves.js");
|
|
82
75
|
const { addressFromKeys } = await import("../monero/address.js");
|
|
83
76
|
const halves = createHalves({
|
|
@@ -86,7 +79,7 @@ export async function restoreFromHalves(payload, warnings = [], deps = {}) {
|
|
|
86
79
|
});
|
|
87
80
|
return { halves, addressFromKeys, keccak256: vendor.keccak256 };
|
|
88
81
|
})();
|
|
89
|
-
if (!libs.keccak256) throw new Error("
|
|
82
|
+
if (!libs.keccak256) throw new Error("no keccak256: the address cannot be checked without it");
|
|
90
83
|
|
|
91
84
|
const built = swapAddressFromHalves({
|
|
92
85
|
halves: libs.halves, addressFromKeys: libs.addressFromKeys, deps: { keccak256: libs.keccak256 },
|
|
@@ -94,7 +87,7 @@ export async function restoreFromHalves(payload, warnings = [], deps = {}) {
|
|
|
94
87
|
otherSpendPoint: m.otherSpendPoint, otherViewPoint: m.otherViewPoint, network,
|
|
95
88
|
});
|
|
96
89
|
if (built.address !== m.address) {
|
|
97
|
-
throw new Error("
|
|
90
|
+
throw new Error("the address from the file does not match the one built from the halves and counterparty points: the file is corrupted or altered");
|
|
98
91
|
}
|
|
99
92
|
const viewSeedHex = watchViewSeedHex({
|
|
100
93
|
halves: libs.halves, ownViewHalf: m.viewHalf, otherViewHalf: m.otherViewHalf,
|
|
@@ -111,7 +104,7 @@ export async function restoreFromHalves(payload, warnings = [], deps = {}) {
|
|
|
111
104
|
otherViewPoint: m.otherViewPoint,
|
|
112
105
|
otherViewHalf: m.otherViewHalf,
|
|
113
106
|
source: "halves",
|
|
114
|
-
//
|
|
107
|
+
// EXPLICIT: this wallet CANNOT yet spend. The spend key appears after a half is revealed on chain.
|
|
115
108
|
spendable: false,
|
|
116
109
|
};
|
|
117
110
|
return {
|
|
@@ -120,49 +113,45 @@ export async function restoreFromHalves(payload, warnings = [], deps = {}) {
|
|
|
120
113
|
restoreHeight: Number.isFinite(m.restoreHeight) && m.restoreHeight > 0 ? m.restoreHeight : 0,
|
|
121
114
|
warnings: [
|
|
122
115
|
...warnings,
|
|
123
|
-
"
|
|
124
|
-
"
|
|
116
|
+
"this file describes the HALVES scheme: the view key sees arrivals, but spending needs the counterparty " +
|
|
117
|
+
"half, which is revealed on chain at the ETH claim",
|
|
125
118
|
],
|
|
126
119
|
};
|
|
127
120
|
}
|
|
128
121
|
|
|
129
|
-
//
|
|
122
|
+
// The full path: decrypt the file with a password and restore the wallet.
|
|
130
123
|
export async function restoreFromFile(fileObject, passphrase, deps = {}) {
|
|
131
124
|
return restoreWalletFromPayload(await decryptFile(fileObject, passphrase), deps);
|
|
132
125
|
}
|
|
133
126
|
|
|
134
|
-
//
|
|
135
|
-
//
|
|
136
|
-
//
|
|
137
|
-
// должно здесь, а не утекать на сервер. Проверяем двумя способами - по именам полей и по
|
|
138
|
-
// значениям (переименовать поле легко, а значение секрета останется тем же).
|
|
127
|
+
// A safeguard pulled out so it can be tested: today share is built from a field allowlist, so a secret cannot
|
|
128
|
+
// get in. But if someone later adds a just-in-case field, it must fail here instead of leaking to the server.
|
|
129
|
+
// We check by field names and by values (renaming a field is easy, the secret value is not).
|
|
139
130
|
export function assertNoSecretsInShare(share, wallet) {
|
|
140
|
-
// spendHalf
|
|
141
|
-
//
|
|
142
|
-
// чтобы видеть входящие, и недостаточно, чтобы их потратить.
|
|
131
|
+
// spendHalf and viewHalf were added with the halves scheme: secrets as strong as the spend key, they must not
|
|
132
|
+
// reach the server - it sees only the SUM of view halves, enough to see incoming, not to spend.
|
|
143
133
|
for (const name of ["mnemonic", "seed", "seedHex", "privateSpendKey", "spendKey", "subaddressKeys", "spendHalf", "viewHalf"]) {
|
|
144
|
-
if (Object.hasOwn(share, name)) throw new Error("watch-only share
|
|
134
|
+
if (Object.hasOwn(share, name)) throw new Error("the watch-only share must not contain " + name);
|
|
145
135
|
}
|
|
146
136
|
const json = JSON.stringify(share);
|
|
147
137
|
for (const [label, secret] of [
|
|
148
|
-
["
|
|
149
|
-
["
|
|
138
|
+
["spend key", wallet.privateSpendKey],
|
|
139
|
+
["mnemonic", wallet.mnemonic],
|
|
150
140
|
["seed", wallet.seedHex],
|
|
151
|
-
["
|
|
152
|
-
["
|
|
141
|
+
["spend key half", wallet.spendHalf],
|
|
142
|
+
["view key half", wallet.viewHalf],
|
|
153
143
|
]) {
|
|
154
|
-
if (secret && json.includes(secret)) throw new Error(
|
|
144
|
+
if (secret && json.includes(secret)) throw new Error(`the watch-only share leaked a secret (${label})`);
|
|
155
145
|
}
|
|
156
146
|
return share;
|
|
157
147
|
}
|
|
158
148
|
|
|
159
|
-
//
|
|
160
|
-
// на кошелёк, и недостаточно, чтобы их потратить.
|
|
149
|
+
// What goes to the server: the address and view key - enough to see incoming, not to spend.
|
|
161
150
|
export function watchOnlyShareFromWallet({ swapId = null, wallet, restoreHeight = null } = {}) {
|
|
162
151
|
if (!wallet || !wallet.address || !wallet.privateViewKey) {
|
|
163
|
-
throw new Error("
|
|
152
|
+
throw new Error("a watch-only wallet needs an address and a private view key");
|
|
164
153
|
}
|
|
165
|
-
//
|
|
154
|
+
// Built from the allowlist: wallet fields not listed here will never go out.
|
|
166
155
|
return assertNoSecretsInShare(
|
|
167
156
|
{
|
|
168
157
|
swapId: swapId || null,
|