Разработка виджета обмена для встраивания на сайт
Embed swap — не просто «добавить кнопку обмена на сайт». Это полноценный интерфейс обмена токенов, который должен работать в чужом DOM, управлять состоянием кошелька, обрабатывать транзакции — и при этом быть изолированным от хост-страницы. 1inch, Uniswap, Paraswap предлагают свои embed-виджеты, но кастомная разработка нужна, когда требуется брендинг, специфичные токены, комиссия в пользу хоста, или конкретный aggregation routing.
Архитектура виджета
iframe vs. Web Component vs. npm-пакет
iframe — самый изолированный вариант. Виджет — отдельная страница, встраивается через <iframe src="https://swap.yourprotocol.com">. Хост-страница не имеет доступа к DOM виджета, XSS на хосте не достанет до виджета. Минус: коммуникация только через postMessage, кастомизация стилей ограничена CSS-переменными через параметры URL.
Web Component — лучший баланс изоляции и интеграции. Shadow DOM изолирует стили, но компонент живёт в том же JavaScript-контексте. Позволяет хост-странице передавать конфиг через атрибуты и реагировать на события:
<swap-widget
tokens="ETH,USDC,USDT"
default-input="ETH"
default-output="USDC"
fee-bps="30"
theme="dark"
></swap-widget>
npm-пакет — максимальная гибкость для разработчиков-интеграторов. Экспортирует React-компонент (или headless логику) для встраивания в существующий React-проект.
Рекомендуемый подход: Web Component как основная форма встраивания + npm-пакет для React-проектов. iframe — только если security requirements требуют полной изоляции.
Routing и aggregation
Собственный DEX vs. aggregator
Виджет для одного протокола (например, только Uniswap v3) — проще: прямые вызовы контракта, знакомый routing. Но пользователь видит только цены одного AMM.
Aggregation через 1inch API, Paraswap API или 0x API даёт лучшие цены. API запрос возвращает оптимальный route + calldata для транзакции:
const quote = await fetch(
`https://api.1inch.dev/swap/v6.0/1/swap?` +
`src=${inputToken}&dst=${outputToken}&amount=${amount}&from=${userAddress}&slippage=1`
).then(r => r.json());
// quote.tx содержит готовую транзакцию для отправки
await writeContractAsync({
to: quote.tx.to,
data: quote.tx.data,
value: BigInt(quote.tx.value),
});
Referral fees
Большинство aggregator API поддерживают referral fees: вы указываете свой адрес и bps, при каждом swap часть комиссии идёт вам. 1inch: параметр fee в API. 0x: affiliateAddress + buyTokenPercentageFee. Это не влияет на смарт-контракт виджета — только на routing calldata.
Ключевые UX компоненты
Token selector
Список токенов с поиском по названию/символу/адресу. Токенлисты: Uniswap default tokenlist, 1inch tokenlist, или кастомный. Загрузка балансов для каждого токена через Multicall3 — один запрос вместо N.
Верификация токенов (verified/unverified) — защита от scam токенов. Предупреждение для токенов не из trusted списка.
Price display и slippage
Цена обмена, price impact (особенно важен для больших сумм), slippage tolerance (обычно 0.1-1%, для volatile пар — выше). Показывать minimum received после slippage — это то, что гарантирует контракт.
Автоматическое обновление котировки каждые 15-30 секунд. Индикатор «цена обновлена» при изменении более чем на X%.
Approve flow
До первого swap ERC-20 токена нужен approve. Permit (EIP-2612) позволяет объединить approve + swap в одну транзакцию (через off-chain подпись). Проверять, поддерживает ли токен Permit через DOMAIN_SEPARATOR() или ERC-165.
Встраивание и конфигурация
// Инициализация через script tag
<script src="https://cdn.yourprotocol.com/swap-widget.js"></script>
<swap-widget
rpc-url="https://mainnet.infura.io/v3/KEY"
chain-id="1"
tokens="0x...,0x..."
theme='{"primaryColor":"#6366f1","borderRadius":"12px"}'
></swap-widget>
// Или через npm
import { SwapWidget } from '@yourprotocol/swap-widget';
<SwapWidget onSwapSuccess={(tx) => analytics.track('swap', tx)} />
Параметры конфигурации: список токенов, дефолтный input/output, тема (light/dark + CSS переменные), referral address + fee bps, slippage defaults, отключение конкретных features.
Изоляция и безопасность
Content Security Policy хост-страницы может блокировать inline scripts виджета. Документировать необходимые CSP директивы (connect-src, script-src) для интеграторов.
Wallet connection в виджете: если хост-страница уже подключила кошелёк, виджет должен переиспользовать соединение. Для Web Component это реализуется через shared window.ethereum provider или кастомный event bus.
Никакого хранения приватных ключей, никакого доступа к localStorage хост-страницы (Shadow DOM изолирует это автоматически).
Ориентиры по срокам
Виджет с фиксированным aggregator API (1inch/0x), Web Component упаковка, базовая кастомизация: 3 дня. С кастомным routing, permit support, iframe + npm вариантами, полной документацией для интеграторов: 5 дней.







