@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,182 +1,167 @@
1
1
  // GENERATED FILE - a byte-for-byte copy of the engine module www/js/evm/escrow.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
- // Вызовы контракта-эскроу ArrakisSwap (один экземпляр = один ордер).
5
+ // Calls to the NinseiSwap escrow contract (one instance = one order).
7
6
  //
8
- // Библиотек для EVM в проекте нет: у эскроу ТРИ функции, которые двигают состояние (markReady, claim,
9
- // refund), и несколько читаемых, поэтому calldata собирается селектором и словами по 32 байта.
10
- // ФУНКЦИИ ВНЕСЕНИЯ У ЭСКРОУ НЕТ: ордер рождается зафондированным в транзакции создания, и двухшаговый
11
- // путь («создать пустым, внести вторым вызовом») убран вместе с ней - он и был источником запирания
12
- // денег в закрытом ордере. Отсюда и отсутствие селектора fund() в таблице ниже.
13
- // Селекторы вычислены инструментом, не вручную - и это те подписи, которые у контракта ЕСТЬ:
7
+ // There is no EVM library here: the escrow has three state-moving functions (markReady, claim, refund) and a few
8
+ // readers, so calldata is built from a selector and 32-byte words. There is NO FUNDING FUNCTION: an order is born
9
+ // funded in the creation transaction, and the two-step path (create empty, fund by a second call) was removed with
10
+ // it - that path locked money inside a closed order. Hence no fund() selector below.
11
+ // Selectors were computed by a tool, and they are the signatures the contract HAS:
14
12
  // cast sig "markReady()" -> 0x1ca087e7
15
13
  // cast sig "claim(bytes32)" -> 0xbd66528a
16
- // cast sig "refund(bytes32)" -> 0x7249fbb6 (refund() БЕЗ аргумента у контракта нет: половина обязательна, :294)
14
+ // cast sig "refund(bytes32)" -> 0x7249fbb6 (no refund() without an argument: the half is mandatory)
17
15
  // cast sig "status()" -> 0x200d2ed2
18
- // Проверяются в tools/check-evm-abi.mjs сверкой с эталоном Foundry (cast sig / cast calldata).
16
+ // They are cross-checked against the Foundry reference (cast sig / cast calldata).
19
17
  //
20
- // ПРО СОБЫТИЯ - ПРЕЖНЯЯ РЕДАКЦИЯ ЭТОГО КОММЕНТАРИЯ ВРАЛА, и врала дважды. У эскроу события ЕСТЬ, три:
21
- // HalfRevealed(bytes32 half, bool byClaimer) - mvp/contracts/NinseiEscrow.sol:131
22
- // OrderTerms(...) - :133-139
23
- // Ready(address indexed by) - :141
24
- // StrayRescued(address indexed token, ...) - :145
25
- // Состояние всё же читается вызовом status(), и причина не в отсутствии событий, а в двух других вещах:
26
- // 1) ВНЕСЕНИЕ ДЕНЕГ СОБЫТИЯ НЕ ИМЕЕТ ВООБЩЕ: флаг funded ставится в конструкторе (:232-237) вместе с
27
- // эмиссией OrderTerms, но отдельного «внесено» в логах нет, а это первое из состояний ордера;
28
- // 2) status() (:406-408) заведён в контракте именно «фронту и watchtower'у» и отдаёт все четыре
29
- // величины ОДНИМ вызовом - внесено, забрано, возвращено, остаток - по адресу эскроу, который уже
30
- // известен из записи сделки. Следить для этого за логами с блока создания не нужно.
31
- // Событие HalfRevealed при этом несёт признак byClaimer, то есть из него видно и «забрано», и
32
- // «возвращено»: тому, кто ведёт журнал ордеров, подписка на него полезна. Для чтения состояния
33
- // конкретного ордера на месте нужен status().
18
+ // EVENTS EXIST - THREE OF THEM:
19
+ // HalfRevealed(bytes32 half, bool byClaimer), OrderTerms(...), Ready(address indexed by), StrayRescued(...)
20
+ // State is still read by calling status(), and the reason is not the absence of events:
21
+ // 1) FUNDING HAS NO EVENT AT ALL: the funded flag is set in the constructor together with OrderTerms, so the
22
+ // first order state never reaches the logs;
23
+ // 2) status() returns all four values - funded, claimed, refunded, balance - in ONE call by escrow address.
24
+ // There is no need to follow logs from the creation block. HalfRevealed carries byClaimer, so it tells both
25
+ // "claimed" and "refunded": useful for whoever keeps an order journal, but status() reads one order on the spot.
34
26
 
