Разработка системы делистинга токенов
Делистинг токена — это удаление торговой пары с биржи. Процесс требует чёткой процедуры: уведомление пользователей, финальный срок торговли, срок вывода средств и техническое закрытие. Плохо управляемый делистинг → пользователи теряют доступ к своим средствам или получают их с большой задержкой.
Причины делистинга
Технические причины:
- Уязвимость в смарт-контракте или протоколе
- Прекращение работы блокчейна / hard fork без поддержки
- Низкий объём торгов (неликвидный рынок)
Compliance причины:
- Проект идентифицирован как unregistered security
- OFAC санкции против команды или проекта
- Отказ команды от обязательных раскрытий
Коммерческие причины:
- Провал проекта / команда бросила разработку
- Rug pull или exit scam
- Нарушение условий листинг-соглашения
Процедура делистинга
T-30 дней: Внутреннее решение о делистинге
T-14 дней: Публичный анонс
├── Официальная страница поддержки
├── Email всем держателям токена
├── Push уведомления
└── Announcement banner в интерфейсе
T-7 дней: Напоминание, заморозка новых депозитов
T-0: Остановка торгов
├── Снятие всех открытых ордеров (return margin)
├── Закрытие торговой пары в matching engine
└── Финальный announcement
T+30 дней: Дедлайн вывода средств
└── Уведомление пользователям с балансами
T+60 дней: Принудительная ликвидация остатков
└── Средства замораживаются или конвертируются
Техническая реализация
Инициирование делистинга
class DelistingManager:
async def initiate_delisting(
self,
symbol: str,
reason: str,
admin_id: str,
urgent: bool = False
) -> DelistingPlan:
# Проверяем текущее состояние
token_info = await self.db.get_token(symbol)
affected_users = await self.db.count_users_with_balance(symbol)
# Для срочного делистинга (security incident) — сокращённые сроки
if urgent:
notice_days = 2
trading_stop_days = 3
withdrawal_days = 30
else:
notice_days = 14
trading_stop_days = 14
withdrawal_days = 60
plan = DelistingPlan(
symbol=symbol,
reason=reason,
affected_users=affected_users,
announced_at=datetime.utcnow(),
trading_stops_at=datetime.utcnow() + timedelta(days=notice_days),
deposits_stop_at=datetime.utcnow() + timedelta(days=notice_days - 7),
withdrawal_deadline=datetime.utcnow() + timedelta(days=notice_days + withdrawal_days),
initiated_by=admin_id
)
await self.db.save_delisting_plan(plan)
# Запускаем уведомления
await self.notification_service.announce_delisting(plan)
return plan
async def stop_trading(self, symbol: str):
"""Останавливаем торги по символу"""
# Снимаем все открытые ордера
open_orders = await self.db.get_open_orders_by_symbol(symbol)
for order in open_orders:
await self.matching_engine.cancel_order(order.id)
# Возвращаем зарезервированные средства
await self.balance_service.release_reserved(order.user_id, order)
# Уведомляем пользователя
await self.notify_order_cancelled(order, reason='delisting')
# Деактивируем пару в matching engine
await self.matching_engine.disable_symbol(symbol)
# Останавливаем депозиты и выводы нового (выводы ещё работают)
await self.db.update_token_status(symbol, 'delisting_in_progress')
logger.info(f"Trading stopped for {symbol}, {len(open_orders)} orders cancelled")
Принудительная ликвидация после дедлайна
async def process_expired_delisting(self, symbol: str):
"""Обработка истёкших балансов после deadline вывода"""
remaining_balances = await self.db.get_token_balances(symbol)
for user_id, amount in remaining_balances.items():
# Опции в зависимости от политики:
action = await self.determine_action(user_id, symbol, amount)
if action == 'auto_convert':
# Конвертируем в USDT по last price
last_price = await self.get_last_price(symbol)
usdt_amount = amount * last_price * Decimal('0.95') # 5% penalty
await self.balance_service.convert(user_id, symbol, 'USDT', usdt_amount)
elif action == 'freeze':
# Замораживаем с возможностью ручного вывода через support
await self.db.freeze_balance(user_id, symbol, amount)
await self.send_final_notice(user_id, symbol, amount, action)
Коммуникация с пользователями
Email уведомление
def generate_delisting_email(plan: DelistingPlan, user: User) -> EmailContent:
user_balance = get_user_balance(user.id, plan.symbol)
return EmailContent(
subject=f"Важно: {plan.symbol} будет делистирован с биржи",
body=f"""
Уважаемый {user.first_name},
Информируем вас о предстоящем делистинге токена **{plan.symbol}**.
**Причина:** {plan.reason}
**Ключевые даты:**
• Остановка депозитов: {plan.deposits_stop_at.strftime('%d.%m.%Y')}
• Остановка торгов: {plan.trading_stops_at.strftime('%d.%m.%Y')}
• Дедлайн вывода средств: {plan.withdrawal_deadline.strftime('%d.%m.%Y')}
**Ваш текущий баланс:** {user_balance} {plan.symbol}
**Рекомендуем до {plan.trading_stops_at.strftime('%d.%m.%Y')}:**
1. Вывести {plan.symbol} на внешний кошелёк, или
2. Обменять на другой актив через торговый терминал
После {plan.withdrawal_deadline.strftime('%d.%m.%Y')} оставшиеся балансы
будут обработаны согласно нашей политике.
С уважением,
Команда биржи
"""
)
Делистинг — болезненная процедура, но прозрачная коммуникация и достаточный срок вывода минимизируют ущерб для пользователей и репутации биржи. Стандарт индустрии: минимум 14 дней notice для некритичных делистингов.







