@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.
- package/CHANGELOG.md +55 -0
- package/engine/atomic/dleq2.js +86 -111
- package/engine/atomic/halfenc.js +58 -72
- package/engine/atomic/halves.js +31 -53
- package/engine/atomic/order-client.js +15 -16
- package/engine/atomic/order.js +44 -62
- package/engine/atomic/orderContext.js +26 -50
- package/engine/atomic/orderHalfCommit.js +17 -18
- package/engine/core/chainSource.js +54 -69
- package/engine/core/clock.js +11 -12
- package/engine/core/config.js +561 -451
- package/engine/core/finality.js +34 -53
- package/engine/core/format.js +35 -36
- package/engine/core/preSignGuards.js +109 -117
- package/engine/core/rfqSource.js +57 -75
- package/engine/core/sellWindow.js +23 -24
- package/engine/core/store.js +20 -21
- package/engine/core/swap.js +242 -243
- package/engine/evm/amounts.js +50 -51
- package/engine/evm/asyncExec.js +577 -0
- package/engine/evm/asyncLegs.js +308 -0
- package/engine/evm/asyncRoute.js +179 -0
- package/engine/evm/auth.js +41 -48
- package/engine/evm/claimGas.js +28 -47
- package/engine/evm/depositor.js +39 -43
- package/engine/evm/dex.js +266 -267
- package/engine/evm/escrow.js +113 -129
- package/engine/evm/factory.js +71 -77
- package/engine/evm/fees.js +41 -42
- package/engine/evm/funding.js +160 -162
- package/engine/evm/gasReserve.js +67 -36
- package/engine/evm/index.js +46 -53
- package/engine/evm/injected.js +34 -42
- package/engine/evm/permit.js +129 -56
- package/engine/evm/prices.js +30 -45
- package/engine/evm/session.js +188 -189
- package/engine/mock/chain.js +17 -18
- package/engine/monero/address.js +33 -48
- package/engine/monero/halvesSweep.js +25 -43
- package/engine/monero/node.js +14 -15
- package/engine/monero/scan.js +82 -95
- package/engine/monero/swapKeys.js +30 -33
- package/engine/monero/unlock.js +17 -20
- package/engine/monero/wallet.js +32 -42
- package/engine/recovery/recoveryFile.js +154 -161
- package/engine/recovery/restore.js +50 -61
- package/engine/sdk/bridge.js +244 -234
- package/engine/ui/dom.js +4 -5
- package/engine/ui/walletModal.js +20 -31
- package/index.d.ts +8 -0
- package/package.json +1 -1
- package/src/adapters/evm-wallet.mjs +16 -0
- package/src/adapters/monero-wallet.mjs +1 -1
- package/src/adapters/monero.mjs +1 -1
- package/src/adapters/node-test-evm-wallet.mjs +20 -8
- package/src/config.mjs +1 -1
- package/src/engine.mjs +4 -0
- package/src/legs/cow-spec.mjs +254 -0
- package/src/legs/cow.mjs +315 -0
- package/src/legs/evm.mjs +86 -2
- package/src/legs/kyber-spec.mjs +265 -0
- package/src/legs/kyber.mjs +425 -0
- package/src/legs/shape.mjs +35 -0
- package/src/legs/uniswaprelay-spec.mjs +541 -0
- package/src/legs/uniswaprelay.mjs +422 -0
- package/src/legs/uniswapx-spec.mjs +329 -0
- package/src/legs/uniswapx.mjs +362 -0
- package/src/quotes.mjs +1 -1
- package/src/swaps.mjs +1 -1
package/engine/atomic/order.js
CHANGED
|
@@ -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
|
|
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
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
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
|
-
//
|
|
18
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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: "
|
|
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: "
|
|
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
|
-
//
|
|
96
|
-
// `
|
|
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
|
-
//
|
|
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
|
-
//
|
|
116
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
3
|
-
// tools/check-sdk-engine.mjs reddens on any drift, so a stale copy cannot ship silently.
|
|
2
|
+
// Edit the source under www/js and regenerate the mirror; a drift guard reddens on any difference.
|
|
4
3
|
// Everything below this header is identical to the source.
|
|
5
4
|
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
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
|
-
//
|
|
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", //
|
|
58
|
-
"claimer", //
|
|
59
|
-
"amount", //
|
|
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("
|
|
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
|
|
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
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// `keccak256(be32(half))`
|
|
12
|
-
//
|
|
13
|
-
// Monero)
|
|
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
|
-
//
|
|
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
|
-
//
|
|
20
|
-
// `secp256k1`
|
|
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("
|
|
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
|
|
27
|
-
const xy = raw.slice(1); // 64
|
|
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
|
}
|