35
27
  export const ESCROW_METHODS = {
36
- // ВНЕСЕНИЯ ЗДЕСЬ НЕТ НАМЕРЕННО. Прежний fund() кодировался как 0xb60d4288; функции у контракта больше
37
- // нет, а её правила переехали в рождение ордера: ровно `amount + fee` проверяет конструктор, и платит
38
- // тот, кто назван внёсшим, - других транзакций у ордера не бывает. Вернувшаяся строка с этим селектором
39
- // краснит tools/check-evm-abi.mjs.
40
- // отметить готовность: "XMR вижу". Без неё забор невозможен (:233) - это защита от забора ETH без XMR.
41
- // Ставит её только внёсший, только на зафондированном ордере и только до readyBy (:218-222).
28
+ // NO FUNDING HERE, ON PURPOSE. The old fund() encoded as 0xb60d4288; the contract no longer has it, and its
29
+ // rules moved into order birth: the constructor checks exactly `amount + fee`, paid by the named depositor. A
30
+ // returning line with this selector reddens the ABI check.
31
+ // mark readiness: "I see the XMR". Without it a claim is impossible - the guard against claiming ETH without XMR.
32
+ // Only the depositor sets it, only on a funded order, and only before readyBy.
42
33
  markReady: "0x1ca087e7",
43
- // забрать, предъявив СВОЮ половину; её хеш (обязательство) зашит в ордер при создании.
44
- // Вызвать вправе кто угодно - адрес отправителя контракт не проверяет (:232-235), - но деньги уходят
45
- // не ему, а записанному claimer (:242), поэтому переадресовать их вызывающий не может.
34
+ // claim, presenting YOUR half; its hash (the commitment) was fixed in the order at creation.
35
+ // Anyone may send it - the contract does not check the sender - but the money goes to the recorded claimer, so
36
+ // the caller cannot redirect it.
46
37
  claim: "0xbd66528a",
47
- // вернуть средства после срока t1, ПРЕДЪЯВИВ ПОЛОВИНУ ВНЁСШЕГО: контракт сверяет её хеш с
48
- // обязательством commitHalfLocker (:253, поле :79) и отвергает чужую половину (BadHalf).
49
- // Отсюда уточнение к прежней формулировке "может позвать кто угодно": отправить транзакцию вправе
50
- // любой (:246-249, роль watchtower), но провести её может лишь тот, у кого есть половина внёсшего.
51
- // Деньги при этом идут ВСЕГДА внёсшему (:260), а половина публикуется - по ней он заберёт своё XMR.
38
+ // refund after t1, PRESENTING THE DEPOSITOR'S HALF: the contract checks its hash against commitHalfLocker and
39
+ // rejects a foreign half (BadHalf). So "anyone may call" is precise: anyone may SEND the transaction (the
40
+ // watchtower role), but only the holder of the depositor's half can carry it through. The money always goes to
41
+ // the depositor, and the half is published - with it he claims his XMR.
52
42
  refund: "0x7249fbb6",
53
- // состояние: (заблокировано, забрано, возвращено, остаток)
43
+ // state: (funded, claimed, refunded, balance)
54
44
  status: "0x200d2ed2",
55
- // СРОК ОДИН. До t1 ETH забирает только claimer по своей половине и только при отметке ready (:233),
56
- // с t1 забор запрещён (:234), а вернуть вправе кто угодно, предъявив половину внёсшего (:250-253).
57
- // Селектора t0() здесь больше нет: поля t0 в контракте не существует (решение
58
- // 2026-09-16, док 26). У ордеров, созданных раньше, срок лежит в записи сделки, и правило для них
59
- // прежнее - см. derive() в core/swap.js.
45
+ // ONE DEADLINE. Before t1 only the claimer may claim by his half and only with the ready mark; from t1 a claim
46
+ // is forbidden and anyone may refund by presenting the depositor's half. There is no t0() selector: the t0 field
47
+ // does not exist in the contract. Orders created earlier carry their deadline in the deal record, with the old
48
+ // rule - see derive() in core/swap.js.
60
49
  t1: "0xfb5343f3",
61
- // до этого срока внёсший может отметить готовность; строго раньше t1
50
+ // up to this deadline the depositor may mark readiness; strictly before t1
62
51
  readyBy: "0x53fc2f40",
63
- // отмечена ли готовность: без неё забор закрыт
52
+ // whether readiness is marked: without it a claim is closed
64
53
  ready: "0x6defbf80",
65
- // раскрытая расчётом половина: из неё вторая сторона собирает ключ траты
54
+ // the half revealed by settlement: from it the other side assembles the spend key
66
55
  revealedHalf: "0x3f267bb6",
67
- // ТОЧКИ И ОБЯЗАТЕЛЬСТВА ОРДЕРА, прочитанные У САМОГО ЭСКРОУ. Нужны перед подписью: доказательство DLEQ
68
- // связывает половину с точкой, но не мешает положить в ордер ДРУГИЕ точки - тогда общий адрес Monero
69
- // соберётся не из тех половин, а узнать об этом можно, только сверив СЛОТЫ ЖИВОЙ ЦЕПИ с тем, что собрала
70
- // страница. Селекторы посчитаны `cast sig` по фактическим подписям; сверка таблицы - в
71
- // tools/check-evm-abi.mjs (отдельный зуб на каждое имя).
72
- edPointLocker: "0x8c03382b", // точка траты внёсшего: половина общего адреса
73
- edPointClaimer: "0xdd98c465", // и точка траты забирающего: сверяется с котировкой, а не берётся на слово
74
- edViewPointLocker: "0x9a06a5fe", // и его точка просмотра: без неё адрес Monero не собрать
75
- commitHalfLocker: "0x498b422e", // обязательство на половину внёсшего: по нему контракт проверит refund
76
- commitHalfClaimer: "0x7d4c81f7", // обязательство на половину забирающего: по нему контракт проверит claim
77
- // условия ордера одним хешем. Нужен, чтобы файл восстановления был ПРИВЯЗАН к ордеру на цепи: по нему
78
- // владелец файла проверяет, что файл относится именно к этому эскроу, а не к похожему.
79
- // ГРАНИЦА, КОТОРУЮ НАЗВАЛ АУДИТ, ЗАКРЫТА РЕШЕНИЕМ. Раньше точки просмотра внёсшего (edViewPointLocker) в хеше
80
- // НЕ было, и сверку именно этой точки приходилось делать отдельно. Теперь поле входит в слепок (:346) на
81
- // своё место - сразу после точки забирающего, как в событии OrderTerms, - поэтому ордер сверяется
82
- // ЦЕЛИКОМ, и отдельная сверка точки просмотра больше не нужна. ЦЕНА НАЗВАНА ПРЯМО: слепок у ВСЕХ ордеров
83
- // другой, значит фабрика и эскроу передеплоиваются, а адреса, записанные где-либо до этого (config.js,
84
- // networks.json, записи сделок), к новым ордерам не относятся.
56
+ // ORDER POINTS AND COMMITMENTS, read FROM THE ESCROW ITSELF. Needed before signing: the DLEQ proof binds a half
57
+ // to a point but does not stop OTHER points from being put into the order - then the Monero address would be
58
+ // assembled from the wrong halves, and only a comparison of the LIVE CHAIN slots with what the page assembled
59
+ // reveals it. Selectors are `cast sig` over the actual signatures; the ABI check cross-checks the table.
60
+ edPointLocker: "0x8c03382b", // the depositor's spend point: half of the joint address
61
+ edPointClaimer: "0xdd98c465", // and the claimer's spend point: checked against the quote, not taken on trust
62
+ edViewPointLocker: "0x9a06a5fe", // and his view point: without it the Monero address cannot be built
63
+ commitHalfLocker: "0x498b422e", // commitment to the depositor's half: the contract checks refund by it
64
+ commitHalfClaimer: "0x7d4c81f7", // commitment to the claimer's half: the contract checks claim by it
65
+ // order terms in one hash. It ties the recovery file to the on-chain order: by it the file's owner checks the
66
+ // file belongs to THIS escrow and not a look-alike.
67
+ // A FORMER GAP, CLOSED BY DECISION: the depositor's view point used to be OUTSIDE the hash, so it had to be
68
+ // checked separately. Now it is inside, right after the claimer's point as in the OrderTerms event, so the order
69
+ // is checked WHOLE. THE PRICE IS NAMED: every order's hash changes, so the factory and escrow are redeployed,
70
+ // and addresses recorded before that do not refer to the new orders.
85
71
  termsHash: "0xb311d9fd",
86
- // КОМИССИЯ, РЕЕСТР И ПРОИСХОЖДЕНИЕ - ВСЁ ЧИТАЕТСЯ У САМОГО ЭСКРОУ. У фабрики этих полей больше нет
87
- // (роутер удалён, решение #76): ставка, величина и получатель комиссии, адрес реестра, провайдер и ключ
88
- // подписи приходят в подписанной котировке и лежат в ордере неизменяемыми полями. Селекторы - `cast sig`
89
- // по фактическим подписям, как и всё остальное в этой таблице; сверка - в tools/check-evm-abi.mjs.
90
- fee: "0xddca3f43", // величина комиссии в wei (посчитана при создании ордера)
91
- feeBps: "0x24a9d853", // ставка в базисных пунктах (30 = 0.30%)
92
- feeRecipient: "0x46904840", // кому комиссия зачисляется в кассу
93
- feeOnTop: "0x2296cc23", // true - покупка (комиссия сверху), false - продажа (вычет из выплаты)
94
- registry: "0x7b103999", // адрес реестра мейкеров ИЗ УСЛОВИЙ ОРДЕРА
95
- cashier: "0xed740e97", // касса комиссии ИЗ УСЛОВИЙ ОРДЕРА (#103): фабрика её НЕ заводит
96
- provider: "0x085d4883", // провайдер, заявленный в котировке (проверен по реестру)
97
- quoteKey: "0x391d53ca", // ключ, которым подписана котировка
98
- validUntil: "0xddac6654", // срок годности котировки (СЕКУНДЫ)
99
- nonce: "0xaffed0e0", // нонс котировки
72
+ // FEE, REGISTRY AND ORIGIN - ALL READ FROM THE ESCROW. The factory no longer has these fields (the router was
73
+ // removed): the rate, amount and recipient of the fee, the registry address, the provider and the signing key
74
+ // come in the signed quote as immutable order fields. Selectors are `cast sig` over the actual signatures.
75
+ fee: "0xddca3f43", // fee amount in wei (computed at order creation)
76
+ feeBps: "0x24a9d853", // rate in basis points (30 = 0.30%)
77
+ feeRecipient: "0x46904840", // who the fee is credited to in the cashier
78
+ feeOnTop: "0x2296cc23", // true - a buy (fee on top), false - a sell (deducted from the payout)
79
+ registry: "0x7b103999", // maker registry address FROM THE ORDER TERMS
80
+ cashier: "0xed740e97", // fee cashier FROM THE ORDER TERMS: the factory does NOT create it
81
+ provider: "0x085d4883", // the provider declared in the quote (checked against the registry)
82
+ quoteKey: "0x391d53ca", // the key the quote was signed with
83
+ validUntil: "0xddac6654", // quote validity (SECONDS)
84
+ nonce: "0xaffed0e0", // quote nonce
100
85
  };
