@ninsei-labs/sdk 0.39.3 → 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.
Files changed (69) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/engine/atomic/dleq2.js +86 -111
  3. package/engine/atomic/halfenc.js +58 -72
  4. package/engine/atomic/halves.js +31 -53
  5. package/engine/atomic/order-client.js +15 -16
  6. package/engine/atomic/order.js +44 -62
  7. package/engine/atomic/orderContext.js +26 -50
  8. package/engine/atomic/orderHalfCommit.js +17 -18
  9. package/engine/core/chainSource.js +54 -69
  10. package/engine/core/clock.js +11 -12
  11. package/engine/core/config.js +554 -449
  12. package/engine/core/finality.js +34 -53
  13. package/engine/core/format.js +35 -36
  14. package/engine/core/preSignGuards.js +109 -117
  15. package/engine/core/rfqSource.js +57 -75
  16. package/engine/core/sellWindow.js +23 -24
  17. package/engine/core/store.js +20 -21
  18. package/engine/core/swap.js +242 -243
  19. package/engine/evm/amounts.js +50 -51
  20. package/engine/evm/asyncExec.js +577 -0
  21. package/engine/evm/asyncLegs.js +308 -0
  22. package/engine/evm/asyncRoute.js +179 -0
  23. package/engine/evm/auth.js +41 -48
  24. package/engine/evm/claimGas.js +28 -47
  25. package/engine/evm/depositor.js +39 -43
  26. package/engine/evm/dex.js +266 -267
  27. package/engine/evm/escrow.js +113 -129
  28. package/engine/evm/factory.js +71 -77
  29. package/engine/evm/fees.js +41 -42
  30. package/engine/evm/funding.js +160 -162
  31. package/engine/evm/gasReserve.js +67 -36
  32. package/engine/evm/index.js +46 -53
  33. package/engine/evm/injected.js +34 -42
  34. package/engine/evm/permit.js +129 -56
  35. package/engine/evm/prices.js +30 -45
  36. package/engine/evm/session.js +188 -189
  37. package/engine/mock/chain.js +17 -18
  38. package/engine/monero/address.js +33 -48
  39. package/engine/monero/halvesSweep.js +25 -43
  40. package/engine/monero/node.js +14 -15
  41. package/engine/monero/scan.js +82 -95
  42. package/engine/monero/swapKeys.js +30 -33
  43. package/engine/monero/unlock.js +17 -20
  44. package/engine/monero/wallet.js +32 -42
  45. package/engine/recovery/recoveryFile.js +154 -161
  46. package/engine/recovery/restore.js +50 -61
  47. package/engine/sdk/bridge.js +244 -234
  48. package/engine/ui/dom.js +4 -5
  49. package/engine/ui/walletModal.js +20 -31
  50. package/index.d.ts +8 -0
  51. package/package.json +1 -1
  52. package/src/adapters/evm-wallet.mjs +16 -0
  53. package/src/adapters/monero-wallet.mjs +1 -1
  54. package/src/adapters/monero.mjs +1 -1
  55. package/src/adapters/node-test-evm-wallet.mjs +5 -5
  56. package/src/config.mjs +1 -1
  57. package/src/engine.mjs +4 -0
  58. package/src/legs/cow-spec.mjs +254 -0
  59. package/src/legs/cow.mjs +315 -0
  60. package/src/legs/evm.mjs +86 -2
  61. package/src/legs/kyber-spec.mjs +265 -0
  62. package/src/legs/kyber.mjs +425 -0
  63. package/src/legs/shape.mjs +35 -0
  64. package/src/legs/uniswaprelay-spec.mjs +541 -0
  65. package/src/legs/uniswaprelay.mjs +422 -0
  66. package/src/legs/uniswapx-spec.mjs +329 -0
  67. package/src/legs/uniswapx.mjs +362 -0
  68. package/src/quotes.mjs +1 -1
  69. package/src/swaps.mjs +1 -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, then run: node tools/build-sdk-engine.mjs
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
- // Обратный путь recovery-файла: файл → кошелёк, которым можно забрать XMR.
7
- //
8
- // Прямой путь (генерация файла) - в recoveryFile.js. Здесь то, ради чего файл вообще существует:
9
- // восстановление, когда вкладка потеряна. Два правила закреплены кодом, а не комментарием:
10
- //
11
- // 1) кошелёк восстанавливается из мнемоники, и восстановленный адрес ОБЯЗАН совпасть с адресом
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
- // ДВЕ РАЗНЫЕ СХЕМЫ, И ОБЕ ЖИВЫЕ. Файл версии 2 несёт МНЕМОНИКУ: из неё собирается полный кошелёк. Файл версии 3
25
- // несёт ПОЛОВИНЫ, и полного ключа траты в нём НЕТ И БЫТЬ НЕ МОЖЕТ: у каждой стороны только своя половина,
26
- // вторая раскрывается в цепи при заборе ETH. Поэтому из файла версии 3 собирается ВИДЯЩИЙ кошелёк (ключ
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
- // динамически, потому что статический импорт бандла сломал бы прогоны в Node), а в тестах передаются свои.
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
- // Мнемоника передаётся явно: monero-js проверит контрольную сумму и откажется работать,
38
- // если фраза битая (в wallet.js это теперь ошибка, а не тихая генерация нового кошелька).
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(`ключ ${field} из файла не совпал с выведенным из мнемоники: файл изменён`);
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
- // Ради этого высота и хранится: без неё кошелёк сканирует историю Monero с нуля (десятки часов).
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
- // ВОССТАНОВЛЕНИЕ ИЗ ПОЛОВИН (файл версии 3). Что здесь есть и чего здесь нет:
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=0951c3dc");
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("нет keccak256: без него адрес не проверить");
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
- "контрагента, которая раскрывается в цепи при заборе ETH",
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
- // проверить тестом: сейчас share собирается по белому списку полей, поэтому секрет в него
136
- // попасть не может. Но если завтра кто-то добавит в share поле «на всякий случай», падать это
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 и viewHalf добавлены вместе со схемой половин: это секреты той же силы, что и ключ траты,
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 не должен содержать " + name);
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
- ["ключ траты", wallet.privateSpendKey],
149
- ["мнемоника", wallet.mnemonic],
138
+ ["spend key", wallet.privateSpendKey],
139
+ ["mnemonic", wallet.mnemonic],
150
140
  ["seed", wallet.seedHex],
151
- ["половина ключа траты", wallet.spendHalf],
152
- ["половина ключа просмотра", wallet.viewHalf],
141
+ ["spend key half", wallet.spendHalf],
142
+ ["view key half", wallet.viewHalf],
153
143
  ]) {
154
- if (secret && json.includes(secret)) throw new Error(`в watch-only share попал секрет (${label})`);
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
- // То, что уходит на сервер: адрес и view key - этого достаточно, чтобы видеть входящие
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("для watch-only нужен кошелёк с адресом и приватным view key");
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,