@ninsei-labs/sdk 0.39.3 → 0.40.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +55 -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 +561 -451
  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 +20 -8
  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,14 +1,13 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/mock/chain.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
- // Мок-чейн: escrow-контракт на EVM и Monero-нога.
5
+ // Mock chain: the escrow contract on EVM and the Monero leg.
7
6
  //
8
- // Здесь НЕТ реальных транзакций. Всё, что возвращается, помечено как mock, а хеши
9
- // детерминированы от id сделки - чтобы демо было воспроизводимо.
10
- // Точка подмены: когда появится контракт, эти функции заменяются на вызовы ethers.js,
11
- // а интерфейс (fund/lock/claim/refund/sweep) сохраняется.
7
+ // There are NO real transactions here. Everything returned is marked mock, and the hashes
8
+ // are deterministic from the swap id - so the demo is reproducible.
9
+ // Swap-out point: once the contract exists, these functions are replaced by ethers.js calls,
10
+ // while the interface (fund/lock/claim/refund/sweep) is preserved.
12
11
 
13
12
  import { randomHex } from "../core/format.js";
14
13
  import * as node from "../monero/node.js";
@@ -23,12 +22,12 @@ function hashOf(...parts) {
23
22
  return "0x" + hh.toString(16).padStart(8, "0") + randomHex(28);
24
23
  }
25
24
 
26
- // CREATE2-адрес per-order escrow (litepaper §8: свой контракт на сделку, без admin).
25
+ // CREATE2 address of the per-order escrow (litepaper §8: its own contract per swap, no admin).
27
26
  export function escrowAddress(swapId) {
28
27
  return "0x" + hashOf("escrow", swapId).slice(2, 42);
29
28
  }
30
29
 
31
- // Адрес совместного Monero-выхода (S_a + S_b, V_a + V_b в реальном протоколе).
30
+ // Address of the joint Monero output (S_a + S_b, V_a + V_b in the real protocol).
32
31
  export function moneroLockAddress(swapId) {
33
32
  return "8" + hashOf("xmrlock", swapId).slice(2, 96);
34
33
  }
@@ -41,10 +40,10 @@ export function fundEscrow({ swapId, ethAmountUsd, ethUsd }) {
41
40
  ethAmount: ethUsd > 0 ? ethAmountUsd / ethUsd : 0,
42
41
  valueUsd: ethAmountUsd,
43
42
  gasUsd: 0.21,
44
- // ИМЯ МЕТОДА НЕ ВЫДУМЫВАЕМ. Здесь стояло lockAndSwap(bytes32 quoteHash, bytes makerSig) - такого
45
- // метода в контракте нет вовсе, но именно эта строка уезжала в запись сделки и показывалась на экране
46
- // как "Escrow method". Демо-путь денег не двигает, поэтому честная подпись для него - эта, без
47
- // выдуманной сигнатуры. Настоящее имя вызова живой путь записывает сам, из фактического вызова.
43
+ // WE DO NOT INVENT THE METHOD NAME. This used to be lockAndSwap(bytes32 quoteHash, bytes makerSig) - no
44
+ // such method exists in the contract at all, yet that exact string went into the swap record and was shown
45
+ // on screen as "Escrow method". The demo path moves no money, so its honest label is this one, without the
46
+ // invented signature. The real call name is written by the live path itself, from the actual call.
48
47
  method: "simulated funding (no real transaction)",
49
48
  confirmations: 1,
50
49
  };
@@ -69,12 +68,12 @@ export function refundEscrow({ swapId, by }) {
69
68
  return { mock: true, txHash: hashOf("refund", swapId, by), by };
70
69
  }
71
70
 
72
- // Подтверждения Monero-ноды: 10 блоков по 2 минуты (litepaper §5.0).
71
+ // Monero node confirmations: 10 blocks of 2 minutes (litepaper §5.0).
73
72
  //
