Fund Sweeping
Fund sweeping is the process of moving confirmed USDT from individual payment addresses to your hot wallet. PayWarden automates this entirely.
Why sweeping is needed
Each order uses a unique payment address. After confirmation, USDT sits at that address. Sweeping consolidates funds to your HOT_WALLET_ADDRESS so you can:
- Access your revenue in one place
- Avoid managing hundreds of individual addresses
- Free up address indices for reuse tracking
How it works
Order confirmed
│
▼
[BullMQ delayed job created] ← 5 min delay (configurable)
│
▼
Gas Manager: check TRX balance at payment address
│
├── TRX balance < threshold?
│ │
│ ▼
│ Send TRX from hot wallet to cover gas
│ │
│ ▼
│ Wait for TRX confirmation (~9s)
│
▼
Fund Sweeper: sign USDT transfer
│
├── Derive private key for address index
├── Build TRC-20 transfer transaction
├── Sign with private key
├── privateKey.fill(0) ← wipe immediately
├── Broadcast to TronGrid
│
▼
Wait for sweep confirmation
│
▼
Mark address as swept in DB
Address index retiredGas management
TRC-20 transfers on TRON require TRX to pay for bandwidth/energy. Payment addresses receive USDT only — they have no TRX.
Before sweeping, the Gas Manager automatically:
- Checks the TRX balance of the payment address
- If below threshold, transfers enough TRX from your hot wallet
- Waits for the TRX transfer to confirm
- Then proceeds with the USDT sweep
Your hot wallet must maintain a sufficient TRX balance. The admin dashboard shows a low-balance warning when it drops below GAS_LOW_THRESHOLD.
Configuration
HOT_WALLET_ADDRESS=TXxx... # destination for swept funds
SWEEP_DELAY_MINUTES=5 # delay after confirmation before sweep
GAS_RESERVE_TRX=5 # TRX to send per payment address
GAS_LOW_THRESHOLD=100 # alert when hot wallet TRX < thisSweep modes
Mode A — TRX burn (default)
Send TRX to the payment address to cover gas, then sweep USDT. Simple and reliable, works for any address.
Typical cost: ~5–10 TRX per sweep (~$0.50–1.00 at current prices).
Mode B — Energy delegation (coming in Phase 3B)
Delegate energy from a staked TRX account to payment addresses. Reduces per-sweep gas cost by ~80%. Requires maintaining a staked TRX position.
Monitoring sweeps
The admin dashboard shows:
- Pending sweeps (confirmed but not yet swept)
- Sweep history with tx hashes
- Hot wallet TRX balance
- Failed sweeps (with error details)
Failed sweeps
If a sweep fails (network error, insufficient TRX, etc.), BullMQ retries automatically with backoff. After 5 failures, the job is marked as failed and appears in the admin dashboard for manual review.
To manually trigger a sweep:
# Coming in v1.1: manual sweep command
docker compose exec app node dist/scripts/sweep.js --order-id ord_abc123Security
Private keys exist in memory only during the signing step, for milliseconds:
const privateKey = derivePrivateKey(mnemonic, index)
const signedTx = signTransaction(tx, privateKey)
privateKey.fill(0) // immediately overwritten with zeros
await broadcastTransaction(signedTx)The mnemonic is decrypted from vault.enc at startup and held in memory for the lifetime of the process. If this concerns you, consider running the sweeper as a separate process with its own vault access pattern.