@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,23 +1,17 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/atomic/order.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
- // посылка и обязательство для контракта.
8
- // Разбор: .hermes/docs/20-key-shares.md, разделы 12, 19, 23, 25.
5
+ // ASSEMBLING ONE ORDER SIDE of an atomic swap: a key half, points, a proof, the encrypted envelope and the
6
+ // contract commitment.
9
7
  //
10
- // ЧТО ЭТО ЗАКРЫВАЕТ. До сих пор модули существовали по отдельности: половины, DLEQ, передача половины,
11
- // байтовый формат. Здесь они собираются в одну сторону ордера и, главное, здесь КОДОМ выполняются два
12
- // обязательных условия из раздела 20.5:
13
- // 1) перед блокировкой проверяется, что в посылке контрагента лежит настоящая половина (сверка с точкой);
14
- // 2) при этом проверяется и его DLEQ - что обе публичные точки стоят за одним скаляром.
15
- // Без этих двух проверок схема не даёт гарантии, и они не «на будущее», а здесь.
8
+ // WHAT THIS CLOSES. The modules used to exist separately; here they assemble into one order side and, above all,
9
+ // two mandatory conditions are enforced IN CODE: before locking, the counterparty envelope must hold a real half
10
+ // (checked against the point), and its DLEQ must verify (both public points stand for one scalar).
16
11
  //
17
- // ЧЕСТНОЕ ОГРАНИЧЕНИЕ ДЕМО. Настоящего мейкера (RFQ-узла) ещё нет: в market.js котировки фиктивные.
18
- // Поэтому сторона контрагента собирается ЛОКАЛЬНО и помечается как заглушка (standIn: true). Это
19
- // позволяет прогнать полный цикл - от половины до обязательства и до вскрытия - но ценность гарантии
20
- // появляется только с настоящим контрагентом, у которого свой ключ.
12
+ // AN HONEST DEMO LIMIT. There is no real maker yet (quotes are fake), so the counterparty side is built LOCALLY
13
+ // and marked standIn: true. This runs the full cycle, but the guarantee only becomes real with a counterparty
14
+ // that has its own key.
21
15
 
22
16
  import { createHalves } from "./halves.js";import { halfCommitmentHex } from "./orderHalfCommit.js";
23
17
  import { createDleq2 } from "./dleq2.js";
