One of the most common scenarios of losing funds on an exchange is an error in processing a blockchain reorg: a transaction is considered confirmed, but the block is rolled back, and the money disappears. Another pain point is gas wars: when withdrawing ETH, the gas price spikes, the transaction gets stuck, and users panic. We build a deposit and withdrawal system that is resilient to such situations. The architecture is based on proven patterns: multi-sig, HSM, automatic reorg-handling, adaptive gas management.
Our team has 5+ years of experience in blockchain development and has implemented over 50 projects for crypto exchanges and DeFi protocols. We guarantee that your deposit and withdrawal system will comply with the best industry practices. Contact us to discuss your exchange architecture — we will evaluate the project and offer the optimal solution.
How are deposit addresses generated?
Each user receives a unique deposit address for each network. There are two approaches.
HD Wallet (Hierarchical Deterministic). A deterministic key tree is generated from a single master seed according to BIP-32 and BIP-44. For Bitcoin: m/44'/0'/0'/0/{user_index}. For Ethereum: m/44'/60'/0'/0/{user_index}.
import { HDNodeWallet } from "ethers";
const masterNode = HDNodeWallet.fromMnemonic(Mnemonic.fromPhrase(MASTER_MNEMONIC));
function getDepositAddress(userId: number, coinIndex: number): string {
// m/44'/coinIndex'/0'/0/userId
const child = masterNode
.deriveChild(44 + 0x80000000) // purpose
.deriveChild(coinIndex + 0x80000000) // coin type
.deriveChild(0 + 0x80000000) // account
.deriveChild(0) // external
.deriveChild(userId);
return child.address;
}
The master seed is stored in an HSM or in an encrypted vault (HashiCorp Vault). Public keys for address generation are in the database, private keys for signing are only in the HSM.
Shared address + memo — one address for the entire exchange, the user specifies a memo/tag. Used for Ripple (XRP), Stellar (XLM), Cosmos. Cheaper to maintain, but a memo error leads to loss of funds.
Monitoring incoming transactions
The monitoring service subscribes to blockchain events:
- EVM chains:
eth_subscribe("logs", { address: [depositAddresses], topics: [ERC20_TRANSFER_TOPIC] })via WebSocket to the node + pollingeth_getBlockByNumberas fallback - Bitcoin:
zmqpubrawtxfrom Bitcoin Core + periodic address scanning viascantxoutset - TRON: TronGrid WebSocket or polling TronScan API
type DepositMonitor struct {
node EthClient
db *DB
confirmations int // minimum confirmations
}
func (m *DepositMonitor) ProcessBlock(blockNum uint64) {
receipts := m.node.GetBlockReceipts(blockNum)
for _, receipt := range receipts {
for _, log := range receipt.Logs {
if !isERC20Transfer(log) {
continue
}
to := common.HexToAddress(log.Topics[2].Hex())
if !m.db.IsDepositAddress(to) {
continue
}
m.recordPendingDeposit(Deposit{
TxHash: receipt.TxHash,
BlockNum: blockNum,
UserAddress: to,
Token: log.Address,
Amount: new(big.Int).SetBytes(log.Data),
})
}
}
}
Confirmations and finality
The number of required confirmations depends on the network and amount:
| Network | Minimum Confirmations | Reason |
|---|---|---|
| Bitcoin | 3–6 | Reorg probability |
| Ethereum | 12–20 (or finalized) | Post-merge finality |
| Polygon PoS | 100–256 | Checkpoint finality |
| BSC | 15–20 | PoSA, more centralized |
| TRON | 19 | Solid consensus |
| Solana | 32 (finalized) | Tower BFT |
After reaching the confirmation threshold, the deposit is credited to the user's balance. Until then, it is in pending status. We display pending deposits with a progress indicator.
Reorg handling: store block_hash along with block_number. When a reorg is detected (block hash changed), mark affected transactions as reorged and restart monitoring.
How is fund consolidation (sweeping) done?
Deposit addresses number in the thousands or millions. Storing ETH on each address is expensive and insecure. Automatic consolidation to a master hot wallet is needed:
func (s *Sweeper) SweepAddress(depositAddr Address) error {
balance, _ := s.node.GetBalance(depositAddr)
if balance.Cmp(s.minSweepAmount) < 0 {
return nil // not worth the gas
}
// For ERC20: first need to send ETH for gas
if s.token != ETH {
gasCost := estimateGas(depositAddr, HOT_WALLET, token)
s.fundGas(depositAddr, gasCost)
}
// Sign via HSM — private key of depositAddr never leaves HSM
tx := s.buildTransfer(depositAddr, HOT_WALLET, balance)
signed := s.hsm.Sign(depositAddr, tx)
return s.node.SendRawTransaction(signed)
}
For ERC20 tokens, the task is complicated: ETH for gas is needed on the deposit address. Solutions:
- Gas station: send ETH before sweep, then sweep tokens
- Gasless sweep via EIP-2612/permit: if the token supports permit, the exchange pays gas itself
- Batch sweep via multicall: one call collects funds from multiple addresses
Savings on gas when using batch sweep can reach $40,000 per month at high volumes.
Withdrawal architecture
A withdrawal goes through several stages:
REQUESTED → VALIDATED → APPROVED → SIGNING → BROADCASTING → PENDING → CONFIRMED
VALIDATED: check balance, limits, AML/KYC. If passed — reserve funds (deduct from available balance).
APPROVED: for large amounts — manual review by exchange operator or multi-signature (M-of-N approval from several operators). For small amounts — automatic approval.
SIGNING: sign the transaction in HSM or cold wallet system. Never sign on the server where approval logic is stored — separation of duties.
BROADCASTING: send the transaction to the network. After that, the transaction cannot be canceled.
Gas management
The exchange must pay gas for withdrawals. A gas management system is needed:
- Monitoring
eth_gasPrice/ EIP-1559baseFee+maxPriorityFee - Gas budget: account for gas cost in withdrawal cost or fee
- RBF (Replace By Fee) for stuck Bitcoin transactions
- EIP-1559 bump: when an Ethereum transaction gets stuck — resend with same nonce and increased
maxFeePerGas
Notifications and statuses
The user must see the withdrawal status in real time. Integration:
- WebSocket push on each status change
- Email/Telegram notifications at key stages (approval, sending, confirmation)
- Transaction hash with a link to the explorer immediately after broadcasting
How is security ensured?
Critical checks:
- Address whitelist: require adding a new address 24-48 hours before withdrawal to it. When adding — email confirmation + 2FA.
- Anti-phishing: display an anti-phishing code in emails and UI (the user sets it). If it's missing — suspicious.
- Withdrawal limits: daily limits by KYC levels. If exceeded — manual review.
- Velocity checks: multiple withdrawals in a short time → temporary block and notification.
Hot/Warm/Cold wallet segregation
- Hot wallet: small operational reserve (10-20% of daily withdrawal volume), always online, automatic withdrawals
- Warm wallet: multi-sig (2-of-3 or 3-of-5 hardware keys), replenishes the hot wallet once a day
- Cold wallet: offline storage, only for large reserves, manual access procedure
Distribution: 5-10% hot, 15-20% warm, 70-80% cold. Specific numbers depend on volumes and the exchange's risk model.
Infrastructure and reliability
Node vs API provider: a full node gives reliability and independence. API providers (Alchemy, QuickNode, Infura) — convenience but dependency on a third party. For a production exchange: several providers + own node, automatic failover.
Idempotency: each withdrawal request has a unique withdrawal_id. Repeated processing of the same ID does not create a duplicate transaction. Critical for recovery after failures.
Transaction monitoring: after broadcasting — periodic check of transaction status. If after N minutes it's not in mempool — consider it dropped, resend with correct nonce.
What's included in the work
- Architecture documentation: description of cold/warm/hot wallet scheme, confirmation logic, signing scheme, and disaster recovery.
- Source code with integration of HSM, multi-sig, gas management, and AML checks.
- Deployment and setup: node configuration, load balancers, monitoring (Prometheus/Grafana).
- API documentation for frontend and admin panel.
- Team training: workshop on system operation and incident response.
- 3-month warranty on identified bugs and free consultations on modifications.
Estimated timelines
| Component | Duration |
|---|---|
| Ethereum + ERC20 deposits/withdrawals | 4-6 weeks |
| Bitcoin | 3-4 weeks |
| TRON | 2-3 weeks |
| Each additional EVM network | 1-2 weeks |
| Multi-currency hot wallet management | 3-4 weeks |
| Admin dashboard for monitoring | 2-3 weeks |
Full system for 5-7 networks with HSM integration, AML checks, and admin interface — 3-5 months. Cost is calculated individually after auditing your project.
Order the development of a deposit and withdrawal system — get a ready-made solution with warranty and support. We value transparency: all stages, timelines, and costs are fixed in the contract. Get a consultation — we will answer questions and propose the optimal architecture for your project. Contact us for an estimate — within two days we will analyze your infrastructure and send a commercial proposal.