74
- // chainMode=mock - подтверждения идут по sim-времени (быстро, для показа потока).
75
- // chainMode=live - подтверждения считаются по реальному росту высоты mainnet-нода:
76
- // сделка тогда честно длится ~20 минут независимо от скорости демки. Если нод
77
- // недоступен или высота на момент лока не зафиксирована, откатываемся на sim-счётчик.
73
+ // chainMode=mock - confirmations follow sim-time (fast, for showing the flow).
74
+ // chainMode=live - confirmations are counted from the real height growth of the mainnet node:
75
+ // the swap then honestly takes ~20 minutes regardless of the demo speed. If the node
76
+ // is unavailable or the height at lock time was not recorded, we fall back to the sim counter.
78
77
  export function confirmations(swap, simElapsedMs) {
79
78
  const target = swap.timeline.confTarget;
80
79
 
@@ -1,29 +1,18 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/monero/address.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
- // Проверка и сборка Monero-адресов: формат + настоящий checksum + префиксы всех сетей.
7
- //
8
- // Важная деталь, на которой легко ошибиться: у Monero СВОЙ base58 - блочный
9
- // (8 байт кодируются 11 символами, остаток идёт в конце). Он НЕ совместим с обычным
10
- // биткойн-base58: там ведущий символ '1' означает нулевой байт, а у Monero блок кодируется
11
- // как ЧИСЛО, и '1' может быть просто цифрой внутри числа. Поэтому ни bs58.decode, ни
12
- // bs58.encode здесь не годятся - base58 реализован ниже численно (см. base58ToBlock).
13
- //
14
- // Зачем это нужно в демке: без проверки checksum опечатка в адресе получения
15
- // выглядит валидной, а в atomic-свопе ошибка в адресе означает потерянные деньги.
16
- //
17
- // Префиксы сетей (из src/cryptonote_config.h, официальные значения):
18
- // mainnet: 18 обычный, 19 с payment id, 42 суб-адрес
19
- // stagenet: 24 / 25 / 36
20
- // testnet: 53 / 54 / 63
21
- // Вендоренная monero-js умеет ТОЛЬКО mainnet (у неё в address.js жёстко 18/19/42),
22
- // поэтому здесь свой энкодер: библиотека даёт публичные ключи, а строку адреса собираем мы.
5
+ // Monero address checks and assembly: format + real checksum + network prefixes.
6
+ // A detail easy to get wrong: Monero has its OWN base58 - block-based (8 bytes -> 11 chars, the remainder last).
7
+ // It is NOT compatible with bitcoin-base58, where a leading '1' means a zero byte; here a block is a NUMBER and
8
+ // '1' can be just a digit. So bs58 does not fit and base58 is implemented numerically below.
9
+ // Without the checksum a typo in the payout address looks valid, and in an atomic swap that means lost money.
10
+ // Network prefixes (official): mainnet 18 plain, 19 with payment id, 42 subaddress.
11
+ // The vendored monero-js knows ONLY mainnet, so we have our own encoder: the library gives public keys, we build the string.
23
12
 
24
13
  const BLOCK_CHARS = 11;
25
14
 
26
- // encoded bytes -> chars (из monero-js/src/base58.js, ENC_BLOCK_BYTE_LENS)
15
+ // encoded bytes -> chars (from the vendored base58 table)
27
16
  const CHARS_BY_BYTES = { 1: 2, 2: 3, 3: 5, 4: 6, 5: 7, 6: 9, 7: 10, 8: 11 };
28
17
  const BYTES_BY_CHARS = { 2: 1, 3: 2, 5: 3, 6: 4, 7: 5, 9: 6, 10: 7, 11: 8 };
29
18
 
@@ -39,7 +28,7 @@ const PREFIXES = {
39
28
  63: { network: "testnet", kind: "subaddress" },
40
29
  };
41
30
 
42
- // обратная таблица: сеть + вид -> байт префикса (для сборки адреса)
31
+ // reverse table: network + kind -> prefix byte (for address assembly)
43
32
  export const PREFIX_BY_NETWORK = {
44
33
  mainnet: { primary: 18, integrated: 19, subaddress: 42 },
45
34
  stagenet: { primary: 24, integrated: 25, subaddress: 36 },
@@ -54,16 +43,16 @@ export function prefixFor(network, kind = "primary") {
54
43
  return prefix;
55
44
  }
56
45
 
57
- // Синхронная проверка формата (для UI до загрузки библиотеки).
46
+ // Synchronous format check (for the UI before the library loads).
58
47
  export function formatCheck(addr) {
59
48
  const a = String(addr || "").trim();
60
49
  if (![95, 106].includes(a.length)) return false;
61
50
  if (!/^[1-9A-HJ-NP-Za-km-z]+$/.test(a)) return false;
62
- // первая буква адреса определяется префиксом сети: 4/8 mainnet, 5/7 stagenet, 9/A/B testnet
51
+ // the first letter is set by the network prefix: 4/8 mainnet, 5/7 stagenet, 9/A/B testnet
63
52
  return ["4", "5", "7", "8", "9", "A", "B"].includes(a[0]);
64
53
  }
65
54
 
66
- // Первая буква адреса прямо следует из префикса: 4/8 - mainnet, 5/7 - stagenet, 9/A/B - testnet
55
+ // The first letter follows directly from the prefix: 4/8 mainnet, 5/7 stagenet, 9/A/B testnet
67
56
  export function networkFromShape(addr) {
68
57
  const a = String(addr || "").trim();
69
58
  if (!formatCheck(a)) return null;
@@ -93,7 +82,7 @@ async function loadLib() {
93
82
  }
94
83
  }
95
84
 
96
- // Алфавит Monero-base58 (тот же, что у Bitcoin, но правила кодирования другие).
85
+ // The Monero-base58 alphabet (same as Bitcoin, different encoding rules).
97
86
  const B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
98
87
  const B58_VALUES = (() => {
99
88
  const m = new Map();
@@ -101,7 +90,7 @@ const B58_VALUES = (() => {
101
90
  return m;
102
91
  })();
103
92
 
104
- // Блок байтов -> base58-строка (числом: никакого «ведущий '1' = нулевой байт»).
93
+ // Block of bytes -> base58 string (as a number: no "leading '1' = zero byte").
105
94
  function blockToBase58(block) {
106
95
  let num = 0n;
107
96
  for (const b of block) num = (num << 8n) | BigInt(b);
@@ -113,12 +102,10 @@ function blockToBase58(block) {
113
102
  return s;
114
103
  }
115
104
 
116
- // base58-строка блока -> ровно `bytes` байтов, выравнивание по ПРАВОМУ краю.
105
+ // base58 block string -> exactly `bytes` bytes, right-aligned.
117
106
  //
118
- // Почему не bs58 из npm: его декодер трактует каждый ведущий символ '1' как нулевой БАЙТ
119
- // (биткойн-семантика). В Monero блок кодируется как число, и '1' может быть просто цифрой
120
- // внутри числа - тогда bs58 выдаёт лишний нулевой байт, payload разъезжается, и checksum
121
- // ложно не сходится. На этом ловились: 18 адресов из 120 (15%) проверялись как «с опечаткой».
107
+ // Why not bs58 from npm: its decoder treats every leading '1' as a zero BYTE (bitcoin semantics). In Monero a
108
+ // block is a number and '1' can be just a digit, so bs58 adds a spurious zero byte and the checksum falsely fails.
122
109
  function base58ToBlock(str, bytes) {
123
110
  let num = 0n;
124
111
  for (const ch of str) {
@@ -135,11 +122,10 @@ function base58ToBlock(str, bytes) {
135
122
  return out;
136
123
  }
137
124
 
138
- // Блочный Monero-base58: строка -> байты.
125
+ // Block-based Monero base58: string -> bytes.
139
126
  //
140
- // Порядок блоков важен: Monero сначала кодирует полные 8-байтовые блоки (по 11 символов),
141
- // а НЕПОЛНЫЙ остаток идёт В КОНЦЕ и занимает меньше символов (см. monero-js/src/base58.js:
142
- // сначала tot_block_cnt блоков, затем last_block_enc_len). Поэтому остаток берём с конца строки.
127
+ // Block order matters: Monero encodes full 8-byte blocks first (11 chars each), and the PARTIAL remainder goes
128
+ // LAST with fewer chars. So the remainder is taken from the end of the string.
143
129
  export function decodeMoneroBase58(str) {
144
130
  const s = String(str);
145
131
  const L = s.length;
@@ -159,8 +145,8 @@ export function decodeMoneroBase58(str) {
159
145
  return out;
160
146
  }
161
147
 
162
- // Блочный Monero-base58: байты -> строка (обратная к decode выше).
163
- // Полные 8-байтовые блоки кодируются по 11 символов (слева дополняются '1'), остаток - в конце.
148
+ // Block-based Monero base58: bytes -> string (inverse of decode above). Full 8-byte blocks are 11 chars
149
+ // (left-padded with '1'), the remainder goes last.
164
150
  export function encodeMoneroBase58(bytes) {
165
151
  const arr = Array.from(bytes);
166
152
  const L = arr.length;
@@ -178,7 +164,7 @@ export function encodeMoneroBase58(bytes) {
178
164
  return out;
179
165
  }
180
166
 
181
- // Полезная нагрузка адреса (без checksum): префикс + публичный spend + публичный view [+ payment id]
167
+ // Address payload (without checksum): prefix + public spend + public view [+ payment id]
182
168
  export function addressPayload({ network, kind = "primary", spendPub, viewPub, paymentId }) {
183
169
  const prefix = prefixFor(network, kind);
184
170
  const spend = hexToBytes(spendPub, 32, "public spend key");
@@ -201,8 +187,8 @@ function hexToBytes(hex, expectedBytes, label) {
201
187
  return out;
202
188
  }
203
189
 
204
- // Сборка адреса из публичных ключей: deps = { keccak256 } (в браузере - из бандла monero-js).
205
- // bs58 здесь не нужен: base58 у Monero блочный и числовой, см. комментарий у base58ToBlock.
190
+ // Assembly of the address from public keys: deps = { keccak256 } (in the browser, from the monero-js bundle).
191
+ // bs58 is not needed: Monero base58 is block-based and numeric, see the comment at base58ToBlock.
206
192
  export function addressFromKeys({ network, kind = "primary", spendPub, viewPub, paymentId }, deps) {
207
193
  if (!deps?.keccak256) throw new Error("addressFromKeys needs { keccak256 }");
208
194
  const payload = addressPayload({ network, kind, spendPub, viewPub, paymentId });
@@ -213,9 +199,9 @@ export function addressFromKeys({ network, kind = "primary", spendPub, viewPub,
213
199
  return encodeMoneroBase58(full);
214
200
  }
215
201
 
216
- // Полная проверка: формат + блочный декод + keccak256-checksum + сеть.
217
- // expectNetwork (необязательно): если задан, адрес чужой сети считается ошибкой -
218
- // в свапе на stagenet mainnet-адрес это потерянные деньги, а не придирка.
202
+ // Full check: format + block decode + keccak256 checksum + network.
203
+ // expectNetwork (optional): if set, an address of another network is an error - on a stagenet swap a mainnet
204
+ // address is lost money, not a nitpick.
219
205
  export async function checkAddress(addr, opts = {}) {
220
206
  const a = String(addr || "").trim();
221
207
  if (!formatCheck(a)) {
@@ -235,13 +221,12 @@ export async function checkAddress(addr, opts = {}) {
235
221
  if (payloadLen < 65) throw new Error("Address is too short");
236
222
  const payload = bytes.slice(0, payloadLen);
237
223
  const checksum = bytes.slice(payloadLen);
238
- // keccak256 из бандла требует Buffer: Uint8Array он не принимает
224
+ // keccak256 from the bundle needs Buffer: it does not take Uint8Array
239
225
  const digest = Uint8Array.from(l.keccak256(l.Buffer.from(payload)));
240
226
  const match = checksum.length === 4 && checksum.every((b, i) => b === digest[i]);
241
227
  if (!match) {
242
- // Мок-адреса демки (wallet.js, режим mock) намеренно без checksum: они помечены в UI как
243
- // фиктивные. Если такой адрес попадёт сюда, «опечатка» - не единственная версия, и текст
244
- // должен это говорить, иначе отладка уходит не туда.
228
+ // The demo mock addresses (wallet.js, mock mode) intentionally have no checksum: the UI marks them as fake,
229
+ // so a "typo" is not the only explanation for such an address and the text must say so.
245
230
  return { ok: false, reason: "Checksum mismatch: the address has a typo, is corrupted, or is a demo mock address (mock addresses have no checksum)" };
246
231
  }
247
232
 
@@ -257,7 +242,7 @@ export async function checkAddress(addr, opts = {}) {
257
242
  }
258
243
  }
259
244
 
260
- // Хелпер для кода, который уже загрузил бандл: собрать адрес нужной сети из ключей кошелька.
245
+ // Helper for code that has already loaded the bundle: build an address of the right network from wallet keys.
261
246
  export async function addressForNetwork({ network, kind = "primary", spendPub, viewPub }) {
262
247
  const l = await loadLib();
263
248
  if (!l) throw new Error("monero-js bundle is not available");
@@ -1,68 +1,50 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/monero/halvesSweep.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
- // КЛЮЧИ ДЛЯ ВЫВОДА ИЗ ФАЙЛА ПОЛОВИН. Вынесено отдельным модулем, потому что это ОДНА логика на два
7
- // потребителя: страница вывода (sweep.js) и проверка (tools/check-sweep-halves.mjs). Пока код жил прямо на
8
- // странице, проверить его можно было только в браузере, а вторая копия в проверке неизбежно разошлась бы с
9
- // первой - и заметил бы это не тот, кто ошибся.
10
- //
11
- // ПОЧЕМУ ЭТО ГЛАВНАЯ ФУНКЦИЯ ВСЕГО ВЫВОДА. Контракт убеждается лишь в том, что ХЕШ раскрытой половины совпал
12
- // с обязательством. Связь «раскрытая половина и точка в адресе» проверить он не может: ed25519 в EVM нет.
13
- // Значит её проверяем мы, и ДО всякой синхронизации: (s_a + s_b)*G обязана совпасть с публичным ключом траты
14
- // ордера. Не совпало - не метём и говорим прямо.
15
- //
16
- // Секреты здесь не создаются: обе половины уже есть у стороны (своя - в файле, чужая - раскрыта в цепи).
5
+ // KEYS FOR SWEEPING FROM A HALVES FILE. One logic for two consumers: the sweep page (sweep.js) and the
6
+ // check, so the two cannot drift.
7
+ // This is the main check of the whole sweep: the contract only verifies that the HASH of the revealed half
8
+ // matched the commitment, but ed25519 does not exist in the EVM, so WE must verify that (s_a + s_b)*G equals
9
+ // the order's public spend key - before any synchronisation. No match: do not sweep and say so.
10
+ // No secrets are created here: both halves already exist for the side (its own in the file, the other on chain).
17
11
  export function spendKeysFromRevealedHalf({
18
12
  halves, ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewHalf, revealedHalf,
19
13
  }) {
20
- if (!halves) throw new Error("нужна математика половин");
14
+ if (!halves) throw new Error("half maths is required");
21
15
  for (const [name, v] of Object.entries({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewHalf, revealedHalf })) {
22
- if (v === undefined || v === null || v === "") throw new Error("не задано: " + name);
16
+ if (v === undefined || v === null || v === "") throw new Error("not set: " + name);
23
17
  }
24
18
  const sum = halves.combineHalves(BigInt(ownSpendHalf), BigInt(revealedHalf));
25
19
  const sumPub = halves.publicHalves(sum).ed;
26
- // Ожидаемый ключ траты собирается ИЗ СВОЕЙ ПОЛОВИНЫ И ЧУЖОЙ ТОЧКИ - то есть из того, что было в ордере.
27
- // ПОЛОВИНА ИЗ ФАЙЛА ПРИХОДИТ СТРОКОЙ, И ЭТО НАДО УЧЕСТЬ КАЖДЫЙ РАЗ. Строкой она была и в сумме, и здесь -
28
- // здесь забыли, и вывод XMR падал на живом стенде с «invalid field element: expected bigint, got string»
29
- // уже ПОСЛЕ чтения раскрытой половины из цепи: до этой строки дело доходило, а дальше нет.
20
+ // The expected spend key is built FROM OWN HALF AND THE COUNTERPARTY POINT (what was in the order).
21
+ // The half from the file arrives as a string, so it must be converted - forgetting this crashed the XMR
22
+ // sweep with "expected bigint, got string".
30
23
  const expected = halves.combinePublic(halves.publicHalves(BigInt(ownSpendHalf)).ed, otherSpendPoint);
31
24
  const norm = (h) => String(h).replace(/^0x/i, "").toLowerCase();
32
25
  if (norm(sumPub) !== norm(expected)) {
33
26
  throw new Error(
34
- "раскрытая половина не соответствует адресу: (s_a + s_b)*G != ключ траты ордера. " +
35
- "Половина из другого ордера или подменена - метать нельзя"
27
+ "revealed half does not match the address: (s_a + s_b)*G != the order spend key. " +
28
+ "The half is from another order or substituted - sweeping is not allowed"
36
29
  );
37
30
  }
38
- // Полный ключ ПРОСМОТРА - тоже сумма: своей половины из файла и половины контрагента. Тратить он не даёт,
39
- // а без него кошелёк не найдёт наши XMR в цепи: ключи просмотра и траты в Monero независимы.
31
+ // The full VIEW key is also a sum (own half from the file + the counterparty half); it cannot spend, but
32
+ // without it the wallet cannot find our XMR on chain.
40
33
  const viewSum = halves.combineHalves(BigInt(ownViewHalf), BigInt(otherViewHalf));
41
- // ПОРЯДОК БАЙТ - ЭТО ФОРМАТ, А НЕ КОСМЕТИКА, И ЗДЕСЬ ОН БЫЛ НЕВЕРЕН ДО 28.09.2026.
42
- // Прежняя редакция писала скаляр обычной записью числа: старший байт первым. Библиотека ждёт ЗАПИСЬ
43
- // MONERO - младшим байтом вперёд, как скаляр лежит в памяти.
34
+ // BYTE ORDER IS A FORMAT, NOT COSMETICS, and here it was wrong until 28.09.2026: the old code wrote the
35
+ // scalar most-significant-byte first, while the library expects Monero order (least-significant first).
36
+ // Both monero-ts 0.11.16 builds rejected the reversed key; the workarounds kept elsewhere must not fire.
44
37
  //
45
- // ЗАМЕР 28.09.2026, ОБЕ СБОРКИ monero-ts 0.11.16 (нативная в узле и вендорная в браузере), ОДНИ И ТЕ ЖЕ
46
- // ключи: своя же строка (createWalletKeys().getPrivateSpendKey()) принимается КАК ЕСТЬ и публичный ключ
47
- // совпадает, а развёрнутая отвергается словами "failed to verify secret spend key". Обе сборки ведут себя
48
- // ОДИНАКОВО - значит расхождение было нашим, а не библиотеки: прежняя запись числа давала развёрнутый
49
- // порядок, и обе точки входа чинили это обходным путём (страница вывода - reversedKey, инструмент узла -
50
- // flip). Обходные пути оставлены КАК СЕТЬ для чужого файла, но срабатывать не должны, и срабатывание
51
- // видно в журнале.
52
- //
53
- // ПРЕФИКСА 0x ЗДЕСЬ НЕТ И БЫТЬ НЕ ДОЛЖНО: библиотека выдаёт ключ 64 hex без префикса и такой принимает,
54
- // а вид "0x"+64 hex отвергает на разборе - "failed to parse secret spend key".
55
- const be32 = (v) => BigInt(v).toString(16).padStart(64, "0"); // обычная запись числа, старший байт первым
56
- const le32 = (v) => be32(v).match(/../g).reverse().join(""); // запись Monero: младший байт первым
38
+ // NO 0x PREFIX here: the library returns and accepts 64 hex without it, and rejects "0x"+64 hex at parse.
39
+ const be32 = (v) => BigInt(v).toString(16).padStart(64, "0"); // ordinary number notation, most significant byte first
40
+ const le32 = (v) => be32(v).match(/../g).reverse().join(""); // Monero notation: least significant byte first
57
41
  return { privateSpendKey: le32(sum), privateViewKey: le32(viewSum) };
58
42
  }
59
43
 
60
- // ОБРАТНЫЙ ПЕРЕХОД - ТОЖЕ ОДНА ФУНКЦИЯ НА ВСЕХ. Кто читает выданный ключ как ЧИСЛО, обязан развернуть порядок:
61
- // обычная запись числа (BigInt("0x" + ключ)) прочтёт запись Monero задом наперёд и даст другой скаляр - молча,
62
- // без всякой ошибки. Ровно на этом мы уже стояли: и проверка, и инструмент пары разбирали ключ напрямую, и
63
- // после исправления порядка байт оба обязаны были бы покраснеть - если бы их не поправили вместе с модулем.
44
+ // THE REVERSE IS ALSO ONE FUNCTION FOR ALL: reading a returned key directly as a number
45
+ // (BigInt("0x" + key)) silently reverses Monero order and gives another scalar.
64
46
  export function scalarFromMoneroHex(h) {
65
47
  const hex = String(h).replace(/^0x/i, "");
66
- if (!/^[0-9a-f]{64}$/.test(hex)) throw new Error("ключ не в записи Monero: ожидались 64 hex");
48
+ if (!/^[0-9a-f]{64}$/.test(hex)) throw new Error("key is not in Monero notation: expected 64 hex");
67
49
  return BigInt("0x" + hex.match(/../g).reverse().join(""));
68
50
  }
@@ -1,20 +1,19 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/monero/node.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
- // Живой Monero-нод: единственная реальная внешняя зависимость демки.
5
+ // The live Monero node: the only real external dependency of the demo.
7
6
  //
8
- // Адрес ноды - константа MONERO.node в www/js/core/config.js: наш прокси
9
- // https://xmr.arrakisswap.trade (конфиг .hermes/deploy/arrakis-xmr-rpc.conf).
10
- // Никаких параметров и переопределений у демки нет: адрес один и виден в коде.
11
- // Используем ноду для двух честных вещей:
12
- // 1) статус сети в шапке: реальная высота, nettype, доступность нода;
13
- // 2) режим "live chain": подтверждения Monero считаются по реальному росту высоты,
14
- // а не по таймеру (медленно - блок ~2 минуты, зато по-настоящему).
7
+ // The node address is the MONERO.node constant in www/js/core/config.js - the proxy
8
+ // address is supplied at deploy time (see the deploy config for the Monero RPC proxy).
9
+ // The demo has no parameters or overrides: the address is one and visible in the code.
10
+ // We use the node for two honest things:
11
+ // 1) network status in the header: real height, nettype, node availability;
12
+ // 2) "live chain" mode: Monero confirmations are counted from real height growth,
13
+ // not from a timer (slow - a block is ~2 minutes, but real).
15
14
  //
16
- // Балансы, outputs и ключи через этот RPC не запрашиваются: для этого нужен кошелёк
17
- // (wallet2 в WASM + view-ключ), это следующий шаг, а не то, что делает демка.
15
+ // Balances, outputs and keys are NOT queried through this RPC: that needs a wallet
16
+ // (wallet2 in WASM + view key), which is the next step, not what the demo does.
18
17
 
19
18
  import { MONERO } from "../core/config.js";
20
19
 
@@ -32,7 +31,7 @@ async function rpc(method, params = {}, path = "/json_rpc") {
32
31
  const res = await fetch(RPC + path, {
33
32
  method: "POST",
34
33
  headers: { "Content-Type": "application/json" },
35
- body: JSON.stringify({ jsonrpc: "2.0", id: "arrakis-demo", method, params }),
34
+ body: JSON.stringify({ jsonrpc: "2.0", id: "ninsei-demo", method, params }),
36
35
  signal: ctl.signal,
37
36
  });
38
37
  if (!res.ok) throw new Error("HTTP " + res.status);
@@ -52,7 +51,7 @@ export function lastError() {
52
51
  return cache.error;
53
52
  }
54
53
 
55
- // Высота сети на момент вызова (из кеша, если он свежий).
54
+ // Network height at call time (from cache, if fresh).
56
55
  export async function height() {
57
56
  const info = await getInfo();
58
57
  return info ? info.height : null;
@@ -94,7 +93,7 @@ function normalize(info) {
94
93
  };
95
94
  }
96
95
 
97
- // Опрос раз в 30 секунд, пока страница открыта: шапка показывает живую высоту.
96
+ // Poll every 30 seconds while the page is open: the header shows the live height.
98
97
  let poller = null;
99
98
  export function startPolling(onUpdate, intervalMs = 30_000) {
100
99
  if (poller) clearInterval(poller);