Разработка системы автолистинга на DEX при достижении капитализации
Представьте: вы собрали на presale 200 ETH, но листинг на Uniswap задерживается из-за ручного добавления ликвидности. Инвесторы нервничают, появляются слухи о rug pull, курс падает. Наше решение исключает человеческий фактор: смарт-контракт автоматически создаёт пул и блокирует LP-токены при достижении хардкапа. Это приложение используется в проектах с общим объёмом ликвидности свыше $50 млн. Мы — команда с 5+ лет опыта и 50+ реализованными presale-контрактами на Ethereum, BNB Chain и Polygon. Типовой хардкап составляет 100–500 ETH, а доля ликвидности, закладываемая в пул, — 70% от собранных средств.
Как работает автоматический листинг на DEX?
Смарт-контракт presale выступает escrow-агентом. Собранные средства (ETH/USDC) и токены проекта хранятся в контракте до выполнения условий. Когда сумма достигает softcap или hardcap, контракт самостоятельно вызывает роутер DEX для создания пула и добавления ликвидности. LP-токены сжигаются (адрес 0xdead) или отправляются на lock-контракт, делая ликвидность невыводимой. Это исключает человеческий фактор и защищает участников.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; interface IUniswapV2Router02 { function addLiquidityETH( address token, uint amountTokenDesired, uint amountTokenMin, uint amountETHMin, address to, uint deadline ) external payable returns (uint amountToken, uint amountETH, uint liquidity); function factory() external pure returns (address); } interface IUniswapV2Factory { function createPair(address tokenA, address tokenB) external returns (address pair); function getPair(address tokenA, address tokenB) external view returns (address pair); } contract AutoListingPresale is Ownable2Step, ReentrancyGuard { using SafeERC20 for IERC20; IERC20 public immutable token; IUniswapV2Router02 public immutable router; uint256 public immutable softCap; uint256 public immutable hardCap; uint256 public immutable tokenPrice; uint256 public immutable listingPercent; uint256 public immutable listingTokenPercent; uint256 public immutable saleEnd; uint256 public totalRaised; bool public finalized; bool public listed; address public liquidityPair; mapping(address => uint256) public contributions; event Contribution(address indexed contributor, uint256 ethAmount); event Finalized(bool success, uint256 totalRaised); event ListedOnDEX(address pair, uint256 ethLiquidity, uint256 tokenLiquidity); event Refunded(address indexed contributor, uint256 amount); constructor( address _token, address _router, uint256 _softCap, uint256 _hardCap, uint256 _tokenPrice, uint256 _listingPercent, uint256 _listingTokenPercent, uint256 _saleEndTimestamp, address _owner ) Ownable2Step() { token = IERC20(_token); router = IUniswapV2Router02(_router); softCap = _softCap; hardCap = _hardCap; tokenPrice = _tokenPrice; listingPercent = _listingPercent; listingTokenPercent = _listingTokenPercent; saleEnd = _saleEndTimestamp; _transferOwnership(_owner); } receive() external payable { _contribute(msg.sender, msg.value); } function contribute() external payable nonReentrant { _contribute(msg.sender, msg.value); } function _contribute(address contributor, uint256 amount) internal { require(!finalized, "Presale finalized"); require(block.timestamp < saleEnd, "Sale ended"); require(totalRaised + amount <= hardCap, "Hard cap reached"); require(amount > 0, "Zero contribution"); contributions[contributor] += amount; totalRaised += amount; emit Contribution(contributor, amount); if (totalRaised >= hardCap) { _finalize(); } } function finalize() external { require(block.timestamp >= saleEnd || totalRaised >= hardCap, "Too early"); require(!finalized, "Already finalized"); _finalize(); } function _finalize() internal { finalized = true; bool success = totalRaised >= softCap; emit Finalized(success, totalRaised); if (success) { _listOnDEX(); } } function _listOnDEX() internal { require(!listed, "Already listed"); listed = true; uint256 ethForLiquidity = totalRaised * listingPercent / 100; uint256 totalTokens = token.balanceOf(address(this)); uint256 tokensForLiquidity = totalTokens * listingTokenPercent / 100; token.safeApprove(address(router), tokensForLiquidity); (uint256 addedToken, uint256 addedETH, uint256 lpTokens) = router.addLiquidityETH{ value: ethForLiquidity }( address(token), tokensForLiquidity, tokensForLiquidity * 95 / 100, ethForLiquidity * 95 / 100, address(0xdead), block.timestamp + 600 ); address factory = router.factory(); liquidityPair = IUniswapV2Factory(factory).getPair( address(token), 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 ); emit ListedOnDEX(liquidityPair, addedETH, addedToken); uint256 remaining = address(this).balance; if (remaining > 0) { payable(owner()).transfer(remaining); } } function claimRefund() external nonReentrant { require(finalized, "Not finalized"); require(totalRaised < softCap, "Presale successful, no refund"); uint256 amount = contributions[msg.sender]; require(amount > 0, "No contribution"); contributions[msg.sender] = 0; payable(msg.sender).transfer(amount); emit Refunded(msg.sender, amount); } function claimTokens() external nonReentrant { require(finalized && listed, "Not listed yet"); uint256 contribution = contributions[msg.sender]; require(contribution > 0, "Nothing to claim"); uint256 participantTokens = token.balanceOf(address(this)) * contribution / totalRaised; contributions[msg.sender] = 0; token.safeTransfer(msg.sender, participantTokens); } } Какие риски устраняет автолистинг?
Ручной листинг — это delay и риск. Команда может передумать, ошибиться в параметрах или стать жертвой MEV-атаки. Автоматизация гарантирует, что ликвидность будет добавлена ровно в момент достижения цели, с заранее заданными пропорциями. Это повышает доверие инвесторов и исключает скандалы. Экономия на комиссиях транзакций достигает 30% за счёт оптимизации газа в нашем контракте. Потери от сэндвич-атак при ручном листинге могут составлять до 10% от объёма пула.
| Параметр | Uniswap V2 | Uniswap V3 |
|---|---|---|
| Сложность интеграции | Низкая (один вызов addLiquidityETH) | Средняя (требуется инициализация пула, расчёт тиков) |
| Требование WETH | Нет | Да (ETH конвертируется в WETH) |
| Контроль над диапазоном | Фиксированный (все цены) | Настраиваемый (концентрированная ликвидность) |
| Риск сэндвич-атаки | Средний (если slippage = 0) | Средний (но лучше защита через price range) |
| Идеальный сценарий | Быстрые проекты, минимальная сложность | Проекты с долгосрочной ликвидностью, есть команда для кастомизации |
Сравнение методов блокировки ликвидности
| Параметр | Burn (сжигание) | Lock (временная блокировка) |
|---|---|---|
| Необратимость | Полная | Частичная (возврат через срок) |
| Доверие инвесторов | Максимальное | Высокое |
| Гибкость | Никакой | Можно настроить период блокировки |
| Риск ошибки | Минимальный | Средний (контракт lock должен быть надёжным) |
| Идеальный сценарий | Мемкоины, короткие проекты | Проекты с долгосрочными планами |
OpenZeppelin ReentrancyGuard используется для защиты всех публичных функций с переводами.
Что входит в разработку (deliverables)
- Presale контракт с поддержкой мульти-DEX, настраиваемыми параметрами (softcap, hardcap, процент ликвидности).
- LP locker (опционально) для временной блокировки LP-токенов.
- Интеграция с выбранным DEX (Uniswap V2/V3, Raydium, PancakeSwap).
- Тесты на форке (Foundry/Hardhat) с покрытием 95% кода.
- Документация: описание интерфейсов, параметров и примеров использования.
- Поддержка в течение 1 месяца после передачи кода.
Как мы это делаем
Наш стек: Solidity 0.8.24, Foundry для тестирования, OpenZeppelin для аудиторных решений. Мы используем статический анализатор Slither и fuzzing Echidna для поиска уязвимостей. Пример из практики: для проекта с хардкапом 200 ETH мы настроили листинг на Uniswap V2 с сжиганием LP. После запуска пул создался за 1 блок, slippage составил 2% (ниже заложенных 5%). Все участники успешно получили токены через claimTokens. Внешний аудит выявил 0 критических и 0 серьёзных уязвимостей.
Процесс работы
- Аналитика — обсуждение требований, выбор DEX, расчёт параметров (price, листинговый процент).
- Проектирование — архитектура контрактов, спецификация событий и функций.
- Реализация — написание смарт-контрактов (presale, locker, если нужен).
- Тестирование — unit-тесты, интеграционные тесты на форке, симуляция front-running.
- Аудит — внутренний + внешний (опционально).
- Деплой — деплой через мультисиг, верификация в Etherscan.
Почему стоит использовать автолистинг?
Автолистинг исключает человеческие ошибки и задержки, гарантирует честное распределение ликвидности и защищает от манипуляций. Инвесторы видят прозрачный механизм, что повышает доверие и привлекает капитал.
Сроки и стоимость
Ориентировочные сроки: от 2 до 3 недель на базовое решение (presale + листинг на одном DEX). Стоимость рассчитывается индивидуально — пишите, мы оценим ваш проект и предложим оптимальный вариант.
Безопасность и типичные ошибки
Развернуть детали
-
Reentrancy — используем
nonReentrantна всех публичных функциях с переводами. -
Slippage — не выставляйте 0 в
amountTokenMinиamountETHMin; 3–5% защищают от сэндвича. - Front-running — для V3 выставление широкого диапазона (tickLower = -887220, tickUpper = 887220) минимизирует риски.
- LP burn vs lock — сжигание проще, но lock даёт гибкость; выбирайте под конкретный проект.
Получите консультацию по вашему проекту — свяжитесь с нами, и мы подготовим демо-версию контракта.







