Разработка системы автолистинга на 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 даёт гибкость; выбирайте под конкретный проект.
Получите консультацию по вашему проекту — свяжитесь с нами, и мы подготовим демо-версию контракта.