101
86
 
102
- // Динамических аргументов у эскроу НЕТ, поэтому все вызовы ниже - голые селекторы. Кодировщика внесения
103
- // здесь больше нет: у эскроу нет самой функции (см. заметку в ESCROW_METHODS).
104
- // call markReady(): аргументов нет.
87
+ // The escrow has NO dynamic arguments, so every call below is a bare selector. The funding encoder is gone with
88
+ // the function itself (see the note in ESCROW_METHODS).
89
+ // call markReady(): no arguments.
105
90
  export function encodeMarkReady() {
106
91
  return ESCROW_METHODS.markReady;
107
92
  }
108
93
 
109
- // call ready()/readyBy()/revealedHalf(): аргументов нет - calldata у них равен самому селектору,
110
- // поэтому отдельных кодировщиков здесь нет, а селекторы лежат в ESCROW_METHODS выше.
94
+ // call ready()/readyBy()/revealedHalf(): no arguments - their calldata equals the selector, so there are no
95
+ // separate encoders and the selectors live in ESCROW_METHODS above.
111
96
 
112
- // call claim(secret): секрет - bytes32. По контракту это половина ЗАБИРАЮЩЕГО (имя параметра -
113
- // halfClaimer, :231), а "секретом" её называют потому, что она служит прообразом обязательства
114
- // commitHalfClaimer: контракт сверяет keccak256 предъявленной половины со своим обязательством
115
- // (:235) и ничего больше - связь половины с адресом Monero проверяет не он (док 27 §10).
97
+ // call claim(secret): the secret is bytes32. In the contract it is the CLAIMER'S half (the parameter is named
98
+ // halfClaimer), called a "secret" because it is the preimage of the commitHalfClaimer commitment: the contract
99
+ // compares keccak256 of the presented half with its commitment and nothing more - it does not check the link
100
+ // between the half and the Monero address.
116
101
  export function encodeClaim(secret) {
117
- return ESCROW_METHODS.claim + halfAsThirtyTwoBytes(secret, "секрет");
102
+ return ESCROW_METHODS.claim + halfAsThirtyTwoBytes(secret, "secret");
118
103
  }
