@ninsei-labs/sdk 0.39.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 +866 -0
- package/LICENSE +21 -0
- package/README.md +187 -0
- package/engine/atomic/dleq2.js +330 -0
- package/engine/atomic/halfenc.js +205 -0
- package/engine/atomic/halves.js +119 -0
- package/engine/atomic/order-client.js +57 -0
- package/engine/atomic/order.js +132 -0
- package/engine/atomic/orderContext.js +70 -0
- package/engine/atomic/orderHalfCommit.js +29 -0
- package/engine/core/chainSource.js +306 -0
- package/engine/core/clock.js +40 -0
- package/engine/core/config.js +885 -0
- package/engine/core/finality.js +93 -0
- package/engine/core/format.js +168 -0
- package/engine/core/preSignGuards.js +495 -0
- package/engine/core/rfqSource.js +193 -0
- package/engine/core/sellWindow.js +43 -0
- package/engine/core/store.js +100 -0
- package/engine/core/swap.js +1153 -0
- package/engine/evm/amounts.js +131 -0
- package/engine/evm/auth.js +156 -0
- package/engine/evm/claimGas.js +87 -0
- package/engine/evm/depositor.js +127 -0
- package/engine/evm/dex.js +836 -0
- package/engine/evm/escrow.js +208 -0
- package/engine/evm/factory.js +149 -0
- package/engine/evm/fees.js +90 -0
- package/engine/evm/funding.js +309 -0
- package/engine/evm/gasReserve.js +88 -0
- package/engine/evm/index.js +123 -0
- package/engine/evm/injected.js +175 -0
- package/engine/evm/permit.js +125 -0
- package/engine/evm/prices.js +179 -0
- package/engine/evm/session.js +777 -0
- package/engine/mock/chain.js +98 -0
- package/engine/monero/address.js +265 -0
- package/engine/monero/halvesSweep.js +68 -0
- package/engine/monero/node.js +110 -0
- package/engine/monero/scan.js +325 -0
- package/engine/monero/swapKeys.js +69 -0
- package/engine/monero/unlock.js +46 -0
- package/engine/monero/wallet.js +168 -0
- package/engine/recovery/recoveryFile.js +363 -0
- package/engine/recovery/restore.js +176 -0
- package/engine/sdk/bridge.js +609 -0
- package/engine/ui/dom.js +90 -0
- package/engine/ui/walletModal.js +132 -0
- package/index.d.ts +1417 -0
- package/package.json +74 -0
- package/src/actions.mjs +72 -0
- package/src/adapters/evm-wallet.mjs +126 -0
- package/src/adapters/index.mjs +21 -0
- package/src/adapters/local-storage.mjs +32 -0
- package/src/adapters/monero-wallet.mjs +185 -0
- package/src/adapters/monero.mjs +51 -0
- package/src/adapters/node-test-evm-wallet.mjs +345 -0
- package/src/codes.mjs +188 -0
- package/src/config.mjs +277 -0
- package/src/engine.mjs +53 -0
- package/src/errors.mjs +24 -0
- package/src/http.mjs +72 -0
- package/src/index.mjs +256 -0
- package/src/legs/evm.mjs +118 -0
- package/src/legs/index.mjs +50 -0
- package/src/levelsWalkSpec.mjs +183 -0
- package/src/lock.mjs +140 -0
- package/src/order.mjs +212 -0
- package/src/preflight.mjs +137 -0
- package/src/primitives.mjs +47 -0
- package/src/quoteEip712.mjs +35 -0
- package/src/quoteEip712Spec.mjs +214 -0
- package/src/quotes.mjs +465 -0
- package/src/record.mjs +65 -0
- package/src/recovery.mjs +40 -0
- package/src/sim.mjs +37 -0
- package/src/swap-flow.mjs +492 -0
- package/src/swaps.mjs +935 -0
- package/src/sweep.mjs +222 -0
- package/src/wallet.mjs +164 -0
- package/src/xmr-arrival.mjs +289 -0
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
// GENERATED FILE - a byte-for-byte copy of the engine module www/js/core/config.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.
|
|
4
|
+
// Everything below this header is identical to the source.
|
|
5
|
+
|
|
6
|
+
// Конфигурация демки. Все числа, которые видит пользователь, живут здесь.
|
|
7
|
+
// Значения взяты из LITEPAPER.md (разделы 5.0, 6, 7) и помечены в UI как демо-данные.
|
|
8
|
+
//
|
|
9
|
+
// Сети и токены описаны одной структурой CHAINS: у каждой сети свой нативный токен,
|
|
10
|
+
// свой список принимаемых токенов и свои decimals. Это не украшательство: у USDT и USDC
|
|
11
|
+
// decimals РАЗНЫЕ в разных сетях (6 на Ethereum/Arbitrum/Base и 18 на BSC), а "ETH"
|
|
12
|
+
// в BSC вообще не существует - там нативный BNB. Поэтому идентификатор токена в
|
|
13
|
+
// состоянии свапа составной: "<chainId>:<SYMBOL>", например "42161:USDC".
|
|
14
|
+
// Адреса и decimals проверяются в сети: node tools/check-tokens.mjs (см. .hermes/docs).
|
|
15
|
+
//
|
|
16
|
+
// ТРИ ПРИЗНАКА У ЗАПИСЕЙ, и ни один из них не означает "не поддерживается":
|
|
17
|
+
// hidden (у СЕТИ) - сеть живая и выбирается по идентификатору, но её строки нет в списке сетей;
|
|
18
|
+
// hidden (у ТОКЕНА) - токен живой и есть в списке на обмене, но его нет в витрине сети (в шапке);
|
|
19
|
+
// peg: "native" - цена токена берётся от натива (WETH = ETH), своей демо-цены у него нет.
|
|
20
|
+
// Смысл токенного hidden: витрина сети решает, что показать рядом с названием сети, а торговать
|
|
21
|
+
// можно всем, что принимается. Так WETH есть в обмене, но не раздувает подпись сети в шапке.
|
|
22
|
+
//
|
|
23
|
+
// DEX-НОГА (Uniswap v3). У сети может быть блок uniswap: адреса QuoterV2 и UniswapV3Factory, адрес
|
|
24
|
+
// обёрнутого нативного токена (WETH/WBNB) и ступени комиссии. Адреса НЕ выдуманы: они взяты из
|
|
25
|
+
// официального списка развёртываний Uniswap (github.com/Uniswap/contracts, deployments/json/<chainId>.json)
|
|
26
|
+
// и подтверждены ВЫЗОВОМ в сети - наличие кода, symbol()/decimals() у токена, ответ getPool и живая
|
|
27
|
+
// котировка. Проверка и замер ликвидности: node tools/check-uniswap-quote.mjs. Сети без этого блока
|
|
28
|
+
// означают "адреса не подтверждены вызовом", а не "DEX там не бывает": интерфейс обязан сказать словами.
|
|
29
|
+
|
|
30
|
+
export const APP = {
|
|
31
|
+
name: "ArrakisSwap",
|
|
32
|
+
version: "0.1.0",
|
|
33
|
+
demo: true,
|
|
34
|
+
storageKey: "arrakis.demo.v1",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// Сети. rpcUrl здесь нужен ТОЛЬКО как параметр для wallet_addEthereumChain (им
|
|
39
|
+
// пользуется кошелёк пользователя, не наша страница), поэтому CSP и наш сервер
|
|
40
|
+
// к этим адресам отношения не имеют. Балансы демка читает через провайдера кошелька.
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
|
|
43
|
+
// Поле escrow отвечает на один вопрос: можно ли в этой сети СЕТТЛИТЬСЯ.
|
|
44
|
+
//
|
|
45
|
+
// Своп заканчивается там, где лежит контракт, поэтому сеть без контракта показываем в списке, но
|
|
46
|
+
// выбрать не даём (в интерфейсе она серая с пометкой "coming soon"). Пока контракт не задеплоен
|
|
47
|
+
// нигде, демка работает в режиме имитации: mode "simulated" - фондирование эскроу подделывается
|
|
48
|
+
// (simulateEscrowFunding), реальных денег никто не двигает, и это честно написано в UI.
|
|
49
|
+
//
|
|
50
|
+
// escrow: { address: null, mode: "simulated" } - демо-режим, выбрать можно
|
|
51
|
+
// escrow: { address: "0x…", mode: "live" } - контракт задеплоен (сеть закрепляется за ним)
|
|
52
|
+
// escrow: null - контракта нет и не планировался: "coming soon"
|
|
53
|
+
//
|
|
54
|
+
// СЕТИ, ГДЕ DEX-НОГА ВКЛЮЧЕНА И ИЗМЕРЯЕТСЯ (список DEX_CHAINS ниже). Он именно в ДАННЫХ, и это не
|
|
55
|
+
// украшение: обходить весь реестр нельзя, потому что большинство сетей в статусе coming soon - по ним
|
|
56
|
+
// работа не ведётся, пулы не ищутся и адреса не подтверждаются. Когда придёт время следующей сети, она
|
|
57
|
+
// добавляется ОДНОЙ СТРОКОЙ в DEX_CHAINS и блоком uniswap у себя - и попадает и в интерфейс, и в замер
|
|
58
|
+
// (tools/check-uniswap-quote.mjs берёт список сетей отсюда, а не обходом CHAINS).
|
|
59
|
+
//
|
|
60
|
+
// ПОЧЕМУ ИМЕННО ЭТИ: arbitrum-sepolia (421614) - демо-сеть, где развёрнут наш эскроу и идут все прогоны;
|
|
61
|
+
// arbitrum (42161) и mainnet (1) - боевые сети, где видно, что маршрут живой на настоящей ликвидности.
|
|
62
|
+
export const DEX_CHAINS = ["arbitrum-sepolia", "arbitrum", "mainnet"];
|
|
63
|
+
|
|
64
|
+
// СОСТАВ ОБМЕНА ЗАДАН ЖЁСТКО ЗДЕСЬ - ЭТО РЕШЕНИЕ, А НЕ РЕЗУЛЬТАТ ИЗМЕРЕНИЯ. Список читается как есть:
|
|
65
|
+
// код ничего в него не добавляет и ничего из него не выкидывает, замер его не фильтрует. Если у токена в
|
|
66
|
+
// сети нет пула, он ВСЁ РАВНО остаётся в списке, а факт отсутствия пула пишется в отчёт
|
|
67
|
+
// (tools/check-uniswap-quote.mjs) - ликвидность оценивает человек, а не инструмент.
|
|
68
|
+
// СЕТЬ МОЖЕТ НЕ ПРЕДЛАГАТЬ ЧАСТЬ СОСТАВА, и это ДРУГОЕ состояние, чем "маршрута нет": тогда актива нет
|
|
69
|
+
// ни в tokens этой сети, ни в DEX_ROUTES у неё, и why-заглушки у него быть НЕ ДОЛЖНО (заглушка значила
|
|
70
|
+
// бы "актив предлагается, но живого пула нет"). Пример - USDS и PyUSD на Arbitrum One (решение хозяина
|
|
71
|
+
// 2026-09-23). Противоречия при этом краснеют: маршрут для актива, которого сеть не предлагает, и токен
|
|
72
|
+
// в списке сети, которого нет в составе обмена.
|
|
73
|
+
export const DEX_ASSETS = ["USDT", "USDC", "DAI", "USDS", "USDe", "PyUSD"];
|
|
74
|
+
|
|
75
|
+
// СПРАВКА ДЛЯ ИНСТРУМЕНТА, А НЕ МЕХАНИЗМ ОБМЕНА: какие стейблы вообще годятся в посредники в этих сетях.
|
|
76
|
+
// Когда обход нужен, он объявляется маршрутом в DEX_ROUTES ниже - человеком, а не перебором. DEX-нога
|
|
77
|
+
// однонаправленная и доводит вход до НАТИВНОГО ТОКЕНА СЕТИ, потому что им фондируется эскроу; маршрутов,
|
|
78
|
+
// заканчивающихся стейблом, не бывает по устройству ноги, и объявить такой нельзя (инструмент заметит).
|
|
79
|
+
export const DEX_HUBS = ["USDC", "USDT", "DAI"];
|
|
80
|
+
|
|
81
|
+
// ПОРОГИ ПРОВЕРКИ ЦЕНЫ - ЧИСЛА В ДАННЫХ, А НЕ В ЛОГИКЕ (www/js/evm/dex.js: priceGateVerdict). Проверка
|
|
82
|
+
// отвечает на ОДИН вопрос: цена в пулах объявленного маршрута похожа на рыночную или сделка неверная.
|
|
83
|
+
// Это не оценка ликвидности (её по-прежнему решает человек), а защита того же рода, что покрытие суммы:
|
|
84
|
+
// неверная цена означает, что сделка не сойдётся, и такая сделка не должна начинаться (док 45).
|
|
85
|
+
// maxDeviationPct - насколько неявная цена натива, ВЫВЕДЕННАЯ ИЗ КОТИРОВКИ последнего шага, может
|
|
86
|
+
// разойтись с эталоном цены натива (Chainlink, evm/prices.js), прежде чем это будет
|
|
87
|
+
// "не рынок". 2% - граница между обычным разбегом и неверной ценой;
|
|
88
|
+
// stableBandPct - полоса "стейбл к стейблу" (шаги USDe→USDC, USDS→USDC, PyUSD→USDC): цена обязана
|
|
89
|
+
// быть около 1:1, и это проверяется САМОЙ КОТИРОВКОЙ, без оракула. 1% покрывает
|
|
90
|
+
// комиссию ступени (0.3%) и обычный разбег курса;
|
|
91
|
+
// stables - какие активы считаются стейблами для этой полосы. Это ТЕ ЖЕ активы, что в составе
|
|
92
|
+
// обмена (DEX_ASSETS): там одни стейблы по определению, и второго списка не заводим.
|
|
93
|
+
export const DEX_PRICE_GATE = {
|
|
94
|
+
maxDeviationPct: 2,
|
|
95
|
+
stableBandPct: 1,
|
|
96
|
+
stables: DEX_ASSETS,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// ПОРОГИ ЗАЩИТ ПЕРЕД ПОДПИСЬЮ - ТОЖЕ ЧИСЛА В ДАННЫХ, А НЕ В ЛОГИКЕ (www/js/core/preSignGuards.js).
|
|
100
|
+
// Здесь лежит только то, что относится к СРОКАМ ордера; пороги цены и покрытия - выше, у DEX-ноги.
|
|
101
|
+
// minReadyLeadSec - насколько срок отметки готовности обязан быть впереди "сейчас", прежде чем
|
|
102
|
+
// ордер можно подписывать. Порог - МЯГКОЕ окно продажи (600 с, www/js/core/sellWindow.js):
|
|
103
|
+
// столько провайдер обещает держать цену на экране. Раньше здесь стояло 1200 - ВЕСЬ срок
|
|
104
|
+
// подтверждений, - и это была мина после issue #111: обратный билет нода выдаёт ровно с
|
|
105
|
+
// контрактным окном 1200 с, а страница проверяет сроки на секунду-другую ПОЗЖЕ (см.
|
|
106
|
+
// www/js/core/reverseOrder.js, deadlineVerdict), то есть заявка блокировала САМУ СЕБЯ.
|
|
107
|
+
// Порог обязан быть строго меньше контрактного окна, иначе он ловит собственный билет.
|
|
108
|
+
// minClaimWindowSec - насколько срок расчёта (t1) обязан отстоять от срока готовности: окно, в
|
|
109
|
+
// которое забирающий успевает забрать ETH, не может быть "несколько секунд".
|
|
110
|
+
// 10 минут против штатного часа.
|
|
111
|
+
export const SIGNING_GUARDS = {
|
|
112
|
+
minReadyLeadSec: 600,
|
|
113
|
+
minClaimWindowSec: 600,
|
|
114
|
+
// maxDeadlineSec - ПОТОЛОК срока (issue #88): `t1` не дальше этого от "сейчас". Иначе срок можно
|
|
115
|
+
// поставить на год вперёд, и внесённые ETH нельзя вернуть ни до readyBy, ни до t1 - целый год.
|
|
116
|
+
// 72 часа (3 суток) - решение владельца; штатные окна (4 ч + 1 ч при покупке, 2 ч + 1 ч при продаже)
|
|
117
|
+
// укладываются в него с большим запасом.
|
|
118
|
+
maxDeadlineSec: 259200,
|
|
119
|
+
// readyLeadWarnSec - за сколько секунд до границы отметки готовности сторона обязана предупредить человека.
|
|
120
|
+
// У ПРЯМОГО режима это предупреждение на экране, у ОБРАТНОГО - тот же порог у сторожа ноды (у него своя
|
|
121
|
+
// настройка RFQ_READY_LEAD_WARN_SEC с тем же значением по умолчанию, потому что нода запускается отдельно
|
|
122
|
+
// и чужой конфиг не читает). Значение читается отсюда, а не повторяется числом в коде: два числа в двух
|
|
123
|
+
// местах разошлись бы молча, и тревога приходила бы не тогда, когда обещано.
|
|
124
|
+
readyLeadWarnSec: 600,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// ОБЪЯВЛЕННЫЕ МАРШРУТЫ DEX-НОГИ - РЕШЕНИЕ ХОЗЯИНА, А НЕ РЕЗУЛЬТАТ ПЕРЕБОРА В РАНТАЙМЕ (док 45).
|
|
128
|
+
// Правило хозяина (2026-09-23): "обычно через USDT или USDC выбирать и всё, если нет прямого".
|
|
129
|
+
// В обмене НЕТ ни поиска ступени, ни сравнения "напрямую / через хаб", ни выбора победителя по выходу:
|
|
130
|
+
// страница котирует ТОЛЬКО тот путь, который объявлен здесь, и либо показывает его числа, либо честно
|
|
131
|
+
// говорит, что по объявленному маршруту котировки нет (и почему). Маршрут выбирает человек.
|
|
132
|
+
// Запись: steps - шаги по порядку, каждый { to, fee, pool, verified }. Первый токен пути - сам актив.
|
|
133
|
+
// to - токен, в который ведёт шаг (символ из токенов этой сети либо обёрнутый натив этой сети);
|
|
134
|
+
// fee - ступень комиссии ЭТОГО шага;
|
|
135
|
+
// pool - адрес пула этого шага (прочитан у factory.getPool и подтверждён замером);
|
|
136
|
+
// verified - дата и блок, на которых шаг подтверждён живой котировкой (tools/check-uniswap-quote.mjs).
|
|
137
|
+
// Пустой steps + why - маршрут НЕ объявлен: это честное "маршрута нет", и по нему подпись блокируется.
|
|
138
|
+
// Числа в комментариях - из замера 2026-09-23: выход на 100 единиц, импант по профилю 100->1000, газ.
|
|
139
|
+
export const DEX_ROUTES = {
|
|
140
|
+
"arbitrum-sepolia": {
|
|
141
|
+
// Демо-сеть: объявлен единственный пул с внятной ликвидностью. ЦЕНА В НЁМ ИЗМЕРЕННО НЕГОДНАЯ
|
|
142
|
+
// (+1738% к эталону Chainlink), и это оставлено наблюдением, а не приговором: решение - за хозяином.
|
|
143
|
+
USDC: {
|
|
144
|
+
steps: [{ to: "WETH", fee: 3000, pool: "0x66eeab70ac52459dd74c6ad50d578ef76a441bbf", verified: { at: "2026-09-23", block: 311900041 } }],
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
arbitrum: {
|
|
148
|
+
// Объявлен хозяином 2026-09-23 по замеру того же дня (блок 508125577).
|
|
149
|
+
USDT: {
|
|
150
|
+
steps: [{ to: "WETH", fee: 500, pool: "0x641c00a822e8b671738d32a431a4fb6074e5c79d", verified: { at: "2026-09-23", block: 508125577 } }], // 0.036707 WETH / 100 USDT, импант -0.004%, газ 93202
|
|
151
|
+
},
|
|
152
|
+
USDC: {
|
|
153
|
+
steps: [{ to: "WETH", fee: 100, pool: "0x6f38e884725a116c9c7fbf208e79fe8828a2595f", verified: { at: "2026-09-23", block: 508125577 } }], // 0.036719 WETH / 100 USDC, импант -0.023%, газ 93921
|
|
154
|
+
},
|
|
155
|
+
DAI: {
|
|
156
|
+
steps: [{ to: "WETH", fee: 3000, pool: "0xa961f0473da4864c5ed28e00fcc53a3aab056c1b", verified: { at: "2026-09-23", block: 508125577 } }], // 0.036601 WETH / 100 DAI, импант -0.373%, газ 95653
|
|
157
|
+
},
|
|
158
|
+
// USDe против WETH пула не имеет вовсе - объявлен обход через USDC (замер: 99.931621 USDC / 100 USDe).
|
|
159
|
+
USDe: {
|
|
160
|
+
steps: [
|
|
161
|
+
{ to: "USDC", fee: 100, pool: "0x3223859e24d5413947761cb79d3094f21be69bfa", verified: { at: "2026-09-23", block: 508125577 } },
|
|
162
|
+
{ to: "WETH", fee: 100, pool: "0x6f38e884725a116c9c7fbf208e79fe8828a2595f", verified: { at: "2026-09-23", block: 508125577 } },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
// USDS И PyUSD ЗДЕСЬ НЕ ОБЪЯВЛЕНЫ ВООБЩЕ (решение хозяина 2026-09-23): на Arbitrum One они больше
|
|
166
|
+
// НЕ ПРЕДЛАГАЮТСЯ - это не "маршрута нет", а "этого актива на этой сети нет". Поэтому записей у
|
|
167
|
+
// них нет ни одной, в том числе why-заглушки: заглушка значила бы "актив предлагается, пула нет".
|
|
168
|
+
// Их нет и в tokens этой сети (см. выше), а на Ethereum оба остаются и работают.
|
|
169
|
+
// WETH - не своп, а unwrap у WETH9: 1:1. Газ зависит от держателя и суммы: замер 2026-09-23 дал 52793
|
|
170
|
+
// (держатель 0x278d…f8d2, блоки 508121467..508121667) и 52598 на другом держателе - поэтому в
|
|
171
|
+
// интерфейсе газ unwrap не показывается числом (см. evm/dex.js: unwrapLeg).
|
|
172
|
+
WETH: { steps: [], unwrap: true },
|
|
173
|
+
},
|
|
174
|
+
mainnet: {
|
|
175
|
+
// Объявлен хозяином 2026-09-23 по замеру того же дня (блок 26040283).
|
|
176
|
+
USDT: {
|
|
177
|
+
steps: [{ to: "WETH", fee: 500, pool: "0x11b815efb8f581194ae79006d24e0d814b7697f6", verified: { at: "2026-09-23", block: 26040283 } }], // 0.036714 WETH / 100 USDT, импант -0.002%, газ 81801
|
|
178
|
+
},
|
|
179
|
+
USDC: {
|
|
180
|
+
steps: [{ to: "WETH", fee: 100, pool: "0xe0554a476a092703abdb3ef35c80e0d76d32939f", verified: { at: "2026-09-23", block: 26040283 } }], // 0.036717 WETH / 100 USDC, импант -0.002%, газ 87061
|
|
181
|
+
},
|
|
182
|
+
DAI: {
|
|
183
|
+
steps: [{ to: "WETH", fee: 500, pool: "0x60594a405d53811d3bc4766596efd80fd545a270", verified: { at: "2026-09-23", block: 26040283 } }], // 0.036707 WETH / 100 DAI, импант -0.006%, газ 79908
|
|
184
|
+
},
|
|
185
|
+
// USDe: прямого пула против WETH нет - объявлен обход через USDC (замер: 99.975800 USDC / 100 USDe).
|
|
186
|
+
USDe: {
|
|
187
|
+
steps: [
|
|
188
|
+
{ to: "USDC", fee: 100, pool: "0xe6d7ebb9f1a9519dc06d557e03c522d53520e76a", verified: { at: "2026-09-23", block: 26040283 } },
|
|
189
|
+
{ to: "WETH", fee: 100, pool: "0xe0554a476a092703abdb3ef35c80e0d76d32939f", verified: { at: "2026-09-23", block: 26040283 } },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
// USDS: прямой пул против WETH существует, но он заведомо непригоден (0.000867 WETH на 100 USDS),
|
|
193
|
+
// поэтому объявлен обход через USDC (замер: 99.640880 USDC / 100 USDS на ступени 0.3%).
|
|
194
|
+
USDS: {
|
|
195
|
+
steps: [
|
|
196
|
+
{ to: "USDC", fee: 3000, pool: "0xa66a2770bc0e0c65b63b5a3bb4560e90f95d6146", verified: { at: "2026-09-23", block: 26040283 } },
|
|
197
|
+
{ to: "WETH", fee: 100, pool: "0xe0554a476a092703abdb3ef35c80e0d76d32939f", verified: { at: "2026-09-23", block: 26040283 } },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
// PyUSD: прямой пул против WETH заведомо непригоден - объявлен обход через USDC (99.989571 USDC / 100 PyUSD).
|
|
201
|
+
PyUSD: {
|
|
202
|
+
steps: [
|
|
203
|
+
{ to: "USDC", fee: 100, pool: "0x13394005c1012e708fce1eb974f1130fdc73a5ce", verified: { at: "2026-09-23", block: 26040283 } },
|
|
204
|
+
{ to: "WETH", fee: 100, pool: "0xe0554a476a092703abdb3ef35c80e0d76d32939f", verified: { at: "2026-09-23", block: 26040283 } },
|
|
205
|
+
],
|
|
206
|
+
},
|
|
207
|
+
// WETH - не своп, а unwrap у WETH9: 1:1. Газ зависит от держателя: замер 2026-09-23 дал 36335 и 36277
|
|
208
|
+
// у двух разных держателей, поэтому числом в интерфейсе он не показывается.
|
|
209
|
+
WETH: { steps: [], unwrap: true },
|
|
210
|
+
},
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
export const CHAINS = [
|
|
214
|
+
{
|
|
215
|
+
id: "arbitrum-sepolia",
|
|
216
|
+
name: "Arbitrum Sepolia",
|
|
217
|
+
vm: "evm",
|
|
218
|
+
chainId: 421614,
|
|
219
|
+
caip2: "eip155:421614",
|
|
220
|
+
testnet: true,
|
|
221
|
+
native: { symbol: "ETH", decimals: 18, usd: 3212.4 },
|
|
222
|
+
// Свой прокси, а не публичный узел напрямую: адрес эскроу (predict) приходит по этому пути, и
|
|
223
|
+
// подменённый или совравший посредник означал бы блокировку ETH не на тот адрес. Подробности,
|
|
224
|
+
// включая проверку сети и CORS, - в tools/check-evm-proxy.mjs и .hermes/deploy/arrakis-evm-*.conf
|
|
225
|
+
rpcUrl: "https://rpc.arrakisswap.trade",
|
|
226
|
+
// Фид Chainlink для цены нейтива. Адреса проверены ВЫЗОВОМ в сети (description() совпало
|
|
227
|
+
// с парой, значение свежее и в разумном коридоре) - см. tools/check-prices.mjs. Если фида
|
|
228
|
+
// нет, цены нет: интерфейс обязан сказать «демо», а не показывать вчерашнюю константу.
|
|
229
|
+
priceFeed: { address: "0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165", pair: "ETH / USD", decimals: 8, min: 100, max: 20000 },
|
|
230
|
+
explorerTx: "https://sepolia.arbiscan.io/tx/",
|
|
231
|
+
explorerAddr: "https://sepolia.arbiscan.io/address/",
|
|
232
|
+
explorerName: "arbiscan (sepolia)",
|
|
233
|
+
gasUsd: 0.01,
|
|
234
|
+
routeType: "atomic",
|
|
235
|
+
// ОСНОВНАЯ сеть демки: сюда поехал контракт эскроу, и здесь РАЗВЁРНУТ РОУТЕР КОМИССИИ - он же
|
|
236
|
+
// фабрика (порождает эскроу, хранит ставку, держит кассу комиссии). Адрес ниже - факт, а не
|
|
237
|
+
// намерение: 0xaEB1c0455C76551dcEb2E6B1245B65F0458b0aE8 (высота 314323871,
|
|
238
|
+
// транзакция 0x7a88c0249867b76b11c6cb678e6092fdc19404b51540a64c5fd03f5a32f74826, код 8223 байта,
|
|
239
|
+
// keccak256 0x90f26f641b48a2a630463195d4429b0818d29187563e09711a40811fcf08a0ba, развёрнут 30.09.2026): добавлен РАННИЙ ВОЗВРАТ в мёртвой зоне - если отметки нет,
|
|
240
|
+
// refund открыт сразу после readyBy, и он же публикует половину внёсшего. Код совпадает с текущей
|
|
241
|
+
// сборкой репозитория байт в байт, то есть ранний возврат В СЕТИ (проверено
|
|
242
|
+
// `npm run check:deployed -- --address=0xaEB1c0455C76551dcEb2E6B1245B65F0458b0aE8`).
|
|
243
|
+
// ПРЕЖНЯЯ (потолок 3%, раннего возврата ещё нет): 0xEa619bDC78989627093B7830990367B2ECE52CE5
|
|
244
|
+
// (высота 314309038, транзакция 0x07ddb118c16639785d0055cfb5e28457f33f0c1e8323ea37d421c8abdfcec767, код 8143 байта).
|
|
245
|
+
// ПРЕЖНЯЯ боевая (роутер с потолком 10%): 0x9f856cd9c14fd9493961473b804039b09a4012ef (высота
|
|
246
|
+
// 313597131, код 8723 байта, keccak 0xe35b9efa1466fc9e57ffc61fd6243019c55990ea4a04de03bbfa3ec477e49427).
|
|
247
|
+
// ПРЕЖНЯЯ до неё (фабрика без комиссии): 0xcb54af9ec11344d448c7900ccb08530a23783259 (высота
|
|
248
|
+
// 312222006, код 6035 байт); ещё ПРЕЖНИЕ: 0x0ec0be02…4222e (высота 312180386, 5989 байт),
|
|
249
|
+
// 0x13556d78…aa8b8 и 0x4E7270…dbe1b0, см. .hermes/deploy.
|
|
250
|
+
// mode: "live" - форма вызывает НАСТОЯЩИЕ транзакции: создание ордера ВМЕСТЕ С ДЕНЬГАМИ одной
|
|
251
|
+
// транзакцией (createOrderAndFund; отдельной функции внесения у эскроу нет вовсе - ордер рождается
|
|
252
|
+
// зафондированным, см. FACTORY_METHODS в www/js/evm/factory.js и ESCROW_METHODS в escrow.js).
|
|
253
|
+
// Прежних lock(sealedHalf) и hashlock в контракте нет: половина не посылается, а раскрывается
|
|
254
|
+
// предъявлением.
|
|
255
|
+
// Возвращается в "simulated" одной правкой, если нужно показать демо без движения средств.
|
|
256
|
+
// cashier (#103): касса комиссии ИНТЕРФЕЙСА, которую он называет в котировках. Контракты в сети ещё
|
|
257
|
+
// ПРЕЖНИЕ (док 55), поэтому кассы там нет: поле заполняется тем же заходом, что и адрес фабрики.
|
|
258
|
+
escrow: { address: "0xaEB1c0455C76551dcEb2E6B1245B65F0458b0aE8", mode: "live", cashier: null },
|
|
259
|
+
tokens: [
|
|
260
|
+
// Тестовый USDC (Circle): decimals 6 и symbol "USDC" проверены в сети этой же нодой.
|
|
261
|
+
{ symbol: "USDC", address: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
262
|
+
// WETH - ЭТО ТОТ ЖЕ ETH, ТОЛЬКО ОБЁРНУТЫЙ, поэтому пула для него не ищем: WETH -> ETH это
|
|
263
|
+
// withdraw у самого WETH9, один к одному и без свопа (см. DEX-ногу ниже и tools/check-uniswap-quote.mjs).
|
|
264
|
+
// hidden: true - "в витрине сети (в шапке) не показываем", а НЕ "не поддерживается": в списке
|
|
265
|
+
// токенов на обмене он есть (список на обмене берёт tokensOf, а шапка фильтрует по hidden).
|
|
266
|
+
// Адрес определён ИЗМЕРЕНИЕМ, а не по памяти: token0/token1 пула USDC/WETH дали этот адрес, а
|
|
267
|
+
// symbol()/decimals()/name() ответили "WETH"/18/"Wrapped Ether" (проверка 2026-09-23).
|
|
268
|
+
// peg: "native" - своей демо-цены у WETH нет: его долларовая цена - это цена ETH.
|
|
269
|
+
{ symbol: "WETH", address: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73", decimals: 18, cls: "ar-token-eth", peg: "native", hidden: true },
|
|
270
|
+
],
|
|
271
|
+
// DEX-НОГА ЭТОЙ СЕТИ (Uniswap v3, только чтение: eth_call к QuoterV2).
|
|
272
|
+
// ФАБРИКИ В ОФИЦИАЛЬНОМ СПИСКЕ ДЛЯ ARBITRUM SEPOLIA НЕТ, поэтому адрес ПОЛУЧЕН ИЗМЕРЕНИЕМ:
|
|
273
|
+
// у пулов, названных в .hermes/docs/43-uniswap-same-chain-ramp.md, прочитан factory(), а затем
|
|
274
|
+
// сверено, что getPool(USDC, WETH, fee) у него возвращает те же адреса (2026-09-23).
|
|
275
|
+
// ВАЖНО ПРО ЭТУ СЕТЬ: пулы в ней ЕСТЬ, но ликвидность негодная. Ступень 0.01% НЕ ОБСЛУЖИВАЕТ НИ
|
|
276
|
+
// ОДНОГО размера ("pool cannot serve this size" - вокруг цены ликвидности нет), у 0.05% выход НОЛЬ
|
|
277
|
+
// на любом размере, а 0.3% отвечает, но ценой, которая с эталоном Chainlink расходится.
|
|
278
|
+
// ОДНО ЧИСЛО, А НЕ ТРИ РАЗНЫХ. До 24 сентября 2026 здесь стояли "в 30 раз" и "в 18 раз", а в
|
|
279
|
+
// докладе 43 - 94.54%: это замеры НА РАЗНЫХ РАЗМЕРАХ В РАЗНЫЕ ДНИ, и без названного размера и
|
|
280
|
+
// способа каждое из них - просто другое число. Замер 2026-09-24, блок 312238363, головной размер
|
|
281
|
+
// 100 USDC: котировка дала 0.116508 WETH, то есть неявная цена натива $858.31 против $2650.42 по
|
|
282
|
+
// фиду Chainlink - расхождение 67.6% при пороге 2%, пул дешевле эталона в 3.09 раза.
|
|
283
|
+
// СПОСОБ ЗАМЕРА - ТОТ ЖЕ КОД, ЧТО В ИНТЕРФЕЙСЕ (www/js/evm/dex.js: priceGateVerdict, эталон из
|
|
284
|
+
// www/js/evm/prices.js), и он перезапускаем одной командой:
|
|
285
|
+
// node tools/check-uniswap-quote.mjs --chain=arbitrum-sepolia
|
|
286
|
+
// ЧИСЛО ЗАВИСИТ ОТ РАЗМЕРА (тот же блок, та же шкала): 1 USDC = 77.5%, 10 USDC = 76.6%,
|
|
287
|
+
// 100 USDC = 67.6%, 1000 USDC = -16.0%. Поэтому здесь стоит ОДНО число С НАЗВАННЫМ РАЗМЕРОМ.
|
|
288
|
+
// Инструмент печатает ещё и наблюдение в шкале "во сколько раз дешевле" (+208.6% для 100 USDC,
|
|
289
|
+
// то есть те же 3.09 раза) - это ПЕРЕСЧЁТ той же величины, а не второй замер.
|
|
290
|
+
uniswap: {
|
|
291
|
+
factory: "0x248ab79bbb9bc29bb72f7cd42f17e054fc40188e",
|
|
292
|
+
quoter: "0x2779a0CC1c3e0E44D2542EC3e79e3864Ae93Ef0B",
|
|
293
|
+
// ИСПОЛНЕНИЕ (в отличие от котировки строкой выше): адреса ЗАМЕРЕНЫ, а не взяты из соглашения о
|
|
294
|
+
// развёртывании, и замер перепроверяется прогоном tools/check-dex-exec.mjs.
|
|
295
|
+
// router - SwapRouter02. Три НЕЗАВИСИМЫХ признака происхождения, все из цепи: (1) код по адресу
|
|
296
|
+
// есть, 24497 байт; (2) его factory() РАВЕН фабрике Uniswap строкой выше; (3) его WETH9()
|
|
297
|
+
// РАВЕН wrapped ниже. Плюс исходник верифицирован у обозревателя: Arbiscan API V2 (chainid
|
|
298
|
+
// 421614) отдал ABI из 41 записи, имя контракта SwapRouter02, компилятор v0.7.6+commit.7338295f.
|
|
299
|
+
// Угадать адрес, который вернёт ИМЕННО эти factory() и WETH9(), нельзя - это тот же контракт.
|
|
300
|
+
// ВАЖНО: permit2() у ЭТОГО роутера ОТКАТЫВАЕТ (проверяется прогоном): он не потребляет
|
|
301
|
+
// Permit2, и разрешение даётся ему самому обычным approve.
|
|
302
|
+
// permit2 - канонический Permit2 Uniswap: код 9152 байта, и его DOMAIN_SEPARATOR() совпал с
|
|
303
|
+
// посчитанным С НУЛЯ для chainId 421614 и этого адреса (имя домена "Permit2"), а исходник
|
|
304
|
+
// верифицирован у обозревателя. Нужен пути ФИНАНСИРОВАНИЯ ордера, а НЕ этому роутеру.
|
|
305
|
+
// UniversalRouter (0x4A7b5Da61326A6379179b40d00F57E5bbDC962c2) СЮДА НЕ ВНЕСЁН - и это решение, а не
|
|
306
|
+
// забывчивость: его происхождение по этой сети НЕ подтверждается (getabi и getsourcecode у
|
|
307
|
+
// обозревателя отвечают NOTOK, то есть исходник не верифицирован), а "тот же адрес, что в докладе
|
|
308
|
+
// 43" доказательством не является. Появится нужда - сначала доказательство, потом запись.
|
|
309
|
+
router: "0x101F443B4d1b059569D643917553c771E1b9663E",
|
|
310
|
+
permit2: "0x000000000022D473030F116dDEE9F6B43aC78BA3",
|
|
311
|
+
// Обёрнутый нативный токен: адрес совпадает с WETH в tokens выше - это проверяется (check-front).
|
|
312
|
+
wrapped: { symbol: "WETH", name: "Wrapped Ether", address: "0x980B62Da83eFf3D4576C647993b0c1D7faf17c73", decimals: 18 },
|
|
313
|
+
feeTiers: [100, 500, 3000, 10000],
|
|
314
|
+
},
|
|
315
|
+
// ЦЕНА ЭТОЙ СЕТИ НЕГОДНАЯ ПО ПРИРОДЕ, И ЭТО ПРИЗНАК СЕТИ (по образцу hidden/unsupported), а не
|
|
316
|
+
// ослабление проверки: тот же замер, что в блоке uniswap выше (блок 312238363, 100 USDC: неявная
|
|
317
|
+
// цена натива $858.31 против эталона $2650.42 - расхождение 67.6% при пороге 2%). На боевой сети такая цена
|
|
318
|
+
// ОСТАНАВЛИВАЕТ подпись, здесь - только помечается словами в интерфейсе: на тестнете проверяется
|
|
319
|
+
// ПОТОК, а не рынок, иначе демо-прогоны встали бы на цене, которую мы не выбирали.
|
|
320
|
+
// Признака нет у боевых сетей - там проверка цены блокирует, и это умолчание (DEX_PRICE_GATE).
|
|
321
|
+
// Песочница local-anvil в проверку не попадает вовсе: у неё нет ни блока uniswap, ни токенов, то
|
|
322
|
+
// есть DEX-ноги там не бывает - помечать нечего.
|
|
323
|
+
dexPriceGate: "tolerated",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
// ЛОКАЛЬНАЯ СЕТЬ ДЛЯ ВНУТРЕННИХ ТЕСТОВ. Это anvil на этой же машине: настоящая цепочка, настоящие
|
|
327
|
+
// транзакции, но песочница. В демо она не для показа - она для прогонов, где нужен полный контроль.
|
|
328
|
+
// СВОЙ chainId (31337), а не как у публичной сети. С совпадающим идентификатором фронт не мог однозначно
|
|
329
|
+
// выбрать песочницу: при сверке с кошельком он находил ПЕРВУЮ сеть с таким id и возвращался к публичной.
|
|
330
|
+
// 31337 - обычный идентификатор локальных цепочек, ни с одной настоящей сетью не совпадает.
|
|
331
|
+
id: "local-anvil",
|
|
332
|
+
name: "Local (anvil)",
|
|
333
|
+
// СКРЫТА ИЗ СПИСКА СЕТЕЙ, НО НЕ ВЫКЛЮЧЕНА. hidden - это только про состав строк, которые рисует
|
|
334
|
+
// список (см. shownChains в www/js/ui/chainPicker.js): в демо песочницу показывать нечего, а для
|
|
335
|
+
// внутренних прогонов она нужна. Признак НЕ означает "не поддерживается" (это unsupported у
|
|
336
|
+
// Monad/Hyperliquid - те остаются в списке, просто серые и подписаны тем же "coming soon", что и остальные неживые сети):
|
|
337
|
+
// escrowState, isSelectable и settlementChain читают сам реестр и про hidden не знают, поэтому
|
|
338
|
+
// песочница остаётся живой сетью с живым эскроу и выбирается по идентификатору "local-anvil".
|
|
339
|
+
hidden: true,
|
|
340
|
+
vm: "evm",
|
|
341
|
+
chainId: 31337,
|
|
342
|
+
caip2: "eip155:31337",
|
|
343
|
+
testnet: true,
|
|
344
|
+
native: { symbol: "ETH", decimals: 18, usd: 3212.4 },
|
|
345
|
+
rpcUrl: "http://127.0.0.1:8545",
|
|
346
|
+
// Фида Chainlink в песочнице нет - и это ШТАТНЫЙ случай: интерфейс обязан сказать «демо»,
|
|
347
|
+
// а не показать вчерашнюю константу.
|
|
348
|
+
priceFeed: null,
|
|
349
|
+
explorerTx: "",
|
|
350
|
+
explorerAddr: "",
|
|
351
|
+
explorerName: "",
|
|
352
|
+
gasUsd: 0.0001,
|
|
353
|
+
routeType: "atomic",
|
|
354
|
+
// ФАБРИКА, развёрнутая в локальную цепочку. Адрес НЕ ВЫБИРАЕТСЯ, А СЛЕДУЕТ ИЗ ИСТОРИИ ОТПРАВИТЕЛЯ:
|
|
355
|
+
// CREATE строит его из счёта и номера его транзакции, поэтому на СВЕЖЕЙ цепочке он один и тот же, а на
|
|
356
|
+
// давно поднятой - другой: у счёта уже есть история. Здесь адрес ИЗМЕРЕН на цепи (чтение кода + keccak)
|
|
357
|
+
// после передеплоя под формат котировки с xmrAmount: счёт 0 anvil, nonce 60, блок 74, 13188 байт кода
|
|
358
|
+
// (keccak 0x9bffb62d50eef21bcfd4b59e1c3d6fb7cbb3e303257edb2f5ba9dc65b3e8719c - совпал со сборкой дерева).
|
|
359
|
+
// Команда - из mvp/contracts: forge script script/Deploy.s.sol:Deploy --rpc-url http://127.0.0.1:8545
|
|
360
|
+
// --broadcast --private-key <ключ счёта 0 anvil> - той же формой, что в tools/stack.mjs.
|
|
361
|
+
// cashier - второй CREATE того же захода (сначала фабрика, затем касса, затем реестр): 573 байта,
|
|
362
|
+
// keccak 0xcca1d8bc53d1d1c98901570800e093d0933f84637c6cdc727153b3ef8d8c3c36 - ровно тот хеш, которым
|
|
363
|
+
// эскроу сверяет кассу (#122). Реестр - третий CREATE (DEPLOY_REGISTRY=true), 4557 байт.
|
|
364
|
+
// ПРЕЖНЯЯ запись: фабрика 0x5fbdb2315678afecb367f032d93f642f64180aa3 (13077 байт, знала только прежний
|
|
365
|
+
// селектор predict 0x3d81fc01) и касса 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512 (573 байта, но ДРУГОЙ
|
|
366
|
+
// сборки - keccak 0x5c32bb9c…): её код не совпадал с канонической кассой, и эскроу отверг бы такой ордер
|
|
367
|
+
// по #122 - поэтому касса развёрнута заново тем же заходом.
|
|
368
|
+
escrow: { address: "0xcbEAF3BDe82155F56486Fb5a1072cb8baAf547cc", mode: "live", cashier: "0x1429859428C0aBc9C2C47C8Ee9FBaf82cFA0F20f" },
|
|
369
|
+
// Токенов в песочнице нет: расплата идёт нейтивом. Пустой список - это факт о песочнице, а не забывчивость.
|
|
370
|
+
tokens: [],
|
|
371
|
+
// Uniswap в песочнице не развёрнут и не нужен: тут своя цепочка для сквозных прогонов, а DEX-нога
|
|
372
|
+
// проверяется в публичных сетях (см. DEX_CHAINS у начала файла). Блока uniswap у сети нет.
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
id: "arbitrum",
|
|
376
|
+
name: "Arbitrum One",
|
|
377
|
+
vm: "evm",
|
|
378
|
+
chainId: 42161,
|
|
379
|
+
caip2: "eip155:42161",
|
|
380
|
+
native: { symbol: "ETH", decimals: 18, usd: 3212.4 },
|
|
381
|
+
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
|
382
|
+
// Фид Chainlink для цены нейтива. Адреса проверены ВЫЗОВОМ в сети (description() совпало
|
|
383
|
+
// с парой, значение свежее и в разумном коридоре) - см. tools/check-prices.mjs. Если фида
|
|
384
|
+
// нет, цены нет: интерфейс обязан сказать «демо», а не показывать вчерашнюю константу.
|
|
385
|
+
priceFeed: { address: "0x639Fe6ab55C921f74e7fac1ee960C0B6293ba612", pair: "ETH / USD", decimals: 8, min: 100, max: 20000 },
|
|
386
|
+
explorerTx: "https://arbiscan.io/tx/",
|
|
387
|
+
explorerAddr: "https://arbiscan.io/address/",
|
|
388
|
+
explorerName: "arbiscan",
|
|
389
|
+
gasUsd: 0.21, // демо-оценка стоимости газа, как и остальные числа демки
|
|
390
|
+
routeType: "atomic",
|
|
391
|
+
// Контракта эскроу нет ни на одной боевой сети, поэтому демка имитирует фондирование.
|
|
392
|
+
escrow: { address: null, mode: "simulated" },
|
|
393
|
+
tokens: [
|
|
394
|
+
{ symbol: "USDC", address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
395
|
+
// Адрес тот же, что был у USDT на Arbitrum, но контракт теперь отвечает symbol="USD₮0":
|
|
396
|
+
// Tether передал контракт USDT0 (миграция прошла без смены адреса, 1:1 к USDT на Ethereum).
|
|
397
|
+
// Идентификатор оставляем USDT - пользователь меняет «USDT», а не «USD₮0».
|
|
398
|
+
{ symbol: "USDT", onChainSymbol: "USD₮0", address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
399
|
+
// DAI и USDe: адреса - РЕШЕНИЕ ХОЗЯИНА (стояли и раньше). Каждый подтверждён чтением в сети
|
|
400
|
+
// 2026-09-23, блок 508109137: код по адресу есть, symbol()/decimals()/name() прочитаны. Замер по
|
|
401
|
+
// фабрике (getPool(токен, WETH, fee), ступени 100/500/3000/10000) на тот же блок: у USDe пулы 500
|
|
402
|
+
// (0xaa9aea1896130089a8fc33acd7028dbaeba421d8) и 3000 (0x9511ee80843cdf8ecfa0337c73621458a8ca1c73)
|
|
403
|
+
// есть, запасы НУЛЕВЫЕ. Это СПРАВКА для человека, а не отбор: список обмена жёсткий (DEX_ASSETS),
|
|
404
|
+
// инструмент его не фильтрует.
|
|
405
|
+
// USDS И PyUSD В ЭТОЙ СЕТИ НЕ ПРЕДЛАГАЮТСЯ (решение хозяина 2026-09-23). Прежние адреса (USDS
|
|
406
|
+
// 0x6491c05A82219b8D1479057361ff1654749b876b, PyUSD 0x46850aD61C2B7d64d08c9C754F45254596696984) убраны
|
|
407
|
+
// отсюда вместе с маршрутами: актив, которого сеть не предлагает, не должен ни рисоваться в строке
|
|
408
|
+
// активов сети (ui/chainPicker.js), ни попадать в список на обмен (ui/views/swapForm.js). На
|
|
409
|
+
// Ethereum оба остаются.
|
|
410
|
+
{ symbol: "DAI", address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", decimals: 18, usd: 1, cls: "ar-token-dai" },
|
|
411
|
+
{ symbol: "USDe", address: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", decimals: 18, usd: 1, cls: "ar-token-usde" },
|
|
412
|
+
// WETH: как в arbitrum-sepolia (см. комментарий там) - пула не ищем, WETH->ETH это unwrap у WETH9.
|
|
413
|
+
// Адрес определён измерением: token0/token1 пулов USDC/WETH, symbol()="WETH", decimals=18, name()="Wrapped Ether".
|
|
414
|
+
{ symbol: "WETH", address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", decimals: 18, cls: "ar-token-eth", peg: "native", hidden: true },
|
|
415
|
+
],
|
|
416
|
+
// DEX-НОГА: адреса из официального списка развёртываний Uniswap (deployments/json/42161.json) и
|
|
417
|
+
// подтверждены вызовами в сети 2026-09-23: код QuoterV2 8273 байта, код фабрики 24535 байт,
|
|
418
|
+
// getPool(USDC|USDT|DAI|USDe, WETH, 100|500|3000|10000) отвечает, котировки живые.
|
|
419
|
+
// ЧЕГО В ЭТОЙ СЕТИ НЕТ: у USDe пул существует, но ликвидности в нём нет (liquidity()=0), а ступень
|
|
420
|
+
// 10000 у DAI мизерная - котировка на 100000 DAI даёт вчетверо меньше, чем на 1000. Это видно в
|
|
421
|
+
// замере, и это ровно тот случай, ради которого нога считается через пул, а не берётся из мока.
|
|
422
|
+
uniswap: {
|
|
423
|
+
factory: "0x1F98431c8aD98523631AE4a59f267346ea31F984",
|
|
424
|
+
quoter: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
|
|
425
|
+
wrapped: { symbol: "WETH", name: "Wrapped Ether", address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", decimals: 18 },
|
|
426
|
+
feeTiers: [100, 500, 3000, 10000],
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
id: "mainnet",
|
|
431
|
+
name: "Ethereum",
|
|
432
|
+
vm: "evm",
|
|
433
|
+
chainId: 1,
|
|
434
|
+
caip2: "eip155:1",
|
|
435
|
+
native: { symbol: "ETH", decimals: 18, usd: 3212.4 },
|
|
436
|
+
rpcUrl: "https://ethereum-rpc.publicnode.com",
|
|
437
|
+
// Фид Chainlink для цены нейтива. Адреса проверены ВЫЗОВОМ в сети (description() совпало
|
|
438
|
+
// с парой, значение свежее и в разумном коридоре) - см. tools/check-prices.mjs. Если фида
|
|
439
|
+
// нет, цены нет: интерфейс обязан сказать «демо», а не показывать вчерашнюю константу.
|
|
440
|
+
priceFeed: { address: "0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419", pair: "ETH / USD", decimals: 8, min: 100, max: 20000 },
|
|
441
|
+
explorerTx: "https://etherscan.io/tx/",
|
|
442
|
+
explorerAddr: "https://etherscan.io/address/",
|
|
443
|
+
explorerName: "etherscan",
|
|
444
|
+
gasUsd: 3.4, // на L1 газ заметно дороже - в демо это видно при выборе сети
|
|
445
|
+
routeType: "atomic",
|
|
446
|
+
escrow: { address: null, mode: "simulated" },
|
|
447
|
+
tokens: [
|
|
448
|
+
{ symbol: "USDC", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
449
|
+
{ symbol: "USDT", address: "0xdAC17F958D2ee523a2206206994597C13D831ec7", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
450
|
+
// DAI, USDe, USDS, PyUSD: USDS и PyUSD - АДРЕСА ХОЗЯИНА, каждый подтверждён чтением в сети
|
|
451
|
+
// 2026-09-23, блок 26039940: код по адресу есть, symbol()/decimals()/name() прочитаны
|
|
452
|
+
// (USDS: "USDS"/18/"USDS Stablecoin"; PyUSD: "PYUSD"/6/"PayPal USD"). USDS и USDe отличаются одной
|
|
453
|
+
// буквой, поэтому подтверждались отдельно. Замер по фабрике (getPool(токен, WETH, fee)) на тот же
|
|
454
|
+
// блок: у USDS пулы только на 500 (0xe547c6d5039d1902db58102e7f52410dbc5b0708, ~2.1 USDS) и 3000
|
|
455
|
+
// (0x691c9c856eeec77531750c465450b5e05a74c047, пусто); у PyUSD - на 500 (0xa8b1a6acc942b8266f987c4b0cdf625e3479a26b, пусто),
|
|
456
|
+
// 3000 (0xf313d711d71eb9a607b4a61a827a9e32a7846621, ~33.8 PyUSD) и 10000 (0x643ea0885875a77bf8dc3e0f3ead866a125276b1, ~265.8 PyUSD);
|
|
457
|
+
// у USDe пула с WETH НЕТ ни на одной ступени. Это СПРАВКА, а не отбор: список жёсткий (DEX_ASSETS).
|
|
458
|
+
{ symbol: "DAI", address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", decimals: 18, usd: 1, cls: "ar-token-dai" },
|
|
459
|
+
{ symbol: "USDe", address: "0x4c9EDD5852cd905f086C759E8383e09bff1E68B3", decimals: 18, usd: 1, cls: "ar-token-usde" },
|
|
460
|
+
{ symbol: "USDS", address: "0xdC035D45d973E3EC169d2276DDab16f1e407384F", decimals: 18, usd: 1, cls: "ar-token-usds" },
|
|
461
|
+
{ symbol: "PyUSD", address: "0x6c3ea9036406852006290770BEdFcAbA0e23A0e8", decimals: 6, usd: 1, cls: "ar-token-pyusd" },
|
|
462
|
+
// WETH: пула не ищем - WETH->ETH это unwrap у WETH9 (1:1). hidden: true = не показывать в витрине
|
|
463
|
+
// сети, но оставить в списке на обмене (см. arbitrum-sepolia). Адрес подтверждён измерением.
|
|
464
|
+
{ symbol: "WETH", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: 18, cls: "ar-token-eth", peg: "native", hidden: true },
|
|
465
|
+
],
|
|
466
|
+
// DEX-НОГА: адреса из официального списка Uniswap (deployments/json/1.json), подтверждены вызовами:
|
|
467
|
+
// код QuoterV2 8273 байта, фабрики 24535 байт, пулы USDC|USDT|DAI к WETH есть по всем четырём
|
|
468
|
+
// ступеням, котировки живые. У USDe в этой сети пула НЕТ ни по одной ступени - так и должно быть
|
|
469
|
+
// сказано словами, а не показано нулём.
|
|
470
|
+
uniswap: {
|
|
471
|
+
factory: "0x1F98431c8aD98523631AE4a59f267346ea31F984",
|
|
472
|
+
quoter: "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
|
|
473
|
+
wrapped: { symbol: "WETH", name: "Wrapped Ether", address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", decimals: 18 },
|
|
474
|
+
feeTiers: [100, 500, 3000, 10000],
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
id: "base",
|
|
479
|
+
name: "Base",
|
|
480
|
+
vm: "evm",
|
|
481
|
+
chainId: 8453,
|
|
482
|
+
caip2: "eip155:8453",
|
|
483
|
+
native: { symbol: "ETH", decimals: 18, usd: 3212.4 },
|
|
484
|
+
rpcUrl: "https://mainnet.base.org",
|
|
485
|
+
// Фид Chainlink для цены нейтива. Адреса проверены ВЫЗОВОМ в сети (description() совпало
|
|
486
|
+
// с парой, значение свежее и в разумном коридоре) - см. tools/check-prices.mjs. Если фида
|
|
487
|
+
// нет, цены нет: интерфейс обязан сказать «демо», а не показывать вчерашнюю константу.
|
|
488
|
+
priceFeed: { address: "0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70", pair: "ETH / USD", decimals: 8, min: 100, max: 20000 },
|
|
489
|
+
explorerTx: "https://basescan.org/tx/",
|
|
490
|
+
explorerAddr: "https://basescan.org/address/",
|
|
491
|
+
explorerName: "basescan",
|
|
492
|
+
gasUsd: 0.08,
|
|
493
|
+
routeType: "atomic",
|
|
494
|
+
escrow: { address: null, mode: "simulated" },
|
|
495
|
+
tokens: [
|
|
496
|
+
{ symbol: "USDC", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
497
|
+
{ symbol: "USDT", address: "0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
498
|
+
{ symbol: "DAI", address: "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb", decimals: 18, usd: 1, cls: "ar-token-dai" },
|
|
499
|
+
{ symbol: "USDe", address: "0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34", decimals: 18, usd: 1, cls: "ar-token-usde" },
|
|
500
|
+
],
|
|
501
|
+
// DEX-НОГИ В ЭТОЙ СЕТИ НЕТ, и это НЕ про "мы не проверяли", а про состояние продукта: сеть в
|
|
502
|
+
// статусе coming soon, блока uniswap у неё нет и пулы здесь не ищутся. Что это значит для экрана -
|
|
503
|
+
// он обязан сказать словами (см. mock/market.js: dexRoute), а не показать пул-пустышку.
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
id: "bsc",
|
|
507
|
+
name: "BNB Chain",
|
|
508
|
+
vm: "evm",
|
|
509
|
+
chainId: 56,
|
|
510
|
+
caip2: "eip155:56",
|
|
511
|
+
native: { symbol: "BNB", decimals: 18, usd: 612.5 },
|
|
512
|
+
rpcUrl: "https://bsc-dataseed.binance.org",
|
|
513
|
+
// Цена BNB: фид Chainlink не подтверждён вызовом - пока сеть показывает «демо»,
|
|
514
|
+
// а не выдуманное число. Добавляется сюда, когда адрес проверен так же, как остальные.
|
|
515
|
+
explorerTx: "https://bscscan.com/tx/",
|
|
516
|
+
explorerAddr: "https://bscscan.com/address/",
|
|
517
|
+
explorerName: "bscscan",
|
|
518
|
+
gasUsd: 0.05,
|
|
519
|
+
routeType: "atomic",
|
|
520
|
+
escrow: { address: null, mode: "simulated" },
|
|
521
|
+
tokens: [
|
|
522
|
+
// Внимание: на BSC у USDT и USDC 18 decimals (не 6, как на Ethereum L1 и L2).
|
|
523
|
+
// DAI и USDe на BNB Chain не добавляем: в демке их там нет.
|
|
524
|
+
{ symbol: "USDT", address: "0x55d398326f99059fF775485246999027B3197955", decimals: 18, usd: 1, cls: "ar-token-usdt" },
|
|
525
|
+
{ symbol: "USDC", address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d", decimals: 18, usd: 1, cls: "ar-token-usdc" },
|
|
526
|
+
],
|
|
527
|
+
// DEX-НОГИ В ЭТОЙ СЕТИ НЕТ: сеть в статусе coming soon, блок uniswap у неё отсутствует, и пулы
|
|
528
|
+
// здесь не ищутся. Обёрнутый нативный токен этой сети - WBNB (нативный тут BNB, не ETH), но пока
|
|
529
|
+
// сеть не включена, в реестре нет ни одного её адреса: незачем подтверждать то, чем не пользуемся.
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
id: "bitcoin",
|
|
533
|
+
name: "Bitcoin",
|
|
534
|
+
vm: "bitcoin",
|
|
535
|
+
chainId: 0, // у Bitcoin нет EVM chainId; 0 - это SLIP-44 coin type, и он не сталкивается с EVM-сетями
|
|
536
|
+
caip2: "bip122:000000000019d6689c085ae165831e93", // bip122 + первые 32 символа хеша нулевого блока
|
|
537
|
+
native: { symbol: "BTC", decimals: 8, usd: 96000 },
|
|
538
|
+
// У Bitcoin нет JSON-RPC: это REST-API блокчейн-обозревателя. Демка в него не ходит.
|
|
539
|
+
rpcUrl: "https://blockstream.info/api",
|
|
540
|
+
explorerTx: "https://mempool.space/tx/",
|
|
541
|
+
explorerAddr: "https://mempool.space/address/",
|
|
542
|
+
explorerName: "mempool.space",
|
|
543
|
+
gasUsd: 1.2,
|
|
544
|
+
routeType: "atomic",
|
|
545
|
+
// В Bitcoin актив один - сам BTC: контрактных токенов в протоколе нет.
|
|
546
|
+
tokens: [],
|
|
547
|
+
// Эскроу-контракта здесь быть не может, и это не пробел: у Bitcoin вместо контракта работает
|
|
548
|
+
// native atomic swap (HTLC/адапторные подписи в скрипте). Поэтому говорим прямо, чем сеттлится,
|
|
549
|
+
// и не выдаём отсутствие контракта за "coming soon". Демка Bitcoin пока не умеет (подключается
|
|
550
|
+
// EVM-кошелёк), поэтому escrow: null - сеть в списке, выбрать её нельзя.
|
|
551
|
+
escrow: null,
|
|
552
|
+
settlement: "native-atomic-swap",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
id: "tron",
|
|
556
|
+
name: "Tron",
|
|
557
|
+
vm: "tron",
|
|
558
|
+
chainId: 728126428, // network id: eth_chainId на TronGrid отвечает 0x2b6653dc
|
|
559
|
+
caip2: "tron:0x2b6653dc",
|
|
560
|
+
native: { symbol: "TRX", decimals: 6, usd: 0.28 },
|
|
561
|
+
rpcUrl: "https://api.trongrid.io",
|
|
562
|
+
explorerTx: "https://tronscan.org/#/transaction/",
|
|
563
|
+
explorerAddr: "https://tronscan.org/#/address/",
|
|
564
|
+
explorerName: "tronscan",
|
|
565
|
+
gasUsd: 0.35,
|
|
566
|
+
routeType: "atomic",
|
|
567
|
+
// НЕ EVM: демка сегодня подключает только EVM-кошелёк (EIP-1193) и не умеет здесь ни читать
|
|
568
|
+
// балансы, ни подписывать. Поэтому сеть видна в списке, но не выбирается - escrow: null.
|
|
569
|
+
escrow: null,
|
|
570
|
+
tokens: [
|
|
571
|
+
// TRC-20: symbol()/decimals() проверены через TronGrid triggerconstantcontract (USDT, 6).
|
|
572
|
+
{ symbol: "USDT", address: "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
573
|
+
],
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
id: "solana",
|
|
577
|
+
name: "Solana",
|
|
578
|
+
vm: "solana",
|
|
579
|
+
chainId: 101, // идентификатор Solana mainnet-beta; ниже CAIP-2 с genesis hash
|
|
580
|
+
caip2: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d",
|
|
581
|
+
native: { symbol: "SOL", decimals: 9, usd: 198.4 },
|
|
582
|
+
rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
583
|
+
explorerTx: "https://solscan.io/tx/",
|
|
584
|
+
explorerAddr: "https://solscan.io/account/",
|
|
585
|
+
explorerName: "solscan",
|
|
586
|
+
gasUsd: 0.002,
|
|
587
|
+
routeType: "atomic",
|
|
588
|
+
// НЕ EVM: см. пояснение у Tron.
|
|
589
|
+
escrow: null,
|
|
590
|
+
tokens: [
|
|
591
|
+
// SPL-mint: проверено getAccountInfo (owner Tokenkeg...) и getTokenSupply (decimals 6).
|
|
592
|
+
{ symbol: "USDC", address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
593
|
+
{ symbol: "USDT", address: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
594
|
+
],
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
id: "monad",
|
|
598
|
+
name: "Monad",
|
|
599
|
+
vm: "evm",
|
|
600
|
+
// Демка эту сеть пока не умеет: в UI ведёт себя как Solana - серая и не выбирается,
|
|
601
|
+
// а в строке честно написано "not supported yet". План сети не отменён - снимем признак,
|
|
602
|
+
// когда появится контракт и токены.
|
|
603
|
+
unsupported: true,
|
|
604
|
+
chainId: 143,
|
|
605
|
+
caip2: "eip155:143",
|
|
606
|
+
native: { symbol: "MON", decimals: 18, usd: 0.03 },
|
|
607
|
+
rpcUrl: "https://rpc.monad.xyz",
|
|
608
|
+
explorerTx: "https://monadscan.com/tx/",
|
|
609
|
+
explorerAddr: "https://monadscan.com/address/",
|
|
610
|
+
explorerName: "monadscan",
|
|
611
|
+
gasUsd: 0.01,
|
|
612
|
+
routeType: "atomic",
|
|
613
|
+
escrow: { address: null, mode: "simulated" },
|
|
614
|
+
tokens: [
|
|
615
|
+
// USDC (Circle) и USDT0: symbol()/decimals() проверены вызовом в сети Monad (chainId 143).
|
|
616
|
+
{ symbol: "USDC", address: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
617
|
+
{ symbol: "USDT", onChainSymbol: "USDT0", address: "0xe7cd86e13AC4309349F30B3435a9d337750fC82D", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
618
|
+
],
|
|
619
|
+
// DEX-НОГИ ЗДЕСЬ НЕТ: сеть в статусе coming soon, пулы не ищутся и адреса в реестр не пишутся.
|
|
620
|
+
// Незачем держать в интерфейсе адрес, которым нельзя пользоваться: то, что мы измерили раньше,
|
|
621
|
+
// после такого решения просто не проверяется, и оставлять это как "данные" было бы обманом.
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: "hyperevm",
|
|
625
|
+
name: "Hyperliquid EVM",
|
|
626
|
+
vm: "evm",
|
|
627
|
+
// Демка эту сеть пока не умеет: в UI ведёт себя как Solana - серая и не выбирается,
|
|
628
|
+
// а в строке честно написано "not supported yet". План сети не отменён - снимем признак,
|
|
629
|
+
// когда появится контракт и токены.
|
|
630
|
+
unsupported: true,
|
|
631
|
+
chainId: 999,
|
|
632
|
+
caip2: "eip155:999",
|
|
633
|
+
native: { symbol: "HYPE", decimals: 18, usd: 38.5 },
|
|
634
|
+
rpcUrl: "https://rpc.hyperliquid.xyz/evm",
|
|
635
|
+
explorerTx: "https://hyperevmscan.io/tx/",
|
|
636
|
+
explorerAddr: "https://hyperevmscan.io/address/",
|
|
637
|
+
explorerName: "hyperevmscan",
|
|
638
|
+
gasUsd: 0.01,
|
|
639
|
+
routeType: "atomic",
|
|
640
|
+
escrow: { address: null, mode: "simulated" },
|
|
641
|
+
tokens: [
|
|
642
|
+
// USDC и USDT0 (на контракте symbol = "USD₮0"): проверено вызовами в самой сети (chainId 999).
|
|
643
|
+
{ symbol: "USDC", address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f", decimals: 6, usd: 1, cls: "ar-token-usdc" },
|
|
644
|
+
{ symbol: "USDT", onChainSymbol: "USD₮0", address: "0xB8CE59FC3717ada4C02eaDF9682A9e934F625ebb", decimals: 6, usd: 1, cls: "ar-token-usdt" },
|
|
645
|
+
],
|
|
646
|
+
// DEX-НОГИ ЗДЕСЬ НЕТ: сеть в статусе coming soon, пулы не ищутся и адреса в реестр не пишутся.
|
|
647
|
+
},
|
|
648
|
+
];
|
|
649
|
+
|
|
650
|
+
export const DEFAULT_CHAIN = "arbitrum-sepolia";
|
|
651
|
+
|
|
652
|
+
// СПИСОК ДОПУЩЕННЫХ ПРОВАЙДЕРОВ - ПОЛИТИКА ИНТЕРФЕЙСА, А НЕ ПРОВЕРКА ЦЕПИ (#76/#77).
|
|
653
|
+
//
|
|
654
|
+
// Цепь отвечает на ДРУГОЙ вопрос: чей ключ подписал котировку (реестр мейкеров, providerOf - его читает
|
|
655
|
+
// ядро, sdk/src/quotes.mjs). ЭТОТ список - решение интерфейса о том, с кем он вообще торгует. Провайдер,
|
|
656
|
+
// чей адрес подтверждён цепью, но не назван здесь, не проходит (код `quote-provider-not-allowed`), и это
|
|
657
|
+
// видно ЧЕЛОВЕКУ до подписи: комиссию показывает котировка, а её выдача не состоится.
|
|
658
|
+
//
|
|
659
|
+
// ПОЧЕМУ ОТДЕЛЬНЫМ СПИСКОМ, А НЕ ЧТЕНИЕМ ИЗ БЕКЕНДА. Отбор - дело интерфейса: он меняется здесь, без
|
|
660
|
+
// передеплоя контрактов и без правок у чужого узла. Такой же список есть и у бекенда
|
|
661
|
+
// (`ARRAKIS_ALLOWED_PROVIDERS`, app/app.env) - это ДВЕ независимые ступени одного решения, а не копия:
|
|
662
|
+
// страница защищает себя сама, даже если сервис настроен иначе.
|
|
663
|
+
//
|
|
664
|
+
// АДРЕСА - в нижнем регистре, как их отдаёт котировка (поле `provider`). Сеть без записи здесь означает
|
|
665
|
+
// "список не задан" (политика не применяется), а НЕ "никого не пускать": иначе сеть без настройки встала бы
|
|
666
|
+
// целиком. Заполнять список для сети нужно тогда, когда там зарегистрирован мейкер.
|
|
667
|
+
export const ALLOWED_PROVIDERS = {
|
|
668
|
+
"local-anvil": ["0x60af2ad4963c738aa6ac16d6cb0af1982508ba21"],
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
// Список допущенных для сети - или null, если он не задан (политики нет). Ядро принимает и массив, и
|
|
672
|
+
// предикат; здесь отдаётся массив в нижнем регистре: список - это то, что видно и меняется в одном месте.
|
|
673
|
+
export function allowedProvidersFor(chainOrId) {
|
|
674
|
+
const id = typeof chainOrId === "string" ? chainOrId : chainOrId && chainOrId.id;
|
|
675
|
+
const list = ALLOWED_PROVIDERS[id];
|
|
676
|
+
if (!Array.isArray(list) || list.length === 0) return null;
|
|
677
|
+
return list.map((a) => String(a).toLowerCase());
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// СПИСОК ИЗВЕСТНЫХ СБОРОК ФАБРИКИ - ПОЛИТИКА ИНТЕРФЕЙСА, РЯДОМ С allowedProviders (#103).
|
|
681
|
+
// Значение - extcodehash (keccak256 кода) фабрики, которую интерфейс готов принять: фабрика приходит из
|
|
682
|
+
// ЗАПИСИ РЕЕСТРА провайдера, а её код сверяется с этим списком. Пусто для сети = политики нет (фабрика всё
|
|
683
|
+
// равно обязана быть названа записью и совпасть с подписанной котировкой). Заполняется при развёртывании:
|
|
684
|
+
// у фабрики нет immutable-полей, поэтому одна и та же сборка даёт один и тот же хеш у любого, кто её
|
|
685
|
+
// развернул, - своей фабрикой провайдер попадает в тот же список.
|
|
686
|
+
export const KNOWN_FACTORY_CODES = {};
|
|
687
|
+
|
|
688
|
+
export function knownFactoryCodesFor(chainOrId) {
|
|
689
|
+
const id = typeof chainOrId === "string" ? chainOrId : chainOrId && chainOrId.id;
|
|
690
|
+
const list = KNOWN_FACTORY_CODES[id];
|
|
691
|
+
if (!Array.isArray(list) || list.length === 0) return null;
|
|
692
|
+
return list.map((h) => String(h).toLowerCase());
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
export function chainById(id) {
|
|
696
|
+
return CHAINS.find((c) => c.id === id) || CHAINS.find((c) => c.id === DEFAULT_CHAIN) || CHAINS[0];
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
export function chainByChainId(chainId) {
|
|
700
|
+
return CHAINS.find((c) => c.chainId === Number(chainId)) || null;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Принимаемые токены сети: нативный первым, затем контрактные.
|
|
704
|
+
export function tokensOf(chainId) {
|
|
705
|
+
const c = chainById(chainId);
|
|
706
|
+
return [nativeOf(chainId), ...c.tokens];
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
export function nativeOf(chainId) {
|
|
710
|
+
const c = chainById(chainId);
|
|
711
|
+
return {
|
|
712
|
+
symbol: c.native.symbol,
|
|
713
|
+
label: c.native.symbol,
|
|
714
|
+
address: null,
|
|
715
|
+
decimals: c.native.decimals,
|
|
716
|
+
usd: c.native.usd,
|
|
717
|
+
cls: "ar-token-eth",
|
|
718
|
+
native: true,
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Токен по составному идентификатору "<chainId>:<SYMBOL>" либо по паре аргументов.
|
|
723
|
+
export function tokenById(idOrChainId, symbolMaybe) {
|
|
724
|
+
let chainId = idOrChainId;
|
|
725
|
+
let symbol = symbolMaybe;
|
|
726
|
+
if (typeof idOrChainId === "string" && idOrChainId.includes(":")) {
|
|
727
|
+
[chainId, symbol] = idOrChainId.split(":");
|
|
728
|
+
}
|
|
729
|
+
const list = tokensOf(chainId);
|
|
730
|
+
return list.find((t) => t.symbol === String(symbol).toUpperCase()) || list[0];
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
// Monero-сеть демки.
|
|
735
|
+
// networkType=mainnet - потому что monero-js (вендоренная библиотека мнемоник/ключей/адресов)
|
|
736
|
+
// поддерживает только mainnet-префиксы адресов (см. www/assets/vendors/monero-js/README.md).
|
|
737
|
+
// Режим monero-ts (WebAssembly wallet2) умеет и stagenet.
|
|
738
|
+
// Получатель ордера (claimer) для ТЕСТОВ на тестовых сетях.
|
|
739
|
+
//
|
|
740
|
+
// ВАЖНО: контракт требует, чтобы locker и claimer различались, поэтому адрес оплаты здесь указывать
|
|
741
|
+
// нельзя - транзакция будет отклонена. Платит подключённый кошелёк (кто угодно), а забирает средства тот,
|
|
742
|
+
// кто записан здесь, предъявив секрет ордера.
|
|
743
|
+
// Это ЗАГЛУШКА для испытаний: в бою адрес получателя приходит от RFQ-узла мейкера вместе с котировкой.
|
|
744
|
+
export const TEST_CLAIMER = "0xc14C7841C7d6350859474dD6DF8dC9672047A6A0";
|
|
745
|
+
|
|
746
|
+
export const MONERO = {
|
|
747
|
+
// Сеть, в которой демка генерирует свой демо-кошелёк и адрес получателя. Обязана совпадать с
|
|
748
|
+
// XMR_NETWORKS (ниже) и с той цепью, к которой подключена нода: адрес чужой сети в поле
|
|
749
|
+
// получателя означает отправленные в никуда XMR. Совпадение проверяет npm test.
|
|
750
|
+
networkType: "stagenet",
|
|
751
|
+
confirmTarget: 10, // подтверждений до claim (пример из litepaper §5.0)
|
|
752
|
+
blockTimeSim: 120_000, // 2 минуты на блок, в sim-времени
|
|
753
|
+
blockTimeReal: 120_000, // реальный target Monero mainnet
|
|
754
|
+
// Нода Monero. Адрес задан прямо здесь и больше нигде: параметров у демки нет
|
|
755
|
+
// (ни ?rpc=, ни localStorage) и переключений RPC не предполагается - меняется строкой в коде.
|
|
756
|
+
// сегодня в публичную, дальше в свою - для демки разницы нет.
|
|
757
|
+
// сегодня в публичную, дальше в свою - для демки разницы нет.
|
|
758
|
+
// Нода нужна только для статуса сети и режима live-подтверждений; ключи, балансы и выводы
|
|
759
|
+
// через неё не запрашиваются.
|
|
760
|
+
// АДРЕС НОДЫ. В бою он один и виден в коде (.hermes/deploy/arrakis-xmr-rpc.conf). НА ЛОКАЛЬНОМ СТЕНДЕ
|
|
761
|
+
// страница обязана ходить на НАШУ ноду: боевой прокси может быть недоступен (502), а высота сети - один из
|
|
762
|
+
// источников высоты скана для файла восстановления, то есть без ноды файл уходит владельцу без высоты.
|
|
763
|
+
// Подстановку делает только стенд через окно; в бою window.ARRAKIS_XMR_NODE никто не ставит.
|
|
764
|
+
node: (typeof window !== "undefined" && window.ARRAKIS_XMR_NODE) || "https://xmr.arrakisswap.trade",
|
|
765
|
+
nodePollMs: 30_000,
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// ---------------------------------------------------------------------------
|
|
769
|
+
// Бэкенд (app/): сессии свопов и наблюдение за Monero-ногой.
|
|
770
|
+
//
|
|
771
|
+
// API живёт на том же домене по пути /api/ (Angie проксирует на 127.0.0.1:8787) - поэтому CSP
|
|
772
|
+
// править не нужно, его покрывает connect-src 'self'. Локально тот же путь отдаёт serve.mjs
|
|
773
|
+
// с флагом --api=http://127.0.0.1:8787 (см. tools/serve.mjs) - тоже без правки политики.
|
|
774
|
+
// ---------------------------------------------------------------------------
|
|
775
|
+
export const API = {
|
|
776
|
+
base: "/api",
|
|
777
|
+
// Источник Monero-ноги: 'app' - настоящий бэкенд по HTTP, 'mock' - симулятор демки без сети.
|
|
778
|
+
xmrSource: "app",
|
|
779
|
+
pollMs: 5_000, // как часто фронт опрашивает сессию свопа
|
|
780
|
+
// СКОЛЬКО ПРОВАЙДЕРОВ ПОКАЗЫВАТЬ в блоке цены. Список из десятков строк не читается, а сравнить больше
|
|
781
|
+
// пяти цен человек всё равно не сможет - он выбирает лучшую. Остальные не скрываются молча: об их
|
|
782
|
+
// существовании говорит строка-итог ("и ещё N").
|
|
783
|
+
maxProviders: 5,
|
|
784
|
+
timeoutMs: 60_000, // Таймаут одного запроса: зависший бэкенд не должен подвешивать экран. 60 с, а не 10: создание
|
|
785
|
+
// кошелька сделки - единственный ДОЛГИЙ путь, он сканирует цепочку от высоты восстановления, и
|
|
786
|
+
// прежние 10 с обрывали РАБОТАЮЩИЙ запрос. Снаружи это выглядело как «Swap wallet unavailable».
|
|
787
|
+
// Срок поднимали дважды (8 -> 10 -> 60): здесь короткий таймаут не защита, а ложный отказ.
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// Сеть Monero для каждого источника. Бэкенд на сервере работает со stagenet-кошельком
|
|
791
|
+
// (monero-wallet-rpc поднят на stagenet-ноде), поэтому в режиме 'app' адрес свопа обязан быть
|
|
792
|
+
// stagenet-адресом: иначе wallet-rpc не сможет создать по нему watch-only кошелёк.
|
|
793
|
+
// Мок-режим демки исторически mainnet - адреса из демо-примеров.
|
|
794
|
+
// Одна цепь на всю демку. Раньше мок-режим притворялся mainnet (демка была исторически mainnet),
|
|
795
|
+
// но это только вредило: адреса в мок-режиме выглядели как mainnet (префикс 4), а живая нода -
|
|
796
|
+
// stagenet (префикс 5), и в одном UI соседствовали две цепи. Смешение цепей в свопе означает
|
|
797
|
+
// потерянные деньги (см. monero/address.js), поэтому теперь и мок, и живой режим - stagenet.
|
|
798
|
+
export const XMR_NETWORKS = { app: "stagenet", mock: "stagenet" };
|
|
799
|
+
|
|
800
|
+
// Страница sweep: отдельный домен со своей (более строгой) политикой и WASM-кошельком.
|
|
801
|
+
// Вывод XMR делает сам пользователь там, по своему recovery-файлу - демка за него не подписывает
|
|
802
|
+
// (см. .hermes/docs/09-sweep-origin.md).
|
|
803
|
+
// Куда ведёт ссылка на страницу вывода. В бою - боевой домен, на локальном стенде - его собственный адрес
|
|
804
|
+
// (там своя нода и свой бэкенд). Отдельная функция, чтобы правило было одним: ссылка и то, где страница
|
|
805
|
+
// действительно поднята, не должны расходиться.
|
|
806
|
+
export function sweepOrigin() {
|
|
807
|
+
const h = typeof location !== "undefined" ? location.hostname : "";
|
|
808
|
+
return /^(localhost|127\.0\.0\.1|\[::1\])$/.test(h) || /\.local$/.test(h) ? SWEEP.localOrigin : SWEEP.origin;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
export const SWEEP = {
|
|
812
|
+
origin: "https://sweep.arrakisswap.trade",
|
|
813
|
+
localOrigin: "http://localhost:5195",
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
// ---------------------------------------------------------------------------
|
|
817
|
+
// EVM-кошелёк: WalletConnect (Reown).
|
|
818
|
+
// projectId - публичный идентификатор приложения, его видит любой сайт с WalletConnect
|
|
819
|
+
// (это не секрет: он уходит в QR и в relay). Метаданные показываются пользователю в кошельке.
|
|
820
|
+
// ---------------------------------------------------------------------------
|
|
821
|
+
export const EVM = {
|
|
822
|
+
projectId: "18c13c05b6c0ab011ffff7cee0700e2d",
|
|
823
|
+
metadata: {
|
|
824
|
+
name: "ArrakisSwap",
|
|
825
|
+
description: "Non-custodial XMR <-> EVM atomic swap. Demo build: real wallet, real balances, no real funds move.",
|
|
826
|
+
url: "https://arrakisswap.trade",
|
|
827
|
+
icons: [],
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
// СТАВКИ КОМИССИИ ЗДЕСЬ НЕТ - И НЕ ДОЛЖНО БЫТЬ. Единственный источник правды: ПОДПИСАННАЯ КОТИРОВКА.
|
|
832
|
+
// Ставка, величина и получатель комиссии - часть условий ордера (решение #76); функций feeBps()/feeFor() у
|
|
833
|
+
// фабрики больше нет (роутер удалён), и узел не сверяет свою настройку с цепью - он её ПОДПИСЫВАЕТ, а
|
|
834
|
+
// величина приходит в котировке и сверяется зеркалом формулы (www/js/evm/fees.js, rfq/fundEscrow.mjs).
|
|
835
|
+
// Копия в конфиге разошлась бы с подписанным набором - и выяснилось бы это не здесь, а на подписи.
|
|
836
|
+
// Запись со ставкой (0.003) удалена 28 сентября 2026; переход на котировку - этап #76 (комиссия в ордере).
|
|
837
|
+
|
|
838
|
+
// Мок-мейкеры. Спред - то, что мейкер забирает себе; комиссия Arrakis считается отдельно.
|
|
839
|
+
// СПИСОК ВЫДУМАННЫХ МЕЙКЕРОВ УДАЛЁН. Раньше здесь лежали Maker A/B/C с надбавкой, аптаймом, запасом и
|
|
840
|
+
// задержкой - и всё это уходило в интерфейс как факты. Аптайм чужого сервиса мы не измеряем, запас не
|
|
841
|
+
// знаем, а надбавку провайдер формирует сам и показывает её только в своей цене. Теперь котировки берутся
|
|
842
|
+
// у провайдера через бекенд (/api/rate), и выдумывать рядом с ними нечего.
|
|
843
|
+
|
|
844
|
+
// Тайминги. Все в sim-миллисекундах (см. js/core/clock.js): при speed=60 одна
|
|
845
|
+
// реальная секунда равна одной sim-минуте.
|
|
846
|
+
export const TIMING = {
|
|
847
|
+
quoteValid: 12_000, // срок жизни котировки (12 сек в sim)
|
|
848
|
+
quoteRefresh: 5_000,
|
|
849
|
+
fundingMined: 6_000, // tx фондирования попала в блок
|
|
850
|
+
makerLock: 30_000, // мейкер лочит XMR
|
|
851
|
+
xmrConfirm: MONERO.blockTimeSim,
|
|
852
|
+
readyWindow: 60 * 60_000, // сколько у пользователя есть на ready после лока
|
|
853
|
+
makerClaim: 45_000,
|
|
854
|
+
sweep: 30_000,
|
|
855
|
+
t0Refund: 20 * 60_000, // после этого можно вернуть ETH, если мейкер не залочил XMR
|
|
856
|
+
t1Refund: 40 * 60_000, // после этого можно вернуть ETH, если мейкер не сделал claim
|
|
857
|
+
watchtowerGrace: 5 * 60_000, // watchtower возвращает деньги, если пользователь ушёл
|
|
858
|
+
makerReclaim: 10 * 60_000, // мейкер забирает XMR, если пользователь не сделал ready
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
export const DEMO = {
|
|
862
|
+
speeds: [1, 20, 60, 300],
|
|
863
|
+
defaultSpeed: 60,
|
|
864
|
+
scenarios: ["happy", "maker_no_lock", "user_no_ready", "maker_stall"],
|
|
865
|
+
defaultScenario: "happy",
|
|
866
|
+
// Режима кошелька больше нет: ключи в демке считает monero-js, и это единственный путь
|
|
867
|
+
// (WASM-кошелёк monero-ts переехал на отдельный домен sweep - своя политика и COOP/COEP).
|
|
868
|
+
// mock - подтверждения по sim-таймеру; live - по реальной высоте Monero mainnet
|
|
869
|
+
chainModes: ["mock", "live"],
|
|
870
|
+
// ЖИВАЯ ВЫСОТА ПО УМОЛЧАНИЮ. "mock" означает подтверждения по таймеру песочницы - это демонстрационный
|
|
871
|
+
// режим, и он больше не подставляется сам: сделка обязана считаться по настоящей высоте цепи, иначе
|
|
872
|
+
// «подтверждено» на экране ничего не значит. Демонстрационный режим остался выбором, а не умолчанием.
|
|
873
|
+
defaultChainMode: "live",
|
|
874
|
+
// Источник Monero-ноги: 'app' - настоящий бэкенд (см. API.xmrSource), 'mock' - симулятор.
|
|
875
|
+
xmrSources: ["app", "mock"],
|
|
876
|
+
defaultXmrSource: "app",
|
|
877
|
+
defaultChain: DEFAULT_CHAIN,
|
|
878
|
+
};
|
|
879
|
+
|
|
880
|
+
export const SCENARIO_LABELS = {
|
|
881
|
+
happy: "happy path",
|
|
882
|
+
maker_no_lock: "maker never locks XMR",
|
|
883
|
+
user_no_ready: "user never confirms",
|
|
884
|
+
maker_stall: "maker stalls after ready",
|
|
885
|
+
};
|