@ninsei-labs/sdk 0.39.2 → 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 +63 -0
- package/README.md +4 -1
- 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 +26 -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/codes.mjs +5 -0
- package/src/config.mjs +1 -1
- package/src/engine.mjs +4 -0
- package/src/index.mjs +16 -2
- 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 +23 -5
- package/src/swaps.mjs +18 -1
package/engine/core/finality.js
CHANGED
|
@@ -1,93 +1,74 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/finality.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
|
-
// base 2.000 с/блок safe ≈0.4 мин finalized ≈25.1 мин
|
|
14
|
-
// bsc 0.450 с/блок safe и finalized отстают на 0-1 блок
|
|
15
|
-
// monad 0.302 с/блок то же
|
|
16
|
-
// hyperevm 0.984 с/блок то же
|
|
17
|
-
// Отсюда два вывода, на которых стоит таблица:
|
|
18
|
-
// 1) одно значение на все сети неверно: на base safe стоит 25 секунд, на arbitrum - 13 минут;
|
|
19
|
-
// 2) на bsc, monad и hyperevm теги ФОРМАЛЬНЫ: они отвечают, но отставания нет, то есть "читать по
|
|
20
|
-
// finalized" там выглядит защитой, а защиты не даёт. Пустышку опаснее отсутствия тега: отсутствие
|
|
21
|
-
// видно, а пустышка молчит. Поэтому там - числовая глубина с НАЗВАННЫМ риском.
|
|
22
|
-
//
|
|
23
|
-
// ЭТА КОПИЯ - ДЛЯ СТРАНИЦЫ: она обслуживает все сети сразу, поэтому таблица нужна ей целиком.
|
|
24
|
-
// Такая же таблица живёт у служб (rfq/finality.mjs); совпадение копий проверяет страж tools/check-finality.mjs,
|
|
25
|
-
// потому что общего модуля у страницы и у служб быть не может.
|
|
26
|
-
//
|
|
27
|
-
// ОБЛАСТЬ ПРИМЕНЕНИЯ. Таблица отвечает на один вопрос: когда состояние EVM-стороны считается достаточно
|
|
28
|
-
// зрелым, чтобы принять по нему НЕОБРАТИМОЕ решение - отправить XMR. Показ состояния на странице живёт по
|
|
29
|
-
// latest и этой таблицей не ограничен.
|
|
5
|
+
// STATE MATURITY PER NETWORK: at which level the chain treats a record as final.
|
|
6
|
+
// One value for all networks is wrong: on base `safe` is ~25 s, on arbitrum ~13 min (measured on live nodes).
|
|
7
|
+
// On bsc, monad and hyperevm the tags are FORMAL: they answer but lag 0-1 block, so reading by `finalized`
|
|
8
|
+
// looks like protection and gives none. A named numeric depth is used there instead.
|
|
9
|
+
// A twin table lives in the node; the two copies are pinned by a check.
|
|
10
|
+
// SCOPE: the table answers one question - when the EVM side is mature enough for an IRREVERSIBLE decision
|
|
11
|
+
// (send XMR). Displaying state on screen uses `latest` and is not limited by this.
|
|
30
12
|
export const FINALITY = {
|
|
31
|
-
//
|
|
32
|
-
"arbitrum-sepolia": { mode: "tag", tag: "safe", costSec: 520, reason: "
|
|
33
|
-
"arbitrum": { mode: "tag", tag: "safe", costSec: 760, reason: "
|
|
34
|
-
"mainnet": { mode: "tag", tag: "safe", costSec: 710, reason: "
|
|
35
|
-
"base": { mode: "tag", tag: "safe", costSec: 25, reason: "safe
|
|
13
|
+
// A MEANINGFUL TAG: the lag is measured and named.
|
|
14
|
+
"arbitrum-sepolia": { mode: "tag", tag: "safe", costSec: 520, reason: "tag is meaningful: lag ~8.6 min (measured)" },
|
|
15
|
+
"arbitrum": { mode: "tag", tag: "safe", costSec: 760, reason: "tag is meaningful: lag ~12.6 min (measured)" },
|
|
16
|
+
"mainnet": { mode: "tag", tag: "safe", costSec: 710, reason: "tag is meaningful: lag ~11.8 min (measured)" },
|
|
17
|
+
"base": { mode: "tag", tag: "safe", costSec: 25, reason: "safe is both meaningful and fast: ~25 s (measured)" },
|
|
36
18
|
|
|
37
|
-
//
|
|
38
|
-
"bsc": { mode: "depth", depth: 15, costSec: 7, reason: "
|
|
39
|
-
"monad": { mode: "depth", depth: 12, costSec: 4, reason: "
|
|
40
|
-
"hyperevm": { mode: "depth", depth: 12, costSec: 12, reason: "
|
|
19
|
+
// A FORMAL TAG: it answers, but there is no lag. A numeric depth with a named risk cost.
|
|
20
|
+
"bsc": { mode: "depth", depth: 15, costSec: 7, reason: "tags are formal (lag 0-1 block, measured): the depth is CHOSEN, not measured - risk named" },
|
|
21
|
+
"monad": { mode: "depth", depth: 12, costSec: 4, reason: "tags are formal (lag 0-1 block, measured): depth chosen" },
|
|
22
|
+
"hyperevm": { mode: "depth", depth: 12, costSec: 12, reason: "tags are formal (lag 0-1 block, measured): depth chosen" },
|
|
41
23
|
};
|
|
42
24
|
|
|
43
|
-
//
|
|
44
|
-
//
|
|
25
|
+
// NETWORK NOT DESCRIBED - WE SAY SO, NOT PASTE SILENTLY. Depth 12 is the old common value and is more
|
|
26
|
+
// honest than a blank: a decision is made, but the risk cost is named as unmeasured.
|
|
45
27
|
export const DEFAULT_FINALITY = {
|
|
46
28
|
mode: "depth", depth: 12, costSec: 0,
|
|
47
|
-
reason: "
|
|
29
|
+
reason: "network not described in the table: using the common depth, risk not measured",
|
|
48
30
|
};
|
|
49
31
|
|
|
50
32
|
export function finalityFor(network) {
|
|
51
33
|
return FINALITY[network] || DEFAULT_FINALITY;
|
|
52
34
|
}
|
|
53
35
|
|
|
54
|
-
//
|
|
55
|
-
// (readyBy, t1)
|
|
36
|
+
// How long this network makes us wait before an irreversible decision, in seconds. Whoever sets the swap
|
|
37
|
+
// windows (readyBy, t1) must add it: a window shorter than the wait kills the swap.
|
|
56
38
|
export function finalityWaitSec(network) {
|
|
57
39
|
return Number(finalityFor(network).costSec || 0);
|
|
58
40
|
}
|
|
59
41
|
|
|
60
|
-
//
|
|
42
|
+
// WHAT TO TELL THE PERSON. A screen line: the level and the wait cost in words.
|
|
61
43
|
export function finalityNote(network) {
|
|
62
44
|
const f = finalityFor(network);
|
|
63
45
|
if (f.mode === "tag") {
|
|
64
46
|
const min = Math.max(1, Math.round(f.costSec / 60));
|
|
65
|
-
return "
|
|
47
|
+
return "waiting for level " + f.tag + ": ~" + min + " min";
|
|
66
48
|
}
|
|
67
|
-
return "
|
|
49
|
+
return "waiting for " + f.depth + " confirmations: ~" + Math.max(1, Math.round(f.costSec)) + " s";
|
|
68
50
|
}
|
|
69
51
|
|
|
70
|
-
//
|
|
71
|
-
//
|
|
52
|
+
// PROVIDER BADGE FOR THE LIST. Short: the person chooses whom to pick, and the wait cost is part of it.
|
|
53
|
+
// Our own measurement beats the provider's words: if its level is a tag, we take the measured cost from the table.
|
|
72
54
|
export function finalityBadge(policy) {
|
|
73
55
|
if (!policy) return "finality not stated";
|
|
74
56
|
if (policy.mode === "tag") {
|
|
75
57
|
const known = Object.values(FINALITY).find((f) => f.mode === "tag" && f.tag === policy.tag);
|
|
76
58
|
const cost = Number(policy.costSec || (known && known.costSec) || 0);
|
|
77
|
-
return cost >= 60 ? policy.tag + " ·
|
|
59
|
+
return cost >= 60 ? policy.tag + " · ~" + Math.round(cost / 60) + " min" : policy.tag + " · ~" + Math.round(cost) + " s";
|
|
78
60
|
}
|
|
79
61
|
const d = Number(policy.depth || 0);
|
|
80
62
|
const cost = Number(policy.costSec || d * (FINALITY[Object.keys(FINALITY)[0]] ? 0.3 : 0.3) || 0);
|
|
81
|
-
return d + "
|
|
63
|
+
return d + " blocks · ~" + Math.max(1, Math.round(cost)) + " s";
|
|
82
64
|
}
|
|
83
65
|
|
|
84
|
-
//
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// позже ready. При продаже сроки назначает нода (rfq/reverseOrder.mjs), там окно 1 час.
|
|
66
|
+
// ORDER WINDOWS BY PROVIDER LEVEL. A window shorter than the wait kills the swap: the readiness mark cannot
|
|
67
|
+
// reach the contract in time and the swap falls apart, so the windows are derived from the wait cost.
|
|
68
|
+
// The numbers are the owner's decision: on a BUY the readiness window is 4 hours and the claim window AFTER
|
|
69
|
+
// readyBy is 1 hour - the formula below keeps exactly that: claim one hour after ready. On a sell the node sets the deadlines.
|
|
89
70
|
export function orderWindows(policy, base = { readyWindowSeconds: 14400, claimWindowSeconds: 3600 }) {
|
|
90
71
|
const wait = Number((policy && policy.costSec) || 0);
|
|
91
|
-
const ready = Math.max(base.readyWindowSeconds, wait + 900); //
|
|
72
|
+
const ready = Math.max(base.readyWindowSeconds, wait + 900); // margin for the deposit and tx travel
|
|
92
73
|
return { readyWindowSeconds: ready, claimWindowSeconds: Math.max(base.claimWindowSeconds, ready + 3600) };
|
|
93
74
|
}
|
package/engine/core/format.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/format.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
|
+
// Formatting of numbers, time and addresses.
|
|
7
6
|
|
|
8
7
|
const nf = (min, max) => new Intl.NumberFormat("en-US", { minimumFractionDigits: min, maximumFractionDigits: max });
|
|
9
8
|
|
|
@@ -11,10 +10,10 @@ export function amount(v, dp = 2) {
|
|
|
11
10
|
const n = Number(v);
|
|
12
11
|
if (!isFinite(n)) return "-";
|
|
13
12
|
const max = Math.min(dp, 6);
|
|
14
|
-
//
|
|
15
|
-
// 0.000000 -
|
|
16
|
-
//
|
|
17
|
-
//
|
|
13
|
+
// DUST DOES NOT VANISH. Before, printing was hard-wired to "six digits", and an amount below 0.000001 showed as
|
|
14
|
+
// 0.000000 - a non-zero value looked like zero. For money this is the worst kind of display: the person
|
|
15
|
+
// sees zero where they actually have something. Extra digits are added only when the print zeroed a
|
|
16
|
+
// non-zero value, and never more than 18 (the wei precision limit).
|
|
18
17
|
if (n !== 0 && Math.abs(n) < Math.pow(10, -max)) {
|
|
19
18
|
const need = Math.min(18, Math.max(max + 1, -Math.floor(Math.log10(Math.abs(n)))));
|
|
20
19
|
return nf(need, need).format(n).replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
|
|
@@ -49,7 +48,7 @@ export function shortHash(h, head = 6, tail = 4) {
|
|
|
49
48
|
return s.slice(0, head) + "..." + s.slice(-tail);
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
// sim
|
|
51
|
+
// sim-time -> the demo's wall clock (UTC, as in the litepaper mockups)
|
|
53
52
|
export function clockTime(ms) {
|
|
54
53
|
const d = new Date(ms);
|
|
55
54
|
const hh = String(d.getUTCHours()).padStart(2, "0");
|
|
@@ -62,7 +61,7 @@ export function clockTimeShort(ms) {
|
|
|
62
61
|
return clockTime(ms).slice(0, 5);
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
//
|
|
64
|
+
// Duration in seconds -> "12m 30s"
|
|
66
65
|
export function duration(ms) {
|
|
67
66
|
const total = Math.max(0, Math.round(ms / 1000));
|
|
68
67
|
const m = Math.floor(total / 60);
|
|
@@ -92,49 +91,49 @@ export function randomHex(bytes = 16) {
|
|
|
92
91
|
return toHex(a);
|
|
93
92
|
}
|
|
94
93
|
|
|
95
|
-
//
|
|
94
|
+
// Bytes (Uint8Array/Buffer) -> hex string without the 0x prefix.
|
|
96
95
|
export function toHex(bytes) {
|
|
97
96
|
const a = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes || []);
|
|
98
97
|
return Array.from(a, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
99
98
|
}
|
|
100
99
|
|
|
101
|
-
//
|
|
100
|
+
// PARSING AN AMOUNT ENTERED BY A PERSON.
|
|
102
101
|
//
|
|
103
|
-
//
|
|
104
|
-
// `e.target.value.replace(/[^0-9.]/g, "")`:
|
|
105
|
-
// ("0,05" -
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
102
|
+
// WHY A SEPARATE FUNCTION, NOT A replace RIGHT IN THE FIELD. The amount fields had
|
|
103
|
+
// `e.target.value.replace(/[^0-9.]/g, "")`: everything but digits and a dot was ERASED. The European comma
|
|
104
|
+
// ("0,05" - the way half the world writes) turned into "005", that is 5 - an error by A HUNDREDFOLD, and spotting
|
|
105
|
+
// it in the interface is nearly impossible: the field shows "005", the quote is computed for five, the person pays
|
|
106
|
+
// a hundred times more. A comma without a dot is a decimal separator; a comma when a dot is present is a
|
|
107
|
+
// thousands separator; the Monero wallets do the same.
|
|
109
108
|
//
|
|
110
|
-
//
|
|
111
|
-
//
|
|
109
|
+
// Returns a CANONICAL string (digits and at most one dot). It is exactly what is shown in the field and exactly
|
|
110
|
+
// what the rest of the code parses - so "what is written" and "what is computed" cannot diverge.
|
|
112
111
|
export function normalizeAmountInput(raw) {
|
|
113
112
|
let s = String(raw == null ? "" : raw).trim().replace(/[^0-9.,]/g, "");
|
|
114
113
|
if (s.includes(".")) {
|
|
115
|
-
//
|
|
114
|
+
// A dot is present - commas separate thousands ("1,234.5" -> "1234.5").
|
|
116
115
|
s = s.replace(/,/g, "");
|
|
117
116
|
} else if (s.includes(",")) {
|
|
118
|
-
//
|
|
119
|
-
//
|
|
117
|
+
// A single comma without a dot is the decimal separator ("0,05" -> "0.05"). Everything AFTER the second
|
|
118
|
+
// comma is dropped: guessing that "1,2,3" meant 123 would, on a typo, inflate the payment a hundredfold.
|
|
120
119
|
const i = s.indexOf(",");
|
|
121
120
|
const rest = s.slice(i + 1);
|
|
122
121
|
const cut = rest.indexOf(",");
|
|
123
122
|
s = s.slice(0, i) + "." + (cut < 0 ? rest : rest.slice(0, cut));
|
|
124
123
|
}
|
|
125
|
-
//
|
|
124
|
+
// A second dot is also a typo, and the tail after it is dropped, not glued to the number ("1.2.3" -> "1.2").
|
|
126
125
|
const dot = s.indexOf(".");
|
|
127
126
|
if (dot >= 0) {
|
|
128
127
|
const rest = s.slice(dot + 1);
|
|
129
128
|
const cut = rest.indexOf(".");
|
|
130
129
|
s = s.slice(0, dot + 1) + (cut < 0 ? rest : rest.slice(0, cut));
|
|
131
130
|
}
|
|
132
|
-
s = s.replace(/^0+(?=\d)/, ""); // "005" -> "5",
|
|
131
|
+
s = s.replace(/^0+(?=\d)/, ""); // "005" -> "5", but "0.5" is left alone
|
|
133
132
|
if (!/[0-9]/.test(s)) return "";
|
|
134
133
|
return s;
|
|
135
134
|
}
|
|
136
135
|
|
|
137
|
-
/**
|
|
136
|
+
/** Number from a human-entered value, or null if there is no number. No "guessing": empty means empty. */
|
|
138
137
|
export function parseAmount(raw) {
|
|
139
138
|
const s = normalizeAmountInput(raw);
|
|
140
139
|
if (!s) return null;
|
|
@@ -143,22 +142,22 @@ export function parseAmount(raw) {
|
|
|
143
142
|
}
|
|
144
143
|
|
|
145
144
|
export function shortId() {
|
|
146
|
-
// 16 hex
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
//
|
|
145
|
+
// 16 hex chars (8 bytes = 2^64). Before it was 4 chars (randomHex(2)): with 100 000 swaps a collision is
|
|
146
|
+
// practically inevitable - 68.8% at 8 chars and 100% at 4 - and the id is a key in the state and the name of
|
|
147
|
+
// the recovery file, so a collision would mean losing a record that backs funds.
|
|
148
|
+
// IMPORTANT: already-created swaps keep short ids. They must not be renamed: the id lives in the recovery
|
|
149
|
+
// file, in links and in the state - they would fall out of sync.
|
|
151
150
|
return randomHex(8);
|
|
152
151
|
}
|
|
153
152
|
|
|
154
|
-
//
|
|
153
|
+
// QUICK CHECK OF A Monero ADDRESS SHAPE: base58, length 95 (plain) / 106 (integrated), network prefix.
|
|
155
154
|
//
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
155
|
+
// WHAT THIS FUNCTION DOES NOT DO: IT DOES NOT VERIFY THE CHECKSUM. Any typo inside the address passes here
|
|
156
|
+
// unnoticed. It used to be named isValidMoneroAddress - and that name was a trap: a function called
|
|
157
|
+
// "address check" that lets a typo through ends up on the money path sooner or later.
|
|
159
158
|
//
|
|
160
|
-
//
|
|
161
|
-
//
|
|
159
|
+
// THE REAL CHECK IS checkAddress() from ../monero/address.js: format + base58 decode + keccak256 checksum
|
|
160
|
+
// + network match. The form calls that one. This one only screens out obvious garbage before the heavy decode.
|
|
162
161
|
export function looksLikeMoneroAddress(addr) {
|
|
163
162
|
const a = String(addr || "").trim();
|
|
164
163
|
if (![95, 106].includes(a.length)) return false;
|