119
104
 
120
- // ПОЛОВИНА - ЧИСЛО, А НЕ СТРОКА ФИКСИРОВАННОЙ ДЛИНЫ. В записи сделки и в файле восстановления половина
121
- // хранится как есть, и ведущий ноль теряется: 63 знака вместо 64. Контракту нужны ровно 32 байта, поэтому
122
- // добиваем слева (как это делает нода в своём заборе). Без этого возврат был невозможен в принципе:
123
- // снаружи это выглядело как "половина должна быть 32 байтами" на живом нажатии кнопки возврата.
105
+ // A HALF IS A NUMBER, NOT A FIXED-LENGTH STRING. In the deal record and the recovery file it is stored as is, and
106
+ // a leading zero is lost: 63 digits instead of 64. The contract needs exactly 32 bytes, so we pad on the left (as
107
+ // the node does in its claim). Without this a refund was impossible in principle: on screen it looked like "the
108
+ // half must be 32 bytes" on a live click of the refund button.
124
109
  function halfAsThirtyTwoBytes(value, what) {
125
110
  const hex = String(value || "").replace(/^0x/i, "").toLowerCase();
126
- if (!/^[0-9a-f]{1,64}$/.test(hex)) throw new Error(what + " должен быть числом до 32 байт: " + value);
111
+ if (!/^[0-9a-f]{1,64}$/.test(hex)) throw new Error(what + " must be a number of up to 32 bytes: " + value);
127
112
  return hex.padStart(64, "0");
128
113
  }
129
114
 
130
- // call refund(bytes32 halfLocker): предъявляется половина ВНЁСШЕГО, а не половина вызывающего -
131
- // контракт сверяет её хеш с обязательством commitHalfLocker (:253) и чужую отвергает (BadHalf).
132
- // Отправителя контракт не проверяет: право на вызов даёт сама половина (:246-249).
115
+ // call refund(bytes32 halfLocker): the DEPOSITOR'S half is presented, not the caller's - the contract checks its
116
+ // hash against commitHalfLocker and rejects a foreign one (BadHalf). The sender is not checked: the half itself
117
+ // grants the right to call.
133
118
  export function encodeRefund(halfLocker) {
134
- return ESCROW_METHODS.refund + halfAsThirtyTwoBytes(halfLocker, "половина");
119
+ return ESCROW_METHODS.refund + halfAsThirtyTwoBytes(halfLocker, "half");
135
120
  }
136
121
 
137
- // call status(): аргументов нет.
122
+ // call status(): no arguments.
138
123
  export function encodeStatus() {
139
124
  return ESCROW_METHODS.status;
140
125
  }
141
126
 
142
- // call t1(): аргументов нет.
127
+ // call t1(): no arguments.
143
128
  export function encodeT1() {
144
129
  return ESCROW_METHODS.t1;
145
130
  }