@@ -30,97 +24,85 @@ export function createOrderBuilder({ ed25519, secp256k1, keccak256, sha3_512, ra
30
24
  const dleq = createDleq2({ ed25519, secp256k1, keccak256, sha3_512, randomBytes });
31
25
  const enc = createHalfEnc({ secp256k1, ed25519, keccak256, randomBytes });
32
26
 
33
- // Сторона ордера: ДВЕ половины (траты и просмотра), их точки, доказательство и ключ шифрования.
27
+ // An order side: TWO halves (spend and view), their points, the proof and the encryption key.
34
28
  //
35
- // ЗАЧЕМ ОТДЕЛЬНАЯ ПОЛОВИНА ПРОСМОТРА. Ключ просмотра в Monero независим от ключа траты, и из половины
36
- // траты его не вывести. Без суммы половин ПРОСМОТРА кошелёк не найдёт входящие и не сможет сканировать:
37
- // половины траты для скана недостаточно. При этом делиться половиной просмотра БЕЗОПАСНО - она даёт
38
- // видеть и не даёт тратить; трата требует суммы половин ТРАТЫ.
29
+ // WHY A SEPARATE VIEW HALF. The Monero view key is independent of the spend key and cannot be derived from the
30
+ // spend half; without the sum of VIEW halves the wallet cannot find incoming funds. Sharing the view half is safe
31
+ // - it sees but cannot spend; spending needs the sum of SPEND halves.
39
32
  //
40
- // ДОКАЗАТЕЛЬСТВА ДЛЯ НЕЁ НЕ НУЖНО, и это осознанно: половина просмотра приходит вместе с котировкой и
41
- // сама определяет точку просмотра, поэтому подменять её нечем. Доказательство DLEQ остаётся там, где
42
- // оно решает - на половине ТРАТЫ, за которую платят ETH.
33
+ // NO PROOF IS NEEDED FOR IT, deliberately: the view half comes with the quote and defines its own view point, so
34
+ // it cannot be substituted. The DLEQ proof stays on the SPEND half, the one paid for with ETH.
43
35
  function newSide(orderContext, standIn = false, onBit) {
44
36
  const half = halves.newHalf();
45
- const pub = halves.publicHalves(half); // ed25519 и secp256k1
37
+ const pub = halves.publicHalves(half); // ed25519 and secp256k1
46
38
  const viewHalf = halves.newHalf();
47
- const viewPub = halves.publicHalves(viewHalf).ed; // точка просмотра: из неё собирается адрес
48
- const encPair = enc.recipientKeyPair(); // отдельно от ключа траты
49
- const proof = dleq.prove(half, orderContext, onBit); // привязано к контексту ордера
39
+ const viewPub = halves.publicHalves(viewHalf).ed; // view point: the address is built from it
40
+ const encPair = enc.recipientKeyPair(); // separate from the spend key
41
+ const proof = dleq.prove(half, orderContext, onBit); // bound to the order context
50
42
  return { half, pub, viewHalf, viewPub, enc: encPair, proof, standIn };
51
43
  }
52
44
 
53
- // Проверка стороны контрагента - это и есть условие (1) и (2) из раздела 20.5.
54
- // Проверка стороны контрагента: доказательство и - если посылка дана - вскрытие ЕГО посылки НАШИМ
55
- // приватным ключом со сверкой по ЕГО публичной точке.
45
+ // Checking the counterparty side IS conditions (1) and (2): its proof, and - if an envelope is given - opening
46
+ // ITS envelope with OUR private key and verifying against ITS public point.
56
47
  //
57
- // РОЛИ КЛЮЧЕЙ ЗДЕСЬ ПРИНЦИПИАЛЬНЫ, и первая версия этой функции их путала: она вскрывала посылку
58
- // приватным ключом самого контрагента, которого у нас нет и быть не может - посылка адресована ему.
59
- // Такая проверка не могла пройти никогда, но выглядела как работающая, что хуже всего.
48
+ // KEY ROLES ARE FUNDAMENTAL HERE, and the first version mixed them up: it opened the envelope with the
49
+ // counterparty's own key (which we do not have - the envelope is addressed to them). That check could never pass.
60
50
  function verifySide(side, orderContext, sealedPayloadBytes, ownEncPriv) {
61
51
  const proofOk = dleq.verify(side.proof, orderContext);
62
- if (!proofOk.ok) return { ok: false, reason: "DLEQ контрагента не прошёл: " + proofOk.reason };
63
- // ДОКАЗАННАЯ ТОЧКА ОБЯЗАНА БЫТЬ ТОЙ ЖЕ, ЧТО УЙДЁТ В ОРДЕР, и этой сверки не было.
52
+ if (!proofOk.ok) return { ok: false, reason: "counterparty DLEQ failed: " + proofOk.reason };
53
+ // THE PROVEN POINT MUST BE THE ONE THAT GOES INTO THE ORDER, and this check was missing. A proof confirms that
54
+ // ONE scalar stands behind a pair of points, but not that the proven ed25519 point is the one the joint address
55
+ // and the contract use. The counterparty could prove one point and put another in the order; the mismatch would
56
+ // surface only at XMR claim time.
64
57
  //
65
- // Чего не хватало без неё: доказательство подтверждает, что за парой точек стоит ОДИН скаляр, но НЕ
66
- // подтверждает, что доказанная точка ed25519 - та самая, по которой собирается общий адрес и которую
67
- // контракт потом заставит раскрыть. Контрагент мог предъявить корректное доказательство для одной
68
- // точки, а в ордер подставить другую. На своей стороне всё выглядело бы исправным, а расхождение
69
- // вылезло бы в самом конце - при заборе XMR, когда исправлять уже нечем.
70
- //
71
- // Сверка одна на обе стороны: у своей стороны pub.ed выведена из половины, у контрагента приходит из
72
- // котировки; в обоих случаях это ТА точка, которую увидят в контракте.
58
+ // One comparison for both sides: our pub.ed is derived from the half, the counterparty's comes from the quote;
59
+ // in both cases this is THE point the contract will see.
73
60
  if (side.pub && side.pub.ed && String(side.proof.XB).toLowerCase() !== String(side.pub.ed).toLowerCase()) {
74
- return { ok: false, reason: "доказанная точка ed25519 не совпадает с заявленной: " + side.proof.XB + " против " + side.pub.ed };
61
+ return { ok: false, reason: "the proven ed25519 point does not match the declared one: " + side.proof.XB + " vs " + side.pub.ed };
75
62
  }
76
63
  if (sealedPayloadBytes && ownEncPriv) {
77
64
  const opened = enc.open(enc.unpackSealed(sealedPayloadBytes), ownEncPriv, side.pub.ed);
78
- if (!opened.ok) return { ok: false, reason: "посылка контрагента не подтверждает половину: " + opened.reason };
65
+ if (!opened.ok) return { ok: false, reason: "the counterparty envelope does not confirm the half: " + opened.reason };
79
66
  }
80
67
  return { ok: true };
81
68
  }
82
69
 
83
- // Посылка со своей половиной, зашифрованная на ключ контрагента: то, что уходит ему и в контракт.
70
+ // The envelope with our half, encrypted to the counterparty key: what goes to them and to the contract.
84
71
  function sealFor(counterparty, ownHalf, orderContext) {
85
72
  const sealed = enc.seal(ownHalf, counterparty.enc.pub, orderContext);
86
73
  const bytes = packSealed(sealed);
87
74
  return { sealed, bytes, commitment: enc.sealedCommitmentOf(bytes) };
88
75
  }
89
76
 
90
- // Открыть посылку контрагента своей половиной и проверить, что это именно его половина.
77
+ // Open the counterparty envelope with our half and check it is really their half.
91
78
  function openFrom(sealedBytes, ownEncPriv, counterpartyEdPub) {
92
79
  return enc.open(enc.unpackSealed(sealedBytes), ownEncPriv, counterpartyEdPub);
93
80
  }
94
81
 
95
- // ОБЯЗАТЕЛЬСТВО НА ПОЛОВИНУ - то, что контракт проверяет в `claim` и `refund`, и то, что входит в
96
- // `termsHash`. Ровно это считает Solidity: `keccak256` от secp256k1-ТОЧКИ половины (`half·G`), а не от
97
- // числа. Значит формула обязана совпадать у приложения и у ноды - поэтому она живёт ОТДЕЛЬНЫМ модулем и
98
- // здесь только вызывается, а курс (`secp256k1`) передаётся снаружи: две копии одной формулы расходятся
99
- // ровно тогда, когда цепь отвергнет «точно правильную» половину (#123).
82
+ // THE HALF COMMITMENT - what the contract checks in `claim` and `refund` and what goes into `termsHash`. Solidity
83
+ // computes `keccak256` of the secp256k1 POINT of the half (`half*G`), not of the number. So the formula must match
84
+ // in the app and the node; it lives in a separate module and is only called here, with the curve passed in.
100
85
  function halfCommitment(half) {
101
86
  return halfCommitmentHex(half, secp256k1, keccak256);
102
87
  }
103
88
 
104
- // Общий адрес Monero: сумма публичных половин. Проверяется свойством (a+b)G == aG + bG.
89
+ // The joint Monero address: the sum of the public halves. Checked by the property (a+b)G == aG + bG.
105
90
  function jointAddressPoint(sideA, sideB) {
106
91
  return halves.combinePublic(sideA.pub.ed, sideB.pub.ed);
107
92
  }
108
93
 
109
- // ТОЧКА ПО ПОЛОВИНЕ. Нужна там, где от контрагента пришла ПОЛОВИНА (так она приходит в котировке под
110
- // ордер), а для адреса нужна точка. Точка из половины выводится тем же модулем половин, что и везде.
94
+ // POINT FROM A HALF. Needed where the counterparty sent a HALF (as in the quote) and the address needs a point.
111
95
  function pointOf(half) {
112
96
  return halves.publicHalves(typeof half === "bigint" ? half : BigInt(half)).ed;
113
97
  }
114
98
 
115
- // ОБЩИЙ АДРЕС MONERO - то, ради чего схема и построена. Ни одна сторона не знает чужой половины ТРАТЫ:
116
- // адрес собирается из СВОЕЙ половины и ЧУЖОЙ ТОЧКИ, потому что (a+b)G == aG + bG. До раскрытия второй
117
- // половины расчётом адрес существует, но потратить с него не может никто.
99
+ // THE JOINT MONERO ADDRESS - the whole point of the scheme. Neither side knows the other's SPEND half: the
100
+ // address is built from OUR half and THEIR point, because (a+b)G == aG + bG. Before the second half is revealed,
101
+ // the address exists but nobody can spend from it.
118
102
  //
119
- // КОДИРОВЩИК БЕРЁМ ТОТ ЖЕ, ЧТО У КОШЕЛЬКА (monero/address.js). Второй кодировщик разошёлся бы с первым
120
- // там, где ошибку видно только отправкой XMR, - и заметил бы её не тот, кто ошибся.
103
+ // SAME ENCODER AS THE WALLET (monero/address.js): a second encoder would diverge where the error is visible only by sending XMR.
121
104
  //
122
- // КЛЮЧ ПРОСМОТРА СОБИРАЕТСЯ ОТДЕЛЬНО И ИЗ СВОИХ ПОЛОВИН: в Monero ключи просмотра и траты независимы, и
123
- // подменять один другим нельзя. Без верной половины просмотра пользователь не увидит приход.
105
+ // THE VIEW KEY IS ASSEMBLED SEPARATELY FROM ITS OWN HALVES: in Monero, view and spend keys are independent.
124
106
  function jointAddress({ ownSpendHalf, ownViewHalf, otherSpendPoint, otherViewPoint, network }) {
125
107
  return swapAddressFromHalves({
126
108
  halves, addressFromKeys, deps: { keccak256 },
@@ -1,47 +1,23 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/atomic/orderContext.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
- // КАНОНИЧЕСКИЙ КОНТЕКСТ ОРДЕРА - то, к чему привязывается доказательство DLEQ.
7
- //
8
- // ЗАЧЕМ ОТДЕЛЬНЫЙ МОДУЛЬ. Раньше контекст был произвольной строкой, которую передавал вызывающий, а
9
- // заглушка контрагента просто дописывала к ней ":контрагент". Внутри одной вкладки это работало, но
10
- // означало ровно одно: доказательство привязано НИ К ЧЕМУ, что снаружи воспроизвести нельзя. Наш же
11
- // критерий приёмки (док 27, раздел 11) требует обратного: взял поля ордера с цепи, собрал точку,
12
- // проверил доказательство, сошлось. Значит контекст обязан собираться ИЗ ПОЛЕЙ ОРДЕРА, и собираться
13
- // одинаково у всех: у приложения, у ноды провайдера и у любого, кто захочет проверить сделку.
14
- //
15
- // ЧТО ВХОДИТ И ЧЕГО НЕ ВХОДИТ. Только УСЛОВИЯ ордера - то, что известно до всякого доказательства:
16
- // сеть, фабрика, адрес внёсшего, адрес забирающего, сумма, сроки и соль.
17
- //
18
- // ОБЯЗАТЕЛЬСТВ И ТОЧЕК ЗДЕСЬ НЕТ, И ЭТО ИСПРАВЛЕНИЕ, А НЕ УПУЩЕНИЕ. Первая версия их включала, и выходил
19
- // КРУГ: доказательство привязывается к контексту, а контекст брал обязательства и точки - то есть то, что
20
- // само появляется только ВМЕСТЕ с доказательством. Порядок получался невыполнимым.
21
- //
22
- // Структура от этого не слабеет, и вот почему: половина провайдера ВЫВОДИТСЯ из условий ордера, поэтому
23
- // одни и те же условия дают у него ту же половину, то же обязательство и ту же точку. Меняются условия -
24
- // меняется всё. А обязательства и точки обеих сторон отдельно закреплены В САМОМ ОРДЕРЕ и в его
25
- // termsHash: контракт делает их неизменяемыми после фондирования. Два разных места - две разные задачи.
26
- //
27
- // Адреса эскроу здесь тоже нет: он выводится из этих полей через CREATE2, то есть следствие, а не вход.
28
- //
29
- // ВЕРСИЯ КОНТЕКСТА. Префикс "arrakis-order-v1" отделяет контекст от любых других подписей и доказательств.
30
- // Менять состав полей можно только вместе с номером версии в префиксе: иначе два разных смысла оказались
31
- // бы под одной строкой, и проверка проходила бы там, где не должна.
32
-
33
- // ВЕРСИЯ ПОДНЯТА ДО v2: состав полей изменился (убраны обязательства и точки), а по нашему же правилу
34
- // смена смысла - это новая версия, а не дополнение. Выданных котировок в обращении ещё нет, поэтому
35
- // переход ничего не ломает; дальше так уже не получится.
36
- // ВЕРСИЯ 3: ИЗМЕНЁН ХЕШ ВЫЗОВА В ДОКАЗАТЕЛЬСТВЕ DLEQ (SHA3-512 и широкое приведение вместо keccak256).
37
- // Это не косметика: доказательства версии 2 не проходят проверку версии 3, поэтому метка поднята - иначе
38
- // живая котировка со старым доказательством ломалась бы молча, а причина искалась бы в подписи.
39
- export const ORDER_CONTEXT_VERSION = "arrakis-order-v3";
5
+ // CANONICAL ORDER CONTEXT - what the DLEQ proof binds to.
6
+ // It is built FROM THE ORDER FIELDS and identically everywhere (app, provider node, any verifier), so a
7
+ // proof can be checked from the on-chain order alone (docs 27, section 11).
8
+ // It holds only the ORDER CONDITIONS known before any proof: network, factory, depositor, claimer, amount,
9
+ // deadlines and salt. Commitments and points are NOT here - they appear only WITH the proof, so binding them
10
+ // would make the order circular. They are pinned separately in the order's termsHash.
11
+ // The escrow address is not here either: it is a CREATE2 consequence, not an input.
12
+ // CONTEXT VERSION. The prefix separates the context from any other signature or proof; the field set may change
13
+ // only together with the version. v2 changed the field set; v3 changed the DLEQ call hash (SHA3-512 wide
14
+ // reduction instead of keccak256), so a v2 proof does not verify under v3.
15
+ export const ORDER_CONTEXT_VERSION = "ninsei-order-v3";
40
16
 
41
17
  const norm = (v) => String(v === undefined || v === null ? "" : v).trim().toLowerCase();
42
18
  const dec = (v) => {
43
- // Числа приводим к десятичной строке: "0x10" и "16" - одно и то же число, и если оставить как пришло,
44
- // два вызывающих дадут разные контексты для одного ордера, а доказательство развалится без причины.
19
+ // Numbers are turned into decimal strings: "0x10" and "16" are the same number, and leaving them as
20
+ // received would give different contexts for one order and break the proof for no reason.
45
21
  if (typeof v === "bigint") return v.toString(10);
46
22
  if (typeof v === "number") return String(Math.trunc(v));
47
23
  const s = norm(v);
@@ -49,22 +25,22 @@ const dec = (v) => {
49
25
  return s;
50
26
  };
51
27
 
52
- // Поля перечислены ЯВНО и в фиксированном порядке. Порядок - часть протокола: переставить значит
53
- // сменить контекст, то есть разом сделать недействительными все выданные котировки под ордер.
28
+ // Fields are listed EXPLICITLY in a fixed order. The order is part of the protocol: rearranging it
29
+ // invalidates all issued quotes for the order at once.
54
30
  const FIELDS = [
55
- "chainId", // сеть: один и тот же ордер в разных сетях - разные ордера
56
- "factory", // какая фабрика создаёт эскроу
57
- "locker", // кто вносит ETH
58
- "claimer", // кому контракт заплатит
59
- "amount", // сумма в wei
60
- "readyBy", // срок готовности
61
- "t1", // срок расчёта
62
- "salt", // соль ордера
31
+ "chainId", // network: the same order on different networks is a different order
32
+ "factory", // which factory creates the escrow
33
+ "locker", // who deposits ETH
34
+ "claimer", // whom the contract pays
35
+ "amount", // amount in wei
36
+ "readyBy", // readiness deadline
37
+ "t1", // settlement deadline
38
+ "salt", // order salt
63
39
  ];
64
40
 
65
41
  export function orderContextString(order) {
66
- if (!order || typeof order !== "object") throw new Error("для контекста ордера нужен объект с полями ордера");
42
+ if (!order || typeof order !== "object") throw new Error("order context needs an object with the order fields");
67
43
  const missing = FIELDS.filter((f) => order[f] === undefined || order[f] === null || order[f] === "");
68
- if (missing.length) throw new Error("в контекст ордера не хватает полей: " + missing.join(", "));
44
+ if (missing.length) throw new Error("order context is missing fields: " + missing.join(", "));
69
45
  return ORDER_CONTEXT_VERSION + "|" + FIELDS.map((f) => dec(order[f])).join("|");
70
46
  }
@@ -1,29 +1,28 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/atomic/orderHalfCommit.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
- // ОБЯЗАТЕЛЬСТВО НА ПОЛОВИНУ - одна формула на все стороны.
5
+ // COMMITMENT TO A HALF - one formula for all sides.
7
6
  //
8
- // ЧТО СЧИТАЕТ ЦЕПЬ. `keccak256(x || y)` от secp256k1-ТОЧКИ половины `half·G` - то есть АДРЕС этой точки.
9
- // При раскрытии контракт проверяет не «хеш числа совпал», а что предъявленная половина РАЗМНОЖАЕТСЯ в ту же
10
- // точку (приём `ecrecover`, `NinseiEscrow.halfMatchesCommit`). Обязательство-ТОЧКА закрывает #123: раньше
11
- // `keccak256(be32(half))` принимал любое число с совпавшим хешем, и вносящий ETH мог закоммитить хеш мусора,
12
- // вернуть этим мусором ETH и запереть XMR второй стороны. Связь точки secp256k1 с точкой ed25519 (и адресом
13
- // Monero) держит DLEQ - её проверяет рискующая сторона ДО платежа.
7
+ // WHAT THE CHAIN COMPUTES. `keccak256(x || y)` of the secp256k1 POINT of the half `half*G` - that is, the ADDRESS of that point.
8
+ // On reveal the contract checks not "the hash of the number matched" but that the presented half MULTIPLIES up to that
9
+ // same point (the `ecrecover` trick, `NinseiEscrow.halfMatchesCommit`). The POINT-commitment closes that hole: before,
10
+ // `keccak256(be32(half))` accepted any number with a matching hash, and the ETH depositor could commit the hash of
11
+ // garbage, return the ETH with that garbage and lock the other side's XMR. The link between the secp256k1 point and
12
+ // the ed25519 point (and the Monero address) is held by DLEQ - the risking side checks it BEFORE payment.
14
13
  //
15
- // Это же обязательство входит в termsHash и в котировку. Значит формула обязана совпадать у приложения, у
16
- // ноды провайдера и у любого, кто станет проверять сделку. Две реализации одной формулы расходятся ровно
17
- // тогда, когда это дороже всего: цепь отвергнет половину, которая «точно правильная».
14
+ // This same commitment goes into termsHash and into the quote. So the formula must match in the app, in the
15
+ // provider's node and in anyone who will verify the swap. Two implementations of one formula diverge exactly
16
+ // when it is most expensive: the chain rejects a half that is "definitely correct".
18
17
  //
19
- // ТОЧКА БЕРЁТСЯ НЕСЖАТОЙ - 64 байта `x || y`, старшим байтом вперёд, как координаты ключа Ethereum.
20
- // `secp256k1` и `keccak256` передаются снаружи: модуль не тянет зависимостей сам, как и соседние.
18
+ // THE POINT IS TAKEN UNCOMPRESSED - 64 bytes `x || y`, big-byte-first, like Ethereum key coordinates.
19
+ // `secp256k1` and `keccak256` are passed in from outside: the module pulls no dependencies itself, like its neighbours.
21
20
 
22
21
  export function halfCommitmentHex(half, secp256k1, keccak256) {
23
- if (!secp256k1 || !keccak256) throw new Error("нужны secp256k1 и keccak256");
22
+ if (!secp256k1 || !keccak256) throw new Error("secp256k1 and keccak256 are required");
24
23
  const value = BigInt(half);
25
- if (value <= 0n) throw new Error("половина должна быть положительным числом");
26
- const raw = secp256k1.Point.BASE.multiply(value).toBytes(false); // 65 байт: 0x04 || x(32) || y(32)
27
- const xy = raw.slice(1); // 64 байта: x || y
24
+ if (value <= 0n) throw new Error("the half must be a positive number");
25
+ const raw = secp256k1.Point.BASE.multiply(value).toBytes(false); // 65 bytes: 0x04 || x(32) || y(32)
26
+ const xy = raw.slice(1); // 64 bytes: x || y
28
27
  return "0x" + Array.from(keccak256(xy), (b) => b.toString(16).padStart(2, "0")).join("");
29
28
  }