Token Delisting System Development
Token delisting is removing a trading pair from an exchange. The process requires clear procedure: user notification, final trading deadline, withdrawal deadline, and technical closure. Poorly managed delisting causes users to lose access to funds or get them with large delays.
Reasons for Delisting
Technical reasons:
- Vulnerability in smart contract or protocol
- Blockchain shutdown / hard fork without support
- Low trading volume (illiquid market)
Compliance reasons:
- Project identified as unregistered security
- OFAC sanctions against team or project
- Team's refusal to provide mandatory disclosures
Commercial reasons:
- Project failure / team abandoned development
- Rug pull or exit scam
- Breach of listing agreement terms
Delisting Procedure
T-30 days: Internal decision on delisting
T-14 days: Public announcement
├── Official support page
├── Email to all token holders
├── Push notifications
└── Announcement banner in interface
T-7 days: Reminder, freeze new deposits
T-0: Stop trading
├── Withdraw all open orders (return margin)
├── Close trading pair in matching engine
└── Final announcement
T+30 days: Withdrawal deadline
└── Notify users with balances
T+60 days: Forced liquidation of remainders
└── Freeze or convert remaining funds
Technical Implementation
Initiating Delisting
class DelistingManager:
async def initiate_delisting(
self,
symbol: str,
reason: str,
admin_id: str,
urgent: bool = False
) -> DelistingPlan:
# Check current state
token_info = await self.db.get_token(symbol)
affected_users = await self.db.count_users_with_balance(symbol)
# For urgent delisting (security incident) — shortened timelines
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)
# Start notifications
await self.notification_service.announce_delisting(plan)
return plan
async def stop_trading(self, symbol: str):
"""Stop trading for symbol"""
# Withdraw all open orders
open_orders = await self.db.get_open_orders_by_symbol(symbol)
for order in open_orders:
await self.matching_engine.cancel_order(order.id)
# Return reserved funds
await self.balance_service.release_reserved(order.user_id, order)
# Notify user
await self.notify_order_cancelled(order, reason='delisting')
# Deactivate pair in matching engine
await self.matching_engine.disable_symbol(symbol)
# Stop deposits and new withdrawals (existing withdrawals still work)
await self.db.update_token_status(symbol, 'delisting_in_progress')
logger.info(f"Trading stopped for {symbol}, {len(open_orders)} orders cancelled")
Forced Liquidation After Deadline
async def process_expired_delisting(self, symbol: str):
"""Handle expired balances after withdrawal deadline"""
remaining_balances = await self.db.get_token_balances(symbol)
for user_id, amount in remaining_balances.items():
# Options depending on policy:
action = await self.determine_action(user_id, symbol, amount)
if action == 'auto_convert':
# Convert to USDT at 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':
# Freeze with manual withdrawal option via support
await self.db.freeze_balance(user_id, symbol, amount)
await self.send_final_notice(user_id, symbol, amount, action)
User Communication
Email Notification
def generate_delisting_email(plan: DelistingPlan, user: User) -> EmailContent:
user_balance = get_user_balance(user.id, plan.symbol)
return EmailContent(
subject=f"Important: {plan.symbol} will be delisted from exchange",
body=f"""
Dear {user.first_name},
We inform you of upcoming delisting of {plan.symbol} token.
**Reason:** {plan.reason}
**Key dates:**
• Deposit stop: {plan.deposits_stop_at.strftime('%d.%m.%Y')}
• Trading stop: {plan.trading_stops_at.strftime('%d.%m.%Y')}
• Withdrawal deadline: {plan.withdrawal_deadline.strftime('%d.%m.%Y')}
**Your current balance:** {user_balance} {plan.symbol}
**We recommend before {plan.trading_stops_at.strftime('%d.%m.%Y')}:**
1. Withdraw {plan.symbol} to external wallet, or
2. Exchange for another asset via trading terminal
After {plan.withdrawal_deadline.strftime('%d.%m.%Y')} remaining balances
will be processed according to our policy.
Best regards,
Exchange Team
"""
)
Delisting is painful, but transparent communication and sufficient withdrawal time minimize user damage and exchange reputation. Industry standard: minimum 14 days notice for non-critical delistings.