146
131
 
147
- // call termsHash(): аргументов нет.
132
+ // call termsHash(): no arguments.
148
133
  export function encodeTermsHash() {
149
134
  return ESCROW_METHODS.termsHash;
150
135
  }
151
136
 
152
- // call к методу эскроу БЕЗ аргументов по имени. Нужен там, где чтений много (слоты ордера): отдельная
153
- // функция на каждый селектор завела бы вторую таблицу тех же имён, и она разошлась бы с первой молча.
137
+ // call an escrow method WITHOUT arguments by name. Used where there are many reads (the order slots): a function
138
+ // per selector would mean a second table of the same names, free to diverge silently.
154
139
  export function encodeRead(name) {
155
140
  const sel = ESCROW_METHODS[name];
156
- if (typeof sel !== "string" || !/^0x[0-9a-f]{8}$/.test(sel)) throw new Error("нет такого метода эскроу: " + name);
141
+ if (typeof sel !== "string" || !/^0x[0-9a-f]{8}$/.test(sel)) throw new Error("no such escrow method: " + name);
157
142
  return sel;
158
143
  }
159
144
 
160
- // Разбор ответа bytes32: одно слово.
161
- export function decodeBytes32(returnedHex, what = "значение") {
145
+ // Parse a bytes32 answer: one word.
146
+ export function decodeBytes32(returnedHex, what = "value") {
162
147
  const hex = String(returnedHex || "").replace(/^0x/, "");
163
- if (hex.length < 64) throw new Error("короткий ответ " + what + ": " + returnedHex);
148
+ if (hex.length < 64) throw new Error("short answer " + what + ": " + returnedHex);
164
149
  return "0x" + hex.slice(0, 64).toLowerCase();
165
150
  }
166
151
 
167
- // Разбор ответа t1(): одно слово, uint64 внутри uint256.
168
- export function decodeUint64(returnedHex, what = "значение") {
152
+ // Parse the t1() answer: one word, uint64 inside uint256.
153
+ export function decodeUint64(returnedHex, what = "value") {
169
154
  const hex = String(returnedHex || "").replace(/^0x/, "");
170
- if (hex.length < 64) throw new Error("короткий ответ " + what + ": " + returnedHex);
155
+ if (hex.length < 64) throw new Error("short answer " + what + ": " + returnedHex);
171
156
  const v = BigInt("0x" + hex.slice(0, 64));
172
- if (v > 18446744073709551615n) throw new Error("ответ " + what + " не влезает в uint64: " + v);
157
+ if (v > 18446744073709551615n) throw new Error("answer " + what + " does not fit in uint64: " + v);
173
158
  return Number(v);
174
159
  }
175
160
 
176
- // Разбор ответа status(): четыре слова по 32 байта, три флага и остаток.
161
+ // Parse the status() answer: four 32-byte words - three flags and the balance.
177
162
  export function decodeStatus(returnedHex) {
178
163
  const hex = String(returnedHex || "").replace(/^0x/, "");
179
- if (hex.length < 256) throw new Error("короткий ответ status(): " + returnedHex);
164
+ if (hex.length < 256) throw new Error("short status() answer: " + returnedHex);
180
165
  const word = (i) => hex.slice(i * 64, (i + 1) * 64);
181
166
  return {
182
167
  isFunded: BigInt("0x" + word(0)) !== 0n,
@@ -186,23 +171,22 @@ export function decodeStatus(returnedHex) {
186
171
  };
187
172
  }
188
173
 
189
- // Какие условия ордера нужны эскроу при создании - одним местом, чтобы форма и проверка не разошлись.
190
- // СРОК ОДИН. Поля t0 в условиях ордера больше нет: до t1 ETH забирает claimer по своей половине и
191
- // только при отметке ready, с t1 забор запрещён, а вернуть вправе кто угодно, ПРЕДЪЯВИВ ПОЛОВИНУ
192
- // ВНЁСШЕГО (NinseiEscrow.sol:250-253) - у сторожа её нет, поэтому "вернёт кто угодно" надо читать как
193
- // "любой, у кого есть половина" (решение 2026-09-16, док 26; то же уточнение - док 24 §1).
194
- // Проверка "t1 позже t0" потеряла смысл, но её место заняла не пустота: срок обязан быть В БУДУЩЕМ,
195
- // иначе ордер создаётся уже закрытым - claim по нему невозможен, и деньги зависли бы до возврата.
196
- // Верхних границ и требования "сроки в будущем" у КОНТРАКТА нет вовсе: конструктор проверяет только
197
- // amount != 0 и t1 > readyBy (:170-171), а сроки в прошлом для него допустимы. Их держит эта функция -
198
- // контракт защищает деньги, но не защищает от ордера с прошлыми сроками.
174
+ // Which order terms the escrow needs at creation - in one place, so the form and the checks do not diverge.
175
+ // ONE DEADLINE. There is no t0 field: before t1 ETH is claimed by the claimer via his half and only with the
176
+ // ready mark; from t1 a claim is forbidden and anyone may refund by PRESENTING THE DEPOSITOR'S HALF - the
177
+ // watchtower does not have it, so "anyone refunds" reads as "anyone holding the half".
178
+ // The "t1 later than t0" check lost its meaning, but emptiness did not take its place: the deadline must be IN
179
+ // THE FUTURE, else the order is created already closed - no claim is possible and the money hangs until refund.
180
+ // The CONTRACT has no upper bounds and no "future deadlines" rule: the constructor only checks amount != 0 and
181
+ // t1 > readyBy, and deadlines in the past are fine for it. This function holds those bounds - the contract
182
+ // protects the money, not the caller from an order with past deadlines.
199
183
  export function orderTerms({ locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, amount, readyBy, t1 }) {
200
184
  for (const [k, v] of Object.entries({ locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, amount })) {
201
- if (v === undefined || v === null || v === "") throw new Error("не задано условие ордера: " + k);
185
+ if (v === undefined || v === null || v === "") throw new Error("order term not set: " + k);
202
186
  }
203
- // Сроков два, и они НЕ должны совпадать: совпадение вернуло бы окно, где забор и возврат валидны
204
- // одновременно, и спор решал бы газ. Первый срок - окно отметки готовности, второй - граница расчёта.
205
- if (Number(readyBy) <= Math.floor(Date.now() / 1000)) throw new Error("readyBy должен быть в будущем: " + readyBy);
206
- if (Number(t1) <= Number(readyBy)) throw new Error("t1 должен быть позже readyBy");
187
+ // Two deadlines that must NOT coincide: coincidence would restore a window where claim and refund are both
188
+ // valid and gas would decide the dispute. The first is the readiness window, the second the settlement boundary.
189
+ if (Number(readyBy) <= Math.floor(Date.now() / 1000)) throw new Error("readyBy must be in the future: " + readyBy);
190
+ if (Number(t1) <= Number(readyBy)) throw new Error("t1 must be later than readyBy");
207
191
  return { locker, claimer, commitHalfLocker, commitHalfClaimer, edPointLocker, edPointClaimer, amount, readyBy, t1 };
208
192
  }