Address Management
Comprehensive guide to managing blockchain addresses in PayIn's address pool system.
Overview
PayIn's Address Pool is a sophisticated address management system that pre-generates and dynamically allocates blockchain addresses for payment processing. This approach enables:
- Instant Address Assignment: No need to generate addresses on-demand
- Address Reusability: Efficient recycling with cooldown protection
- Multi-Chain Support: Unified management across EVM, Tron, and Solana
- Security: Proper key management and address isolation
- Scalability: Handle high-volume payment operations
Why Address Pool?
Traditional payment systems generate addresses on-demand, causing delays. PayIn's address pool pre-generates addresses, enabling instant payment address assignment and improving user experience.
Address Pool Concept
How It Works
┌─────────────────────────────────────────────────────────────┐
│ Address Pool System │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ Allocate ┌──────────────┐ │
│ │ Available │ ────────→ │ Allocated │ │
│ │ Addresses │ │ (Order) │ │
│ │ │ │ │ │
│ │ 1000 addrs │ ←──────── │ 50 addrs │ │
│ └──────────────┘ Release └──────────────┘ │
│ ↑ │
│ │ │
│ │ After cooldown │
│ │ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Cooldown │ │ Bound │ │
│ │ (30 min) │ │ (Deposit) │ │
│ │ │ │ │ │
│ │ 100 addrs │ │ 200 addrs │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘Key Concepts:
- Pre-Generation: Addresses are generated in bulk before being needed
- Dynamic Allocation: Addresses are assigned on-demand from the pool
- State Management: Addresses transition through defined states
- Automatic Recycling: Order addresses return to the pool after completion
- Cooldown Protection: Prevents immediate reuse for privacy and security
Address States
PayIn manages addresses through four states:
| State | Description | Used For | Returns to Pool |
|---|---|---|---|
| available | Ready for allocation | Waiting in pool | N/A |
| allocated | Temporarily assigned | Active orders | Yes (after completion) |
| bound | Permanently assigned | User deposits | Optional (manual unbind) |
| cooldown | Recently released | Temporary hold | Yes (after 30 min) |
State Transition Diagram
allocate complete/expire
available ─────────→ allocated ──────────────→ cooldown
↑ │
│ │
└───────────────── after 30 min ────────────────────┘
bind unbind
available ─────────→ bound ─────────────→ cooldown
↑ │
│ │
└───────────────── after 30 min ────────────────────┘Why Cooldown Matters
Cooldown Period (default: 30 minutes) provides:
- Privacy Protection: Prevents linking different orders to the same address immediately
- Settlement Window: Allows time for blockchain finalization
- User Confidence: Avoids confusion from address reuse
- Monitoring Cleanup: Ensures monitoring tasks complete properly
Configurable Cooldown
The cooldown period can be adjusted per organization via the address_pool.cooldown_minutes configuration parameter.
Address Management Modes
PayIn supports two address management approaches to fit different security models and operational preferences.
Comparison Table
| Feature | HD Wallet Mode | Self-Managed Mode |
|---|---|---|
| Key Management | User controls mnemonic | User controls private keys |
| Address Generation | PayIn CLI tool | User's own tools |
| Traceability | Full (derivation index) | Limited |
| Backup | Single mnemonic | Each key separately |
| Tooling | PayIn provides tools | User provides tools |
| Recommended For | Most users | Advanced users |
| Security Responsibility | User (mnemonic) | User (all keys) |
Recommendation
We strongly recommend HD Wallet Mode for most use cases. It provides better traceability, easier backup, and full tooling support from PayIn.
HD Wallet Mode (Recommended)
HD Wallet Mode uses BIP44 hierarchical deterministic wallet standard to derive addresses from a single master seed (mnemonic phrase).
Advantages:
- ✅ Single Backup: One 12/24-word mnemonic backs up unlimited addresses
- ✅ Deterministic: Can regenerate any address with mnemonic + derivation index
- ✅ Full Traceability: Track which addresses belong to your wallet
- ✅ PayIn Tooling: Complete CLI tool support for generation and verification
- ✅ Standards-Based: Uses industry-standard BIP44 paths
How It Works:
- Generate a BIP39 mnemonic phrase (12 or 24 words)
- Use PayIn Address Tool to derive addresses from the mnemonic
- Export addresses to CSV (includes derivation index)
- Import CSV to PayIn via Admin UI
- PayIn assigns addresses from the pool
Derivation Paths:
EVM Chains: m/44'/60'/0'/0/{index} (Ethereum, Polygon, etc.)
Tron Chain: m/44'/195'/0'/0/{index}
Solana Chain: m/44'/501'/{index}'/0' (Hardened derivation)Solana Limitations
Solana uses Ed25519 cryptography with hardened derivation (SLIP-0010), which means:
- ❌ Cannot derive child addresses from master public key
- ✅ Address generation requires full mnemonic
- ✅ Addresses are still tracked via derivation index
- ✅ Fully compatible with PayIn's address pool
This is a fundamental cryptographic property of Ed25519, not a limitation of PayIn.
Self-Managed Mode
Self-Managed Mode allows you to import addresses generated by your own tools or wallets.
Advantages:
- ✅ Full Control: Use your preferred wallet software
- ✅ Flexibility: Import addresses from any source
- ✅ Custom Workflows: Integrate with existing key management
- ✅ No Derivation: Not tied to any specific derivation scheme
Considerations:
- ⚠️ Manual Backup: Must backup each address/key separately
- ⚠️ Limited Traceability: PayIn doesn't know address relationships
- ⚠️ Your Responsibility: Must ensure addresses are valid and secure
How It Works:
- Generate addresses using your wallet software
- Export to CSV format
- Import CSV to PayIn via Admin UI or API
- PayIn assigns addresses from the pool
CSV Format:
address,protocol,derivation_index,master_public_key
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0,evm,,
TYASr5UV6HEcXatwdFQfmLVUqQQQMUxHLS,tron,,Optional Fields
For self-managed addresses, derivation_index and master_public_key are optional. Leave them empty if not applicable.
HD Wallet Address Generation
This section covers the complete workflow for generating addresses in HD Wallet Mode using PayIn's official tools.
Prerequisites
- Secure Environment: Use an offline computer or air-gapped system for maximum security
- Node.js: Version 18 or higher
- PayIn Address Tool: Official CLI tool
- Secure Storage: Physical location for mnemonic backup (safe, vault, etc.)
Step 1: Install Address Tool
# Navigate to PayIn repository
cd payin/apps/address-tool
# Install dependencies
npm install
# Build the tool
npm run buildGlobal Installation
For easier access, you can install globally:
npm install -g @payin/address-toolThen run with: payin-address-tool
Step 2: Generate Mnemonic
Start the tool and choose to generate a new mnemonic:
npm startInteractive Flow:
? Select an action: Generate New Addresses
? Select protocol: EVM (Ethereum, Polygon, etc.)
? Do you have an existing mnemonic? No, generate new mnemonic
🔐 Your Mnemonic Phrase (WRITE THIS DOWN IMMEDIATELY!):
witch collapse practice feed shame open despair creek road again ice least
⚠️ WARNING:
- Write down this mnemonic on paper
- Store it in a secure, offline location
- Anyone with this phrase can access your funds
- This tool does NOT save it for you
? Have you written down the mnemonic? (yes/no): yesSecurity Checklist:
- ✅ Write mnemonic on paper (not digitally)
- ✅ Double-check each word is correct
- ✅ Store in secure location (safe, safety deposit box)
- ✅ Consider creating multiple backups in separate locations
- ❌ NEVER save to computer, phone, or cloud storage
- ❌ NEVER take a photo of the mnemonic
- ❌ NEVER share with anyone
Step 3: Generate Addresses
Configure address generation parameters:
? Starting derivation index: 0
? How many addresses to generate: 1000
Generating addresses...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% | 1000/1000
✅ Generated 1000 addresses successfully!
Protocol: evm
Derivation Path: m/44'/60'/0'/0/{index}
Range: 0 - 999Generation Tips:
- Batch Size: Generate 1000-5000 addresses per batch
- Starting Index: Use 0 for first batch, then 1000, 2000, etc.
- Multiple Protocols: Generate separately for EVM, Tron, and Solana
Step 4: Export to CSV
? Export to CSV? Yes
? Output file name: evm-addresses-2025-01-20
Exporting to CSV...
✅ Exported to: output/evm-addresses-2025-01-20-full.csv
CSV Format: Full (HD Wallet Mode)
Columns: address, derivation_index, protocol, master_public_key
Rows: 1000CSV Content Example:
address,derivation_index,protocol,master_public_key
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0,0,evm,xpub6CUGRUonZSQ4TWUJKy...
0x5AEDA56215b167893e80B4fE645BA6d5Bab767DE,1,evm,xpub6CUGRUonZSQ4TWUJKy...
0x9FF4e4b8a0451F7c8F7a1D8c1C6b5C2d3E4f5A6B,2,evm,xpub6CUGRUonZSQ4TWUJKy...CSV File Security
The CSV file contains your extended public key (xpub), which allows anyone to:
- View all your addresses
- Track your payment history
- Derive future addresses (EVM/Tron only)
Do NOT:
- Share CSV files publicly
- Upload to untrusted systems
- Commit to version control
Store CSV files securely and only upload to trusted PayIn instances.
Step 5: Verify Addresses (Optional)
Verify that an address belongs to your wallet:
npm start? Select an action: Verify Address Ownership
? Select protocol: EVM
? Do you have an existing mnemonic? Yes, use existing
? Enter your mnemonic phrase: [enter your 12/24 words]
? Enter address to verify: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0
? Search range (max derivation index): 1000
Searching for address...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100%
✅ ADDRESS FOUND!
Address: 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0
Derivation Index: 0
Derivation Path: m/44'/60'/0'/0/0Verification Use Cases:
- Confirm an address belongs to your wallet
- Find the derivation index of a known address
- Audit imported addresses
- Recover address information
Importing Addresses to PayIn
After generating addresses, import them into PayIn's address pool via the Admin UI or API.
Import via Admin UI (Recommended)
The Admin UI provides a user-friendly interface for importing addresses with validation.
Step-by-Step:
Login to PayIn Admin
- Navigate to testnet.payin.com or your PayIn instance
- Login with your account
Navigate to Address Pool
- Click "Address Pool" in the sidebar
- You'll see current pool statistics
Start Import
- Click "Import Addresses" button
- Import dialog opens
Select Import Mode
○ HD Wallet Import (Recommended) ○ Self-Managed Import- Choose HD Wallet Import if your CSV includes
derivation_indexandmaster_public_key - Choose Self-Managed Import for addresses from external wallets
- Choose HD Wallet Import if your CSV includes
Upload CSV File
- Click "Choose File" or drag-and-drop
- Select your exported CSV file
- Wait for file validation
Review and Confirm
Import Summary: - Protocol: EVM - Total Addresses: 1000 - Derivation Range: 0 - 999 - Master Public Key: xpub6CUG... ✓ All addresses are valid ✓ No duplicates found ✓ Format validation passedStart Import
- Click "Import"
- Progress bar shows import status
- Receive confirmation when complete
Duplicate Handling
PayIn automatically skips duplicate addresses during import. If an address already exists in the pool, it won't be imported again.
Import via API
For automation or programmatic imports, use the Address Pool API.
Endpoint: POST /api/v1/address-pool/import
Authentication: Requires API Key with admin or owner role
Request:
import fs from 'fs';
const csvContent = fs.readFileSync('evm-addresses-2025-01-20-full.csv', 'utf8');
const response = await fetch('https://testnet.payin.com/api/v1/address-pool/import', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.PAYIN_API_KEY}`
},
body: JSON.stringify({
protocol: 'evm',
mode: 'hd_wallet', // or 'self_managed'
addresses: csvContent
})
});
const result = await response.json();
console.log('Import result:', result);Response:
{
"success": true,
"imported": 1000,
"skipped": 0,
"errors": [],
"summary": {
"protocol": "evm",
"mode": "hd_wallet",
"derivation_range": {
"start": 0,
"end": 999
},
"master_public_key": "xpub6CUGRUonZSQ4TWUJKy..."
}
}Error Response (400 Bad Request):
{
"success": false,
"error": "Validation failed",
"details": [
{
"line": 42,
"address": "0xinvalid",
"error": "Invalid EVM address format"
}
]
}Python Example:
import requests
with open('evm-addresses-2025-01-20-full.csv', 'r') as f:
csv_content = f.read()
response = requests.post(
'https://testnet.payin.com/api/v1/address-pool/import',
headers={
'Content-Type': 'application/json',
'Authorization': f'Bearer {os.getenv("PAYIN_API_KEY")}'
},
json={
'protocol': 'evm',
'mode': 'hd_wallet',
'addresses': csv_content
}
)
result = response.json()
print(f"Imported: {result['imported']} addresses")Batch Import Strategy
For large address pools (10,000+ addresses), use batch imports:
Strategy:
- Generate in Batches: Create CSV files with 1000-5000 addresses each
- Sequential Import: Import batches sequentially to avoid timeout
- Verify After Each: Check pool statistics after each batch
- Track Progress: Maintain a log of imported batches
Example Batch Workflow:
# Batch 1: Addresses 0-999
npm start
# Generate: start=0, count=1000
# Export: evm-batch-1.csv
# Batch 2: Addresses 1000-1999
npm start
# Generate: start=1000, count=1000
# Export: evm-batch-2.csv
# Batch 3: Addresses 2000-2999
npm start
# Generate: start=2000, count=1000
# Export: evm-batch-3.csv
# Import each batch via Admin UIAddress Lifecycle Management
Understanding how addresses move through different states helps you manage the pool effectively.
Order Payment Lifecycle
Scenario: User creates an order to pay 100 USDT
Step 1: Order Creation
─────────────────────────────────────────────────────────────
POST /api/v1/orders
{
"orderReference": "ORDER-2025-001",
"amount": "100",
"currency": "USDT",
"chainId": "ethereum-sepolia"
}
↓ PayIn allocates address from pool
Response:
{
"orderId": "ord_abc123",
"paymentAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0",
"status": "pending"
}
Address State: available → allocatedStep 2: User Pays
─────────────────────────────────────────────────────────────
User sends 100 USDT to 0x742d35...
↓ PayIn monitor detects transaction
↓ Waits for confirmations (default: 3 blocks)
↓ Order completes
Webhook Event: order.completed
Address State: allocated (still held)Step 3: Address Release
─────────────────────────────────────────────────────────────
After order completion, PayIn automatically:
1. Releases address from order
2. Sets cooldown period (30 minutes)
3. Address enters cooldown state
Address State: allocated → cooldownStep 4: Return to Pool
─────────────────────────────────────────────────────────────
After cooldown expires (30 minutes):
- Address becomes available again
- Can be allocated to new orders
- Prioritized by LRU (least recently used)
Address State: cooldown → availableTimeline:
Order Created ────→ Payment Sent ────→ Order Completed ────→ Cooldown Expires
↓ ↓ ↓ ↓
allocated allocated cooldown available
0s ~3 min ~3 min 30s ~33 min 30sDeposit Address Lifecycle
Scenario: User wants a permanent deposit address
Step 1: Address Binding
─────────────────────────────────────────────────────────────
POST /api/v1/deposits/references
{
"depositReference": "user_alice_123",
"currency": "USDT",
"chainId": "polygon-amoy"
}
↓ PayIn allocates and binds address
Response:
{
"depositReference": "user_alice_123",
"address": "0x9FF4e4b8a0451F7c8F7a1D8c1C6b5C2d3E4f5A6B",
"protocol": "evm"
}
Address State: available → boundStep 2: Multiple Deposits
─────────────────────────────────────────────────────────────
User can deposit multiple times to the same address:
Deposit 1: 50 USDT → Creates deposit record
Deposit 2: 100 USDT → Creates another deposit record
Deposit 3: 25 USDT → Creates another deposit record
Each deposit:
- Triggers webhook: deposit.pending → deposit.confirmed
- Records amount and transaction
- Address remains bound
Address State: bound (permanent)Step 3: Address Unbinding (Optional)
─────────────────────────────────────────────────────────────
If user closes account or stops using service:
DELETE /api/v1/deposits/references/user_alice_123
↓ PayIn unbinds address
↓ Sets cooldown period (30 minutes)
Address State: bound → cooldown → availableTimeline:
Binding ────→ Deposit 1 ────→ Deposit 2 ────→ ... ────→ Unbind ────→ Cooldown ────→ Available
↓ ↓ ↓ ↓ ↓ ↓
bound bound bound cooldown cooldown available
0s Hours Days ~0s ~30 min ~30 minLRU Allocation Strategy
PayIn uses Least Recently Used (LRU) algorithm to allocate addresses efficiently.
Allocation Priority:
-- 1. NEW ADDRESSES (never used)
WHERE recycled_at IS NULL
ORDER BY created_at ASC
-- 2. RECYCLED ADDRESSES (used before, cooled down)
WHERE recycled_at IS NOT NULL
AND cooldown_until <= NOW()
ORDER BY recycled_at ASCWhy LRU?
- Fairness: All addresses get equal usage over time
- Privacy: Maximizes time between reuses
- Distribution: Prevents hot addresses
- Monitoring: Ensures old monitoring tasks clean up
Example Allocation Sequence:
Pool State:
- Address A: recycled_at = NULL (new)
- Address B: recycled_at = 2025-01-20 10:00 (used 2 hours ago)
- Address C: recycled_at = 2025-01-20 11:30 (used 30 min ago)
- Address D: recycled_at = NULL (new)
Allocation Order:
1. Address A (new address, created first)
2. Address D (new address, created second)
3. Address B (recycled, oldest)
4. Address C (recycled, newest)Address Pool Monitoring
Monitor your address pool health to ensure smooth payment operations.
Pool Status API
Get real-time pool statistics:
Endpoint: GET /api/v1/address-pool/status
Request:
const response = await fetch('https://testnet.payin.com/api/v1/address-pool/status', {
headers: {
'Authorization': `Bearer ${process.env.PAYIN_API_KEY}`
}
});
const status = await response.json();
console.log('Pool Status:', status);Response:
{
"evm": {
"total": 5000,
"available": 4200,
"allocated": 500,
"bound": 250,
"coolingDown": 50
},
"tron": {
"total": 2000,
"available": 1800,
"allocated": 150,
"bound": 40,
"coolingDown": 10
},
"solana": {
"total": 1000,
"available": 950,
"allocated": 30,
"bound": 15,
"coolingDown": 5
}
}Admin UI Dashboard
The Admin UI provides visual pool monitoring:
Navigate to Address Pool
- See real-time statistics
- View address allocation chart
Pool Health Indicators
EVM Address Pool ● Healthy ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Total: 5,000 addresses Available: 4,200 (84%) ▓▓▓▓▓▓▓▓░░ Allocated: 500 (10%) ▓▓░░░░░░░░ Bound: 250 ( 5%) ▓░░░░░░░░░ Cooldown: 50 ( 1%) ░░░░░░░░░░Low Inventory Warning
⚠️ WARNING: Low Address Inventory EVM pool has only 120 available addresses (2.4%) Recommended actions: 1. Generate more addresses immediately 2. Import new batch to pool 3. Review address allocation rate
Monitoring Metrics
Key Metrics to Track:
| Metric | Description | Healthy Range | Action Threshold |
|---|---|---|---|
| Available % | Percentage of available addresses | > 50% | < 20% |
| Allocation Rate | Addresses allocated per hour | Varies | Trending up sharply |
| Cooldown % | Percentage in cooldown | < 5% | > 10% |
| Bound Growth | Rate of deposit binding | Varies | Unexpected spike |
Low Inventory Alerts
Configure alerts to notify you when pool inventory is low.
Admin UI Configuration:
Navigate to Settings → Address Pool
Set Alert Thresholds:
Low Inventory Alert: ┌─────────────────────────────────┐ │ EVM: [200] addresses │ │ Tron: [100] addresses │ │ Solana: [50] addresses │ └─────────────────────────────────┘ Notification Methods: ☑ Email ☑ Webhook ☐ SMSTest Alert:
- Click "Send Test Alert"
- Verify you receive notification
Email Alert Example:
Subject: [PayIn Alert] Low Address Pool Inventory
Dear PayIn User,
Your EVM address pool is running low:
Current Status:
- Available: 180 addresses (3.6%)
- Total Pool: 5,000 addresses
- Threshold: 200 addresses (4%)
Recommended Actions:
1. Generate 1,000+ new addresses using PayIn Address Tool
2. Import addresses via Admin UI
3. Review allocation patterns
View Details: https://testnet.payin.com/address-pool
—
PayIn Alert SystemWebhook Alert Payload:
{
"type": "alert.address_pool_low",
"timestamp": "2025-01-20T15:30:00Z",
"data": {
"protocol": "evm",
"available": 180,
"total": 5000,
"percentage": 3.6,
"threshold": 200
}
}Proactive Pool Management
Best Practices:
- Monitor Daily: Check pool status at least once per day
- Maintain Buffer: Keep 50%+ addresses available
- Batch Generation: Generate 1000+ addresses per batch
- Predictive Scaling: Monitor allocation trends, add addresses proactively
- Protocol Balance: Ensure adequate addresses for each protocol
Monitoring Script Example:
// check-pool-health.ts
import { PayInClient } from '@payin/sdk';
const client = new PayInClient({
apiKey: process.env.PAYIN_API_KEY,
baseUrl: 'https://testnet.payin.com'
});
async function checkPoolHealth() {
const status = await client.addressPool.getStatus();
for (const [protocol, stats] of Object.entries(status)) {
const availablePercent = (stats.available / stats.total) * 100;
if (availablePercent < 20) {
console.error(`❌ CRITICAL: ${protocol} pool at ${availablePercent.toFixed(1)}%`);
// Send alert
} else if (availablePercent < 50) {
console.warn(`⚠️ WARNING: ${protocol} pool at ${availablePercent.toFixed(1)}%`);
// Consider generating more
} else {
console.log(`✅ OK: ${protocol} pool at ${availablePercent.toFixed(1)}%`);
}
}
}
// Run every hour
setInterval(checkPoolHealth, 60 * 60 * 1000);
checkPoolHealth();Cron Job Setup:
# Add to crontab (runs every 4 hours)
0 */4 * * * cd /path/to/scripts && node check-pool-health.jsSelf-Managed Address Import
For users who prefer to manage their own keys and addresses outside of the HD wallet system.
Preparing Self-Managed Addresses
Step 1: Generate Addresses
Use your preferred wallet software:
# Using ethers.js (EVM)
import { Wallet } from 'ethers';
const addresses = [];
for (let i = 0; i < 1000; i++) {
const wallet = Wallet.createRandom();
addresses.push({
address: wallet.address,
privateKey: wallet.privateKey // Store securely, don't include in CSV!
});
}
# Save private keys securely (encrypted file, hardware wallet, etc.)
# Export only addresses to CSV# Using TronWeb (Tron)
import TronWeb from 'tronweb';
const addresses = [];
for (let i = 0; i < 1000; i++) {
const account = await TronWeb.createAccount();
addresses.push({
address: account.address.base58,
privateKey: account.privateKey // Store securely!
});
}Step 2: Create CSV File
Format for self-managed addresses:
address,protocol,derivation_index,master_public_key
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0,evm,,
0x5AEDA56215b167893e80B4fE645BA6d5Bab767DE,evm,,
0x9FF4e4b8a0451F7c8F7a1D8c1C6b5C2d3E4f5A6B,evm,,
TYASr5UV6HEcXatwdFQfmLVUqQQQMUxHLS,tron,,
TPJRnELdwXLWVvokPJyT3CHPF4YPKVkrSU,tron,,Key Points:
- Leave
derivation_indexandmaster_public_keyempty - Include only public addresses (never private keys!)
- One address per line
- Protocol must be
evm,tron, orsolana
Step 3: Validate Addresses
Before import, validate addresses locally:
// validate-addresses.ts
import { isAddress } from 'ethers';
import fs from 'fs';
import csv from 'csv-parse/sync';
const content = fs.readFileSync('self-managed-addresses.csv', 'utf8');
const records = csv.parse(content, { columns: true });
let errors = 0;
for (const [index, record] of records.entries()) {
if (record.protocol === 'evm' && !isAddress(record.address)) {
console.error(`Line ${index + 2}: Invalid EVM address: ${record.address}`);
errors++;
}
// Add validation for Tron and Solana addresses
}
if (errors === 0) {
console.log('✅ All addresses are valid');
} else {
console.error(`❌ Found ${errors} invalid addresses`);
process.exit(1);
}Import Process
Via Admin UI:
- Navigate to Address Pool → Import Addresses
- Select "Self-Managed Import"
- Upload CSV file
- Review import summary
- Confirm import
Via API:
const csvContent = fs.readFileSync('self-managed-addresses.csv', 'utf8');
const response = await fetch('https://testnet.payin.com/api/v1/address-pool/import', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.PAYIN_API_KEY}`
},
body: JSON.stringify({
protocol: 'evm',
mode: 'self_managed',
addresses: csvContent
})
});
const result = await response.json();
console.log(`Imported ${result.imported} self-managed addresses`);Security Considerations
Self-Managed Mode Responsibilities:
Private Key Management
- Store private keys securely (hardware wallet, encrypted storage)
- Never include private keys in CSV imports
- Implement key rotation strategy
- Backup keys in multiple secure locations
Address Tracking
- Maintain your own mapping of addresses to keys
- Document address generation method
- Track which addresses are imported to PayIn
Recovery Planning
- Document key recovery procedures
- Test recovery process regularly
- Ensure successors can access keys if needed
Critical: Private Key Security
PayIn NEVER asks for or stores your private keys. If someone asks for your private keys claiming to be from PayIn, it's a scam. Only import public addresses to PayIn.
Best Practices
For HD Wallet Mode
Security Best Practices:
Mnemonic Storage
- ✅ Write on paper, store in safe/vault
- ✅ Create multiple backups in separate locations
- ✅ Use metal backup plates for fire/water resistance
- ✅ Consider Shamir's Secret Sharing for enterprise
- ❌ Never store digitally (computer, phone, cloud)
- ❌ Never take photos
- ❌ Never share with anyone
Generation Environment
- ✅ Use offline/air-gapped computer for generation
- ✅ Verify tool integrity (checksum, signature)
- ✅ Disconnect network during generation
- ❌ Don't generate on shared/public computers
Verification
- ✅ Verify first few addresses after import
- ✅ Test small payment before production use
- ✅ Document derivation paths used
Operational Best Practices:
Batch Management
- Generate addresses in batches (1000-5000)
- Track derivation ranges (0-999, 1000-1999, etc.)
- Document batch imports with dates
- Maintain batch generation logs
Pool Sizing
- Maintain 50%+ available addresses
- Generate new batches proactively
- Monitor allocation rate trends
- Scale before running low
Traceability
- Keep records of all imports (batch files, dates)
- Document master public keys
- Track derivation index ranges
- Audit pool regularly
For Self-Managed Mode
Key Management:
Private Key Security
- Use hardware wallets when possible
- Encrypt private key storage
- Implement access controls
- Regular security audits
Address Generation
- Use reputable wallet software
- Verify address formats before import
- Check for duplicates
- Document generation method
Backup Strategy
- Multiple secure backups
- Geographic distribution
- Regular backup verification
- Documented recovery procedures
Import Best Practices:
Validation
- Validate all addresses before import
- Check for format errors
- Verify no duplicates
- Test small batch first
Documentation
- Maintain address-to-key mapping
- Document import dates and batches
- Track which addresses are in PayIn
- Record any address rotation
General Best Practices
Pool Management:
Monitoring
- Check pool status daily
- Set up low inventory alerts
- Monitor allocation patterns
- Track cooldown rates
Maintenance
- Regular pool audits
- Review address states
- Clean up stale bindings
- Update cooldown configuration as needed
Scaling
- Monitor business growth
- Scale proactively
- Plan for peak periods
- Maintain adequate buffer
Security:
Access Control
- Limit who can import addresses
- Use API keys with appropriate permissions
- Audit import activities
- Monitor unauthorized access attempts
Address Privacy
- Don't reuse addresses unnecessarily
- Respect cooldown periods
- Consider unbinding unused deposit addresses
- Rotate address pools periodically
Operational:
Testing
- Test address allocation on testnet first
- Verify import process works correctly
- Test pool exhaustion scenarios
- Practice emergency procedures
Documentation
- Document your address management process
- Keep import logs
- Maintain runbooks for common tasks
- Train team members
Security Considerations
Mnemonic Security
Critical: Your mnemonic phrase is the master key to all addresses generated from it.
Threat Model:
| Threat | Impact | Mitigation |
|---|---|---|
| Physical Theft | Attacker gains full access | Multiple secure locations, split storage |
| Digital Exposure | Malware/hacking access | Never store digitally, offline generation |
| Social Engineering | Tricked into revealing | Education, verification procedures |
| Loss/Destruction | Permanent loss of access | Multiple backups, metal plates |
Defense in Depth:
Layer 1: Offline Generation
- Generate on air-gapped computer
- Use verified, open-source tools
- Disconnect network during generation
Layer 2: Physical Security
- Store in safe/vault
- Multiple geographic locations
- Fire/water resistant containers
Layer 3: Access Control
- Limit who knows where mnemonic is stored
- Multi-person access requirements (for enterprises)
- Regular security reviews
Layer 4: Backup Strategy
- Multiple copies
- Different formats (paper, metal)
- Different locations
- Regular verificationAddress Validation
Import Validation:
PayIn performs comprehensive validation during address import:
Format Validation
- EVM: Checksummed address format (0x + 40 hex chars)
- Tron: Base58 format starting with 'T'
- Solana: Base58 format (32-44 chars)
Duplicate Detection
- Checks against existing pool addresses
- Prevents duplicate imports
- Validates within CSV file
Protocol Consistency
- Ensures address matches specified protocol
- Detects format mismatches
- Validates derivation data consistency
Blacklist Checking:
Future Feature
PayIn is planning to add support for address blacklist checking against known malicious addresses. This will help prevent importing compromised addresses.
For now, implement your own blacklist checking:
// check-blacklist.ts
import fs from 'fs';
import csv from 'csv-parse/sync';
// Load blacklist from public sources
const blacklist = new Set([
'0x...', // Known scam addresses
// Add more
]);
const addresses = csv.parse(
fs.readFileSync('addresses.csv', 'utf8'),
{ columns: true }
);
for (const record of addresses) {
if (blacklist.has(record.address.toLowerCase())) {
console.error(`❌ Blacklisted address found: ${record.address}`);
process.exit(1);
}
}
console.log('✅ No blacklisted addresses found');Import Verification
After importing addresses, verify the import was successful:
Verification Steps:
Check Pool Statistics
typescriptconst status = await client.addressPool.getStatus(); console.log(`Total EVM addresses: ${status.evm.total}`); // Should match expected countSpot Check Addresses
typescript// Verify random addresses from your import exist in pool const response = await client.addressPool.listAddresses({ protocol: 'evm', page: 1, pageSize: 10 }); // Check if addresses from your CSV appearTest Allocation
typescript// Create a test order to verify allocation works const order = await client.orders.create({ orderReference: 'TEST-VERIFICATION', amount: '1', currency: 'USDT', chainId: 'ethereum-sepolia' }); // Verify allocated address is from your imported set console.log(`Allocated address: ${order.paymentAddress}`);
Audit Logging
PayIn maintains comprehensive audit logs for address pool operations:
Logged Events:
- Address imports (who, when, how many)
- Address allocations (order/deposit)
- Address releases
- Address bindings/unbindings
- Pool configuration changes
Accessing Audit Logs:
const logs = await client.addressPool.getAuditLogs({
startDate: '2025-01-20',
endDate: '2025-01-21',
eventType: 'import'
});
for (const log of logs.entries) {
console.log(`${log.timestamp}: ${log.eventType} by ${log.user} - ${log.count} addresses`);
}Troubleshooting
Common Issues
1. Import Failed: "Invalid address format"
Cause: Address doesn't match the expected format for the protocol.
Solution:
# Validate EVM addresses
import { isAddress } from 'ethers';
console.log(isAddress('0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb0')); // true
# Ensure addresses are checksummed
import { getAddress } from 'ethers';
const checksummed = getAddress('0x742d35cc6634c0532925a3b844bc9e7595f0beb0');
console.log(checksummed); // 0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb02. Import Failed: "Duplicate addresses found"
Cause: Addresses already exist in the pool or appear multiple times in CSV.
Solution:
// Check for duplicates in CSV
const addresses = records.map(r => r.address.toLowerCase());
const unique = new Set(addresses);
if (addresses.length !== unique.size) {
console.error('CSV contains duplicate addresses');
}
// Check against existing pool
const existing = await client.addressPool.checkAddresses(addresses);
console.log(`${existing.duplicates.length} addresses already in pool`);3. "No available addresses in pool"
Cause: All addresses are allocated, bound, or in cooldown.
Solution:
Check Pool Status:
typescriptconst status = await client.addressPool.getStatus(); console.log(status);Generate and Import More Addresses (immediate fix)
Review Allocation Pattern (long-term fix):
- Are orders expiring properly?
- Are addresses being released?
- Is cooldown period too long?
- Is allocation rate higher than expected?
4. Address Tool: "Invalid mnemonic phrase"
Cause: Mnemonic has typos, wrong word count, or invalid words.
Solution:
# Verify mnemonic word count (12 or 24)
echo "your mnemonic words here" | wc -w
# Check for typos in words (use BIP39 word list)
# Common mistakes:
# - Extra spaces between words
# - Wrong word order
# - Words not in BIP39 list5. Verification: "Address not found in wallet"
Cause: Address doesn't belong to the mnemonic, or search range is too small.
Solution:
# Increase search range
? Search range: 5000 # Instead of default 1000
# Verify you're using correct:
# - Mnemonic phrase
# - Protocol (EVM vs Tron vs Solana)
# - Derivation path6. Low Pool Performance
Cause: Too many addresses in cooldown, frequent allocation/release cycles.
Solution:
- Increase Pool Size: More addresses = better performance
- Adjust Cooldown: Reduce if not needed for your use casetypescript
await client.config.update({ 'address_pool.cooldown_minutes': 15 // Reduce from 30 to 15 }); - Review Allocation Strategy: Are you creating too many short-lived orders?
Getting Help
If you encounter issues not covered here:
Check API Logs:
- Review error responses
- Check HTTP status codes
- Look for detailed error messages
Admin UI Diagnostics:
- Navigate to Address Pool
- Check "Pool Health" tab
- Review recent activity logs
Contact Support:
- Email: support@payin.com
- Include: pool statistics, error messages, CSV sample
- Specify: protocol, import mode, environment (testnet/mainnet)
Community:
- Discord: discord.gg/payin
Next Steps
Now that you understand address management, explore these related topics:
Essential Reading
- Order Payment Service - Learn how orders use address pool
- Deposit Service - Learn how deposits use bound addresses
- Security Guide - Mnemonic and key security best practices
API References
- Address Pool API - Complete API documentation
- Orders API - How orders allocate addresses
- Deposits API - How deposits bind addresses
Advanced Topics
- Multi-Chain Strategy - Managing addresses across chains
- High Availability - Pool management for scale
- Monitoring and Alerts - Production monitoring setup
Tools
- Admin Dashboard - Visual address pool management
- CLI Tools - Command-line address management
Summary
Key Takeaways:
✅ Address Pool enables instant payment address assignment and efficient reuse
✅ HD Wallet Mode (recommended) uses BIP44 standard with full tooling support
✅ Self-Managed Mode provides flexibility for advanced users
✅ LRU Allocation ensures fair distribution and privacy protection
✅ Cooldown Period (30 min) protects privacy and allows settlement
✅ Monitoring is critical - maintain 50%+ available addresses
✅ Security requires proper mnemonic storage and validation
Quick Reference:
| Task | Tool/Method | Time Required |
|---|---|---|
| Generate 1000 addresses | PayIn Address Tool | ~2 minutes |
| Import addresses | Admin UI or API | ~1 minute |
| Check pool status | Admin UI or API | Instant |
| Allocate address | Automatic (API call) | < 100ms |
| Release address | Automatic (order completion) | < 100ms |
Production Checklist:
- [ ] Mnemonic generated and securely stored (multiple backups)
- [ ] Addresses generated for all needed protocols (EVM, Tron, Solana)
- [ ] Addresses imported to PayIn (verified via pool status)
- [ ] Low inventory alerts configured
- [ ] Pool monitoring set up (daily checks or automated)
- [ ] Team trained on address generation process
- [ ] Emergency procedures documented
- [ ] Backup generation capability established
You're now ready to manage addresses efficiently for your PayIn payment infrastructure!