Автоматичний лістинг токена на 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);
}
}
Які ризики усуває автолістинг?
Ручний лістинг — це затримка та ризик. Команда може передумати, помилитися в параметрах або стати жертвою 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 дає гнучкість; обирайте під конкретний проект.
Отримайте консультацію щодо вашого проекту — зв'яжіться з нами, і ми підготуємо демо-версію контракту.







