@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.
- package/CHANGELOG.md +35 -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 +554 -449
- 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 +5 -5
- 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/core/rfqSource.js
CHANGED
|
@@ -1,39 +1,28 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/rfqSource.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
|
+
// LIVE INDICATIVE QUOTES FROM OUR PROVIDER - THROUGH THE SDK CORE.
|
|
6
|
+
// WHY THROUGH THE CORE, NOT OUR OWN REQUEST. The read rules (zero = "no direction", staleness by the quote own
|
|
7
|
+
// stamp, a self-contradicting row) used to live here and, separately, in the core, and two editions of one rule
|
|
8
|
+
// diverge silently. So the book now comes from the CORE: watch - quotes.watch, snapshot - quotes.snapshot, and
|
|
9
|
+
// the shape is translated by the bridge. NO selection rule is left here: the core decides. Core silence or
|
|
10
|
+
// refusal gives an EMPTY book.
|
|
11
|
+
// THE SNAPSHOT SHAPE, in short: no STEPS (one RANGE, one price); one quote KIND - quoteKind is gone, the only
|
|
12
|
+
// firm quote is the order one; rate: 0 means "direction disabled" with a why, and zero is NEVER a price; the
|
|
13
|
+
// network is part of the market identity and is checked against ours; seq and at travel with the quote for
|
|
14
|
+
// freshness; the SIZE is in units of the ASSET (the first pair element).
|
|
7
15
|
//
|
|
8
|
-
// ПОЧЕМУ ЧЕРЕЗ ЯДРО, А НЕ СВОИМ ЗАПРОСОМ. Раньше этот модуль САМ ходил в /api/rate и САМ разбирал книгу:
|
|
9
|
-
// правила чтения (ноль = "направления нет", просрочка по СОБСТВЕННОЙ метке котировки, строка, противоречащая
|
|
10
|
-
// себе) жили здесь, а рядом - в ядре (sdk/src/quotes.mjs). Две редакции одних правил расходятся молча
|
|
11
|
-
// (док 41/54), поэтому книга теперь берётся у ЯДРА: опрос - quotes.watch, снимок - quotes.snapshot, а
|
|
12
|
-
// ПЕРЕВОД ФОРМЫ делает склейник (www/js/sdk/bridge.js). Здесь не осталось НИ ОДНОГО правила отбора: что
|
|
13
|
-
// предложение, что отказ, что лучший - решает ядро. Молчание или отказ ядра даёт ПУСТУЮ книгу: прежняя
|
|
14
|
-
// (движковая) книга на экран не подставляется.
|
|
15
16
|
//
|
|
16
|
-
// ЧТО ЗНАЧИТ ЭТА ФОРМА СНИМКА (полный текст формы - .hermes/docs/41-quote-protocol-v4.md; действующие
|
|
17
|
-
// правила приёма - .hermes/docs/54-quote-protocol-v5.md), если коротко:
|
|
18
|
-
// - СТУПЕНЕЙ НЕТ. min/max/step/ttlMs описывают ДИАПАЗОН целиком, а цена одна на весь диапазон: объём либо
|
|
19
|
-
// попадает в диапазон провайдера, либо он за него не берётся;
|
|
20
|
-
// - ВИД КОТИРОВКИ ОДИН. quoteKind (live/fixed) ушёл: твёрдая котировка ровно одна - ордерная;
|
|
21
|
-
// - rate: 0 ЗНАЧИТ "НАПРАВЛЕНИЕ ОТКЛЮЧЕНО" и рядом стоит why. Это ЕДИНСТВЕННЫЙ сигнал отсутствия
|
|
22
|
-
// предложения, поэтому ноль НИКОГДА не становится ценой и НИКОГДА не делится;
|
|
23
|
-
// - СЕТЬ - ЧАСТЬ ЛИЧНОСТИ РЫНКА: assetNetwork/currencyNetwork сверяются со своей сетью (quoteNetworksFit);
|
|
24
|
-
// - seq (номер запроса) и at (метка времени) едут вместе с котировкой: по ним читается свежесть;
|
|
25
|
-
// - ОБЪЁМ МЕРЯЕТСЯ В ЕДИНИЦАХ ASSET ПАРЫ (первого элемента): у XMR/<токен> это XMR, у <токен>/XMR - токен.
|
|
26
17
|
import { API, DEFAULT_CHAIN } from "./config.js";
|
|
27
18
|
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
31
|
-
// (док 21, D5.1; док 22, версия 4). Тело котировки бэкенд не хранит и не логирует.
|
|
19
|
+
// QUOTE FOR A SPECIFIC ORDER (orderQuote). The request goes THROUGH OUR BACKEND, not straight to the provider
|
|
20
|
+
// node: a direct request would require CORS headers, i.e. a node exposed to the internet. Our backend routes
|
|
21
|
+
// the request and also checks the provider signature against an allowlist of keys.
|
|
32
22
|
export async function requestOrderQuote({ providerId, order }) {
|
|
33
|
-
if (!providerId) throw new Error("
|
|
34
|
-
//
|
|
35
|
-
// 404
|
|
36
|
-
// запрос уходил и получал отказ: сбивало с толку и разбор, и поиск причины.
|
|
23
|
+
if (!providerId) throw new Error("no provider selected: there is no one to ask for an order quote");
|
|
24
|
+
// A PATH WITHOUT A SECOND "api": apiBase() already returns "/api", and a stray prefix gave /api/api/order-quote -
|
|
25
|
+
// a 404 on every request.
|
|
37
26
|
const res = await fetch(apiBase() + "/order-quote", {
|
|
38
27
|
method: "POST",
|
|
39
28
|
headers: { "content-type": "application/json" },
|
|
@@ -43,70 +32,65 @@ export async function requestOrderQuote({ providerId, order }) {
|
|
|
43
32
|
try { body = await res.json(); } catch { body = null; }
|
|
44
33
|
if (!res.ok || !body || body.ok !== true || !body.quote) {
|
|
45
34
|
const why = (body && (body.error || body.why)) || ("HTTP " + res.status);
|
|
46
|
-
throw new Error("
|
|
35
|
+
throw new Error("the provider gave no order quote: " + why);
|
|
47
36
|
}
|
|
48
|
-
//
|
|
49
|
-
//
|
|
50
|
-
// подтверждения нельзя. Отсутствие флага тоже считаем непроверенной: обещать больше, чем сделано, нельзя.
|
|
37
|
+
// THE BACKEND CHECKS THE SIGNATURE, BUT THE RESULT COMES AS A FIELD, NOT BY SILENCE. An unverified signature
|
|
38
|
+
// is not "probably fine": an order quote commits to keys. A missing flag also counts as unverified.
|
|
51
39
|
if (body.signatureVerified !== true) {
|
|
52
|
-
throw new Error("
|
|
40
|
+
throw new Error("provider signature not confirmed: " + String(body.signatureWhy || "the backend did not verify"));
|
|
53
41
|
}
|
|
54
42
|
return body.quote;
|
|
55
43
|
}
|
|
56
44
|
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
// которую мы отдадим в контракт. Второе без первого бессмысленно, первое без второго не защищает.
|
|
45
|
+
// ORDER-QUOTE CHECK - the reason it is requested at all. The verifier code is the same as our own side: a proof
|
|
46
|
+
// in the order context, and the proven ed25519 point must match the one we will send to the contract.
|
|
60
47
|
export function checkOrderQuoteShape(quote) {
|
|
61
|
-
//
|
|
48
|
+
// THE VIEW HALF IS MANDATORY: without it the Monero address cannot be built from the quote.
|
|
62
49
|
const missing = ["providerId", "amount", "claimer", "edPointClaimer", "commitHalfClaimer", "viewHalfClaimer", "proof", "context", "expiresAt"]
|
|
63
50
|
.filter((f) => quote[f] === undefined || quote[f] === null || quote[f] === "");
|
|
64
|
-
if (missing.length) throw new Error("
|
|
65
|
-
//
|
|
66
|
-
|
|
67
|
-
if (quote.
|
|
68
|
-
if (Number(quote.expiresAt) <= Date.now()) throw new Error("котировка под ордер просрочена");
|
|
51
|
+
if (missing.length) throw new Error("the order quote is missing fields: " + missing.join(", "));
|
|
52
|
+
// THE ONLY FIRM KIND IS THE ORDER ONE: a broadcast quote does not fit here.
|
|
53
|
+
if (quote.quoteKind !== "orderQuote") throw new Error("not an order quote, but " + String(quote.quoteKind));
|
|
54
|
+
if (Number(quote.expiresAt) <= Date.now()) throw new Error("the order quote has expired");
|
|
69
55
|
}
|
|
70
56
|
|
|
71
57
|
let baseOverride = null;
|
|
72
58
|
const apiBase = () => baseOverride || API.base;
|
|
73
59
|
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
// размер приходит от экрана, который выровнял его по сетке провайдера.
|
|
60
|
+
// WHAT WE ASK THE CORE. The side and SIZE are in units of the ASSET; the network and token let the core pick ITS
|
|
61
|
+
// instance. No numbers appear here: the size comes from the screen, aligned to the provider grid.
|
|
77
62
|
let query = { size: 1, side: "buy", chain: DEFAULT_CHAIN, token: null };
|
|
78
63
|
export function setRfqQuery(next) {
|
|
79
64
|
if (!next) return;
|
|
80
65
|
if (next.side === "buy" || next.side === "sell") query.side = next.side;
|
|
81
|
-
//
|
|
66
|
+
// NO QUOTE KIND IN THE REQUEST anymore: the second kind does not exist.
|
|
82
67
|
if (next.size !== null && next.size !== undefined && Number(next.size) > 0) query.size = Number(next.size);
|
|
83
|
-
else query.size = 1; //
|
|
68
|
+
else query.size = 1; // no amount entered - ask for one ASSET unit, not "nothing"
|
|
84
69
|
if (typeof next.chain === "string" && next.chain) query.chain = next.chain;
|
|
85
70
|
if (typeof next.token === "string" && next.token) query.token = next.token;
|
|
86
71
|
}
|
|
87
72
|
|
|
88
|
-
//
|
|
89
|
-
//
|
|
73
|
+
// The current request: the screen needs to know the side and size asked for, else it cannot tell "the provider is
|
|
74
|
+
// silent" from "the provider does not take this size".
|
|
90
75
|
export function rfqQuery() {
|
|
91
76
|
return { ...query };
|
|
92
77
|
}
|
|
93
78
|
|
|
94
|
-
//
|
|
95
|
-
// (
|
|
96
|
-
//
|
|
79
|
+
// THE SNAPSHOT THE SCREENS READ: { ok, error, data, at }. In data - references and best, while offers and
|
|
80
|
+
// refusals come from rfqOffers(). If the core was never polled or refused, there is no data, shown by ok.
|
|
81
|
+
// Old numbers are not pasted as "live".
|
|
97
82
|
const snapshot = { ok: false, error: null, data: null, at: 0, fetching: false };
|
|
98
83
|
export function rfqSnapshot() {
|
|
99
84
|
return snapshot;
|
|
100
85
|
}
|
|
101
86
|
|
|
102
|
-
//
|
|
87
|
+
// The pair the user SEES: buying XMR means the pairs XMR/<asset>; selling means the reverse.
|
|
103
88
|
export function pairFacingUser(side = query.side, asset = "ETH") {
|
|
104
89
|
return side === "sell" ? asset.toUpperCase() + "/XMR" : "XMR/" + asset.toUpperCase();
|
|
105
90
|
}
|
|
106
91
|
|
|
107
|
-
//
|
|
108
|
-
// CURRENCY (
|
|
109
|
-
// "XMR/ETH" значит "сколько ETH за 1 XMR" и ничего другого.
|
|
92
|
+
// PAIR SIDES ARE READ BY POSITION: the first element is the ASSET (the size is measured in it), the second is
|
|
93
|
+
// the CURRENCY (what is paid). Separate functions, because this is a convention.
|
|
110
94
|
export function pairAsset(pair) {
|
|
111
95
|
return String(pair || "").split("/")[0].toUpperCase();
|
|
112
96
|
}
|
|
@@ -114,10 +98,9 @@ export function pairCurrency(pair) {
|
|
|
114
98
|
return String(pair || "").split("/")[1] ? String(pair).split("/")[1].toUpperCase() : "";
|
|
115
99
|
}
|
|
116
100
|
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
120
|
-
// снимок - пустая книга; прежняя книга на экране не остаётся.
|
|
101
|
+
// OFFERS AND REFUSALS - FROM THE CORE SNAPSHOT, NO SECOND SELECTION. The core already split the rows into
|
|
102
|
+
// offers and refusals and named the best. Here they are only transferred: no own filtering, sorting or
|
|
103
|
+
// thresholds. An empty snapshot is an empty book.
|
|
121
104
|
export function rfqOffers() {
|
|
122
105
|
const data = snapshot.ok && snapshot.data ? snapshot.data : null;
|
|
123
106
|
if (!data) return { offers: [], refused: [], seq: null, at: null };
|
|
@@ -129,10 +112,10 @@ export function rfqOffers() {
|
|
|
129
112
|
};
|
|
130
113
|
}
|
|
131
114
|
|
|
132
|
-
//
|
|
133
|
-
// arbitrum-sepolia
|
|
134
|
-
//
|
|
135
|
-
//
|
|
115
|
+
// CHECKING THE QUOTE NETWORK AGAINST OURS. The network is part of the market identity: arbitrum and
|
|
116
|
+
// arbitrum-sepolia are ONE WORD FOR DIFFERENT MONEY, and a wallet does not move coins between them. The side
|
|
117
|
+
// holding XMR must speak Monero, the other the EVM registry language (its id). We return a verdict, not a
|
|
118
|
+
// throw: one quote refusal does not close the market.
|
|
136
119
|
export function quoteNetworksFit(quote, { monero, evm }) {
|
|
137
120
|
const xmrOnAsset = quote.asset === "XMR";
|
|
138
121
|
const xmrOnCurrency = quote.currency === "XMR";
|
|
@@ -147,15 +130,14 @@ export function quoteNetworksFit(quote, { monero, evm }) {
|
|
|
147
130
|
if (evmNet && evmNet !== evm) {
|
|
148
131
|
return { ok: false, stated: true, why: quote.pair + ": " + evmSide + " is " + evmNet + ", but this page settles on " + evm };
|
|
149
132
|
}
|
|
150
|
-
//
|
|
151
|
-
//
|
|
133
|
+
// NOT NAMED - DOES NOT MEAN "OURS". There is nothing to compare, and that is logged, but we do not close the
|
|
134
|
+
// market for it: the rule catches a network NAMED WRONG, not a missing field.
|
|
152
135
|
return { ok: true, stated: quote.networkStated, moneroNet: moneroNet || null, evmNet: evmNet || null, why: null };
|
|
153
136
|
}
|
|
154
137
|
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
// страничные модули. Отказ загрузки ядра - ЭТО ОТКАЗ, а не повод взять книгу где-то ещё.
|
|
138
|
+
// POLLING GOES THROUGH THE CORE. The bridge hands the core what it needs (network, size, direction) and returns
|
|
139
|
+
// the TRANSLATED snapshot. The core is loaded DYNAMICALLY: this module is also read without a browser, and then
|
|
140
|
+
// the book is simply not requested. A load failure IS a failure, not a reason to take the book elsewhere.
|
|
159
141
|
export async function refreshRfq() {
|
|
160
142
|
if (snapshot.fetching) return snapshot;
|
|
161
143
|
snapshot.fetching = true;
|
|
@@ -168,8 +150,8 @@ export async function refreshRfq() {
|
|
|
168
150
|
snapshot.data = next.data;
|
|
169
151
|
snapshot.at = next.at || Date.now();
|
|
170
152
|
} catch (error) {
|
|
171
|
-
//
|
|
172
|
-
//
|
|
153
|
+
// A REFUSAL IS NOT REPLACED BY OLD NUMBERS: showing a stale price as live is worse than saying there is no
|
|
154
|
+
// connection. No data is left at all.
|
|
173
155
|
snapshot.ok = false;
|
|
174
156
|
snapshot.error = String((error && error.message) || error);
|
|
175
157
|
snapshot.data = null;
|
|
@@ -182,9 +164,9 @@ export async function refreshRfq() {
|
|
|
182
164
|
|
|
183
165
|
let timer = null;
|
|
184
166
|
|
|
185
|
-
//
|
|
186
|
-
//
|
|
187
|
-
//
|
|
167
|
+
// ALWAYS POLL. The poll is not to "catch a new price": it confirms the provider is there. The core holds the
|
|
168
|
+
// poll for the subscription; this tick keeps the book updating with the screen. Node silence shows as a missing
|
|
169
|
+
// fresh stamp, not as "the price is the same".
|
|
188
170
|
export function startRfqPolling(chain = null, ms = API.pollMs) {
|
|
189
171
|
if (typeof chain === "string" && chain) query.chain = chain;
|
|
190
172
|
if (timer) return;
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/sellWindow.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
|
+
// XMR SELL WINDOW: ONE RULE FOR TWO SIDES, NOT TWO NUMBERS.
|
|
7
6
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// *
|
|
11
|
-
//
|
|
12
|
-
// 10
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
7
|
+
// WHY A SEPARATE MODULE. The XMR sell window (a person gives XMR, receives the network native coin) has two
|
|
8
|
+
// numbers, and they are of different nature:
|
|
9
|
+
// * HARD - the readiness-mark deadline FROM THE CONTRACT. Past it nobody accepts the mark, and it goes into
|
|
10
|
+
// the order BINDING (termsHash), so it does not change after the ticket is issued. The contract ALWAYS
|
|
11
|
+
// gets the maximum: 10 Monero confirmations is about 1200 s, and it cannot be less than the contract
|
|
12
|
+
// window, else there would be nothing to mark readiness with (the contract forbids the mark after readyBy,
|
|
13
|
+
// and the XMR would be locked for both).
|
|
14
|
+
// * SOFT - THE ON-SCREEN PROMISE: how long the provider holds the price while the person sends XMR.
|
|
15
|
+
// The owner's and partner's "ten minutes".
|
|
16
|
+
// THE EXTENSION RULE IS ONE. While the pool does NOT show the FULL expected amount, the promise is extended to
|
|
17
|
+
// the contract maximum: the person may have sent, and needs the first block. Once the full amount is visible,
|
|
19
18
|
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// rfq -> www
|
|
19
|
+
// WHY A FUNCTION, NOT TWO NUMBERS IN TWO PLACES. This rule is decided by the NODE (rfq/reverseArrival.mjs -
|
|
20
|
+
// the mark, the alarm, the early refund) and shown by the PAGE (www/js/core/swap.js - the on-screen countdown).
|
|
21
|
+
// A second edition would diverge from the first silently: the person would see one window while the node lived by
|
|
22
|
+
// another. The rfq -> www import direction is already accepted (precedent: rfq/reverseGas.mjs imports ../www/js/evm/claimGas.js).
|
|
24
23
|
export const SELL_WINDOW = {
|
|
25
|
-
softSec: 600, // "
|
|
26
|
-
hardSec: 1200, //
|
|
24
|
+
softSec: 600, // "ten minutes": the provider's on-screen promise
|
|
25
|
+
hardSec: 1200, // contract window: 10 Monero confirmations, the maximum that goes into termsHash
|
|
27
26
|
};
|
|
28
27
|
|
|
29
28
|
const positiveSec = (v, dflt) => {
|
|
@@ -31,10 +30,10 @@ const positiveSec = (v, dflt) => {
|
|
|
31
30
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : dflt;
|
|
32
31
|
};
|
|
33
32
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
37
|
-
//
|
|
33
|
+
// HOW MANY SECONDS THE SELL WINDOW LASTS given the named pool state. poolSeen - the FULL expected amount is
|
|
34
|
+
// visible in the pool, and only it (not "something is visible" and not an underpayment: the swap would hang on
|
|
35
|
+
// someone else's penny - see poolCoverage in rfq/reverseArrival.mjs). The contract window is a CEILING: the soft
|
|
36
|
+
// promise cannot be longer than the contract window, else we would promise what the chain will not give.
|
|
38
37
|
export function sellWindowSec({ poolSeen, soft = SELL_WINDOW.softSec, hard = SELL_WINDOW.hardSec } = {}) {
|
|
39
38
|
const s = positiveSec(soft, SELL_WINDOW.softSec);
|
|
40
39
|
const h = positiveSec(hard, SELL_WINDOW.hardSec);
|
package/engine/core/store.js
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/store.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
|
+
// Demo state storage.
|
|
7
6
|
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
7
|
+
// Why it exists at all: litepaper §5.1.7 - "the tab can be closed, the browser will restore the state". The swap
|
|
8
|
+
// state is derived from saved timestamps and events, so the tab really can be closed.
|
|
10
9
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
10
|
+
// STORAGE COMES FROM OUTSIDE. Before, `localStorage` stood right in the code, and that made the module unusable
|
|
11
|
+
// both in Node (the repository checks) and in the SDK (the package promises "no DOM"). Now storage is an adapter
|
|
12
|
+
// with three actions (get/set/remove), and by default localStorage is taken IF it exists in the environment,
|
|
13
|
+
// otherwise process memory: silently not saving is not allowed - the swap would be lost after a reload.
|
|
15
14
|
|
|
16
15
|
import { APP, DEMO } from "./config.js";
|
|
17
16
|
|
|
18
17
|
const EMPTY = {
|
|
19
18
|
version: 1,
|
|
20
|
-
// payToken -
|
|
21
|
-
//
|
|
19
|
+
// payToken - the chosen payment asset. Stored next to the network: otherwise the form on reload returned
|
|
20
|
+
// the native token and the user's choice was lost.
|
|
22
21
|
settings: { speed: DEMO.defaultSpeed, scenario: DEMO.defaultScenario, chainMode: DEMO.defaultChainMode, chain: DEMO.defaultChain, payToken: "" },
|
|
23
22
|
wallet: { connected: false, address: null, balances: {} },
|
|
24
23
|
swaps: {},
|
|
25
24
|
activeSwapId: null,
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
//
|
|
29
|
-
// try/catch
|
|
30
|
-
//
|
|
27
|
+
// DEFAULT ADAPTER: web storage, if present, otherwise memory. The environment is probed via typeof, not
|
|
28
|
+
// try/catch around an access: accessing a non-existent name in Node is a ReferenceError, and catching it
|
|
29
|
+
// as "no storage" would hide typos.
|
|
31
30
|
export function memoryStorage(map = new Map()) {
|
|
32
31
|
return {
|
|
33
32
|
get: (key) => (map.has(key) ? map.get(key) : null),
|
|
@@ -48,15 +47,15 @@ const webStorage = (name) => {
|
|
|
48
47
|
|
|
49
48
|
let adapter = webStorage("localStorage") || memoryStorage();
|
|
50
49
|
|
|
51
|
-
/**
|
|
50
|
+
/** Swap the storage. The shape is checked, not the origin: localStorage, memory and the SDK all qualify. */
|
|
52
51
|
export function setStorage(next) {
|
|
53
52
|
const ok = next && typeof next.get === "function" && typeof next.set === "function" && typeof next.remove === "function";
|
|
54
|
-
if (!ok) throw new Error("
|
|
53
|
+
if (!ok) throw new Error("storage must support get/set/remove");
|
|
55
54
|
adapter = next;
|
|
56
55
|
return adapter;
|
|
57
56
|
}
|
|
58
57
|
|
|
59
|
-
/**
|
|
58
|
+
/** What is used right now: this is visible from outside, and by this the checks tell memory from a browser. */
|
|
60
59
|
export const storage = () => adapter;
|
|
61
60
|
|
|
62
61
|
export function loadState() {
|
|
@@ -70,10 +69,10 @@ export function loadState() {
|
|
|
70
69
|
}
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
// BigInt
|
|
74
|
-
// ("Do not know how to serialize a BigInt", bigintSafe),
|
|
75
|
-
//
|
|
76
|
-
//
|
|
72
|
+
// BigInt IS NOT SERIALISED IN JSON - and the key halves are BigInt. JSON.stringify FAILS on them
|
|
73
|
+
// ("Do not know how to serialize a BigInt", bigintSafe), and the failure looks silent: the swap record is
|
|
74
|
+
// simply not saved, the recovery file is not issued, and the screen shows an address that is not on chain.
|
|
75
|
+
// We turn BigInt into a hex string: the same meaning, but serialisable, and the secret is not lost.
|
|
77
76
|
const bigintSafe = (key, value) => (typeof value === "bigint" ? "0x" + value.toString(16) : value);
|
|
78
77
|
export function saveState(state) {
|
|
79
78
|
try {
|