Indodax Module
The indodax module provides comprehensive cryptocurrency conversion functionality for Indonesian market including IDR to USDC conversion, automated trading, crypto wallet management, and real-time job tracking. Built for enterprise-grade crypto operations with multi-stage conversion flow and automated withdrawal support.
Import
const indodax = await brdzSDK.indodax;
Methods Overview
| Method | Description | Auth Required | HTTP Endpoint |
|---|---|---|---|
saveCryptoWallet | Save/update crypto wallet | ✅ | POST /indodax/crypto-wallet/save |
getCryptoWalletList | List all wallets | ✅ | GET /indodax/crypto-wallet/list |
getPrimaryCryptoWallet | Get primary wallet | ✅ | GET /indodax/crypto-wallet/primary |
deleteCryptoWallet | Delete wallet | ✅ | POST /indodax/crypto-wallet/delete |
getSupportedNetworks | Get supported networks | ✅ | GET /indodax/crypto-wallet/networks |
runDailyConversion | Run daily conversion | ✅ Admin | POST /indodax/conversion/run-daily |
executeConversion | Execute specific job | ✅ Admin | POST /indodax/conversion/execute/:job_id |
getPendingConversions | Get pending conversions | ✅ Admin | GET /indodax/conversion/pending |
runBalancePolling | Run balance polling | ✅ Admin | POST /indodax/conversion/poll-balance |
pollJobBalance | Poll specific job | ✅ Admin | POST /indodax/conversion/poll-job/:job_id |
runTradeExecution | Run trade execution | ✅ Admin | POST /indodax/conversion/run-trades |
executeTrade | Execute specific trade | ✅ Admin | POST /indodax/conversion/execute-trade/:job_id |
getConversionStatus | Get job status | ✅ | GET /indodax/conversion/status/:job_id |
startDisbursement | Start manual disbursement | ✅ | POST /indodax/disbursement/start |
Crypto Wallet Operations
saveCryptoWallet
Save or update user's crypto wallet address for USDC withdrawal. Supports multiple blockchain networks with format validation.
const wallet = await brdzSDK.indodax.saveCryptoWallet({
wallet_address: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
network: "ERC20",
set_as_primary: true
});
console.log(`Action: ${wallet.data.action}`);
console.log(`Wallet ID: ${wallet.data.bw_id}`);
console.log(`Network: ${wallet.data.network}`);
console.log(`Primary: ${wallet.data.is_primary}`);
Parameters:
wallet_address(string, required): Crypto wallet addressnetwork(string, required): Network type (ERC20, TRC20, BEP20, POLYGON, SOLANA)set_as_primary(boolean, optional): Set as primary wallet (default: true)
Returns: Wallet creation/update response with action indicator
getCryptoWalletList
Retrieve all active crypto wallets for the authenticated user.
const wallets = await brdzSDK.indodax.getCryptoWalletList();
console.log(`Total wallets: ${wallets.data.total}`);
wallets.data.wallets.forEach(wallet => {
const primaryFlag = wallet.is_primary ? '⭐ PRIMARY' : '';
console.log(`${wallet.network} ${primaryFlag}`);
console.log(` Address: ${wallet.wallet_address}`);
console.log(` Chain: ${wallet.chain_id}`);
});
Returns: List of all user wallets with primary designation
getPrimaryCryptoWallet
Get user's primary crypto wallet used for USDC withdrawals.
try {
const primary = await brdzSDK.indodax.getPrimaryCryptoWallet();
console.log(`Primary Wallet: ${primary.data.network}`);
console.log(`Address: ${primary.data.wallet_address}`);
console.log(`Chain ID: ${primary.data.chain_id}`);
} catch (error) {
if (error.error_code === 'NO_PRIMARY_WALLET') {
console.log('No primary wallet set - user must add wallet first');
}
}
Returns: Primary wallet details or error if not set
deleteCryptoWallet
Soft delete (deactivate) a crypto wallet.
const result = await brdzSDK.indodax.deleteCryptoWallet({
bw_id: 123
});
console.log(result.message);
// Get updated wallet list
const wallets = await brdzSDK.indodax.getCryptoWalletList();
console.log(`Remaining wallets: ${wallets.data.total}`);
Parameters:
bw_id(number, required): Blockchain wallet ID to delete
Returns: Deletion confirmation
getSupportedNetworks
Get list of all supported blockchain networks for USDC withdrawal.
const networks = await brdzSDK.indodax.getSupportedNetworks();
console.log(`Total supported networks: ${networks.data.total}\n`);
networks.data.networks.forEach(network => {
console.log(`${network.name} (${network.code})`);
console.log(` Example: ${network.example}`);
console.log(` Indodax Code: ${network.indodax_code}\n`);
});
Returns: List of supported networks with format examples
Conversion Operations
runDailyConversion
Trigger automated daily conversion process for all eligible users.
const result = await brdzSDK.indodax.runDailyConversion();
console.log(`Users Processed: ${result.data.total_users}`);
console.log(`Jobs Created: ${result.data.jobs_created}`);
console.log(`Total IDR: ${result.data.total_idr.toLocaleString()}`);
console.log(`Disbursements: ${result.data.disbursements_initiated}`);
// Status breakdown
console.log('\nJob Status Summary:');
Object.entries(result.data.summary).forEach(([status, count]) => {
console.log(` ${status.toUpperCase()}: ${count}`);
});
Returns: Daily conversion results with job statistics
executeConversion
Manually execute a specific conversion job by ID.
try {
const result = await brdzSDK.indodax.executeConversion(123);
console.log(`Job ID: ${result.data.job_id}`);
console.log(`Amount: IDR ${result.data.batch_amount_idr.toLocaleString()}`);
console.log(`Disbursement ID: ${result.data.disbursement_id}`);
console.log(`Status: ${result.data.status}`);
} catch (error) {
if (error.error_code === 'RECONCILIATION_FAILED') {
console.error('Balance reconciliation failed');
console.error(`Difference: IDR ${error.details.difference_amount.toLocaleString()}`);
}
}
Parameters:
jobId(number, required): Conversion job ID to execute
Returns: Job execution response with disbursement details
getPendingConversions
Get list of all users with pending conversions eligible for processing.
const pending = await brdzSDK.indodax.getPendingConversions();
console.log(`Total Pending Users: ${pending.data.total}\n`);
const totalAmount = pending.data.users.reduce(
(sum, user) => sum + user.total_amount_idr,
0
);
console.log(`Total Pending Amount: IDR ${totalAmount.toLocaleString()}\n`);
pending.data.users.forEach((user, index) => {
console.log(`${index + 1}. ${user.email}`);
console.log(` Amount: IDR ${user.total_amount_idr.toLocaleString()}`);
console.log(` Wallet: ${user.wallet_network}`);
console.log(` Ready: ${user.eligible_for_conversion && user.has_primary_wallet ? '✅' : '❌'}\n`);
});
Returns: List of users with pending conversion amounts
runBalancePolling
Poll Indodax account balance to confirm funds received after disbursement.
const result = await brdzSDK.indodax.runBalancePolling();
console.log(`Jobs Checked: ${result.data.jobs_checked}`);
console.log(`Balance Confirmed: ${result.data.balance_confirmed}`);
console.log(`Still Waiting: ${result.data.still_waiting}`);
// Indodax balance
console.log(`\nIndodax Balance:`);
console.log(` IDR: ${result.data.indodax_balance.idr.toLocaleString()}`);
console.log(` USDC: ${result.data.indodax_balance.usdc}`);
Returns: Balance polling results with Indodax account balance
pollJobBalance
Manually poll Indodax balance for a specific conversion job.
const result = await brdzSDK.indodax.pollJobBalance(123);
console.log(`Job ID: ${result.data.job_id}`);
console.log(`Status: ${result.data.status}`);
console.log(`Balance Confirmed: ${result.data.balance_confirmed ? '✅' : '❌'}`);
console.log(`Expected Amount: IDR ${result.data.expected_amount.toLocaleString()}`);
console.log(`Indodax Balance: IDR ${result.data.indodax_balance.toLocaleString()}`);
if (result.data.balance_confirmed) {
console.log('\n✅ Ready for trade execution');
}
Parameters:
jobId(number, required): Conversion job ID to poll
Returns: Job balance polling result
runTradeExecution
Execute USDC trades on Indodax for all jobs in TRADING status.
const result = await brdzSDK.indodax.runTradeExecution();
console.log(`Jobs Checked: ${result.data.jobs_checked}`);
console.log(`Trades Executed: ${result.data.trades_executed}`);
console.log(`\nTrading Summary:`);
console.log(` IDR Spent: ${result.data.total_idr_spent.toLocaleString()}`);
console.log(` USDC Received: ${result.data.total_usdc_received}`);
console.log(` Avg Price: ${(result.data.total_idr_spent / result.data.total_usdc_received).toFixed(2)} IDR/USDC`);
console.log(`\nWithdrawals Initiated: ${result.data.withdrawals_initiated}`);
Returns: Trade execution results with trading statistics
executeTrade
Manually execute USDC trade for a specific conversion job.
try {
const result = await brdzSDK.indodax.executeTrade(123);
console.log(`Job ID: ${result.data.job_id}`);
console.log(`Trade ID: ${result.data.trade_id}`);
console.log(`\nTrade Details:`);
console.log(` USDC Price: ${result.data.usdc_price} IDR`);
console.log(` IDR Spent: ${result.data.idr_spent.toLocaleString()}`);
console.log(` USDC Received: ${result.data.usdc_received}`);
console.log(` Trade Fee: ${result.data.trade_fee} IDR`);
console.log(`\nWithdrawal:`);
console.log(` Withdrawal ID: ${result.data.withdrawal_id}`);
console.log(` Address: ${result.data.wallet_address}`);
console.log(` Network: ${result.data.network}`);
} catch (error) {
if (error.error_code === 'INSUFFICIENT_INDODAX_BALANCE') {
console.error('Insufficient Indodax balance');
}
}
Parameters:
jobId(number, required): Conversion job ID to execute trade for
Returns: Trade execution response with withdrawal details
getConversionStatus
Get detailed status and progress of a conversion job.
const status = await brdzSDK.indodax.getConversionStatus(123);
console.log(`Job ID: ${status.data.job_id}`);
console.log(`Status: ${status.data.status}`);
console.log(`Amount: IDR ${status.data.batch_amount_idr.toLocaleString()}`);
if (status.data.trade_id) {
console.log(`\nTrade Details:`);
console.log(` Trade ID: ${status.data.trade_id}`);
console.log(` USDC Received: ${status.data.usdc_received}`);
}
if (status.data.withdrawal_id) {
console.log(`\nWithdrawal Details:`);
console.log(` Withdrawal ID: ${status.data.withdrawal_id}`);
console.log(` Address: ${status.data.wallet_address}`);
console.log(` Network: ${status.data.network}`);
}
// Status progression
console.log(`\nStatus History:`);
status.data.status_history.forEach(h => {
console.log(` ${h.status}: ${h.timestamp}`);
});
Parameters:
jobId(number, required): Conversion job ID
Returns: Complete job status with history and details
Disbursement Operations
startDisbursement
Manually trigger disbursement from Xendit balance to Indodax VA for crypto conversion. User can initiate their own disbursement without waiting for daily cron.
try {
const result = await brdzSDK.indodax.startDisbursement({
wallet_id: 59,
amount_idr: 500000
});
console.log('✅ Disbursement initiated!');
console.log(`Job ID: ${result.data.job_id}`);
console.log(`Disbursement ID: ${result.data.disbursement_id}`);
console.log(`Reference ID: ${result.data.reference_id}`);
console.log(`Amount: IDR ${result.data.amount_idr.toLocaleString()}`);
console.log(`Status: ${result.data.status}`);
// Check reconciliation
if (result.data.reconciliation.requires_admin_attention) {
console.log('⚠️ Reconciliation requires admin attention');
} else {
console.log('✅ Reconciliation passed');
}
// Track conversion status
setTimeout(async () => {
const status = await brdzSDK.indodax.getConversionStatus(result.data.job_id);
console.log(`Current Status: ${status.data.status}`);
}, 5000);
} catch (error) {
if (error.error_code === 'AMOUNT_BELOW_MINIMUM') {
console.error('❌ Amount too low - minimum is IDR 200,000');
} else if (error.error_code === 'RECONCILIATION_FAILED') {
console.error('❌ Balance reconciliation failed');
console.error(`Difference: IDR ${error.details.difference_amount.toLocaleString()}`);
} else if (error.error_code === 'INSUFFICIENT_XENDIT_BALANCE') {
console.error('❌ Insufficient Xendit balance');
} else {
console.error(`Error: ${error.message}`);
}
}
Response Structure:
success: true,
message: "Disbursement to Indodax initiated successfully",
data: {
job_id: 123,
disbursement_id: "disb_1234567890abcdef",
reference_id: "INDODAX-DISB-123-1705308000000",
amount_idr: 500000,
status: "DISBURSING",
reconciliation: {
reconciliation_id: 456,
status: "MATCHED",
requires_admin_attention: false
}
},
timestamp: "2024-01-15T10:00:00Z"
}
Error Codes:
- MISSING_REQUIRED_FIELDS: wallet_id and amount_idr are required
- INVALID_AMOUNT: Amount must be a positive number
- AMOUNT_BELOW_MINIMUM: Minimum disbursement amount is IDR 200,000
- WALLET_NOT_FOUND: Wallet not found or access denied
- INSUFFICIENT_WALLET_BALANCE: Wallet balance insufficient for disbursement
- RECONCILIATION_FAILED: Pre-disbursement reconciliation failed
- INSUFFICIENT_XENDIT_BALANCE: Xendit account balance insufficient
- DISBURSEMENT_FAILED: Failed to start disbursement
Parameters:
- wallet_id (number, required): User's IDR wallet ID
- amount_idr (number, required): Amount in IDR (minimum 200,000)
Returns: Disbursement response with job_id, disbursement_id, and reconciliation status
Complete Usage Examples
Basic Indodax Setup
// Configure SDK
const config = await brdzSDK.config;
config.setApiKey('your-api-key');
config.setToken('your-jwt-token');
// Check if user has wallet
try {
const primary = await brdzSDK.indodax.getPrimaryCryptoWallet();
console.log('User has primary wallet:', primary.data.wallet_address);
} catch (error) {
if (error.error_code === 'NO_PRIMARY_WALLET') {
console.log('User needs to add crypto wallet first');
// Get supported networks
const networks = await brdzSDK.indodax.getSupportedNetworks();
console.log('Supported networks:', networks.data.networks.map(n => n.code));
}
}
Complete Wallet Setup Workflow
async function setupWalletForConversion(walletAddress, network) {
console.log('Setting up crypto wallet...\n');
try {
// Step 1: Check supported networks
const networks = await brdzSDK.indodax.getSupportedNetworks();
const supportedNetwork = networks.data.networks.find(
n => n.code.toUpperCase() === network.toUpperCase()
);
if (!supportedNetwork) {
throw new Error(`Network ${network} is not supported`);
}
console.log(`✅ Network ${supportedNetwork.name} is supported`);
// Step 2: Save wallet
const wallet = await brdzSDK.indodax.saveCryptoWallet({
wallet_address: walletAddress,
network: network,
set_as_primary: true
});
console.log(`✅ Wallet ${wallet.data.action}: ${wallet.data.bw_id}`);
console.log(`Network: ${wallet.data.network}`);
console.log(`Primary: ${wallet.data.is_primary}\n`);
// Step 3: Verify primary wallet
const primary = await brdzSDK.indodax.getPrimaryCryptoWallet();
console.log(`✅ Primary wallet confirmed: ${primary.data.wallet_address}`);
return {
success: true,
wallet_id: wallet.data.bw_id,
network: wallet.data.network
};
} catch (error) {
console.error(`❌ Setup failed: ${error.message}`);
return {
success: false,
error: error.message
};
}
}
// Usage
await setupWalletForConversion(
"0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"ERC20"
);
Track Conversion Job Progress
async function trackConversionJob(jobId) {
console.log(`Tracking conversion job ${jobId}...\n`);
const statusMap = {
'PENDING': 'Waiting for disbursement',
'WAITING_INDODAX': 'Waiting for Indodax balance confirmation',
'TRADING': 'Executing USDC trade',
'WITHDRAWING': 'Withdrawing USDC to wallet',
'COMPLETED': 'Conversion completed',
'FAILED': 'Conversion failed'
};
let lastStatus = '';
let attempts = 0;
const maxAttempts = 120; // 10 minutes
while (attempts < maxAttempts) {
try {
const status = await brdzSDK.indodax.getConversionStatus(jobId);
const currentStatus = status.data.status;
if (currentStatus !== lastStatus) {
console.log(`\n[${new Date().toLocaleTimeString()}] ${currentStatus}`);
console.log(`${statusMap[currentStatus]}`);
if (status.data.trade_id) {
console.log(`USDC Received: ${status.data.usdc_received}`);
}
lastStatus = currentStatus;
}
if (currentStatus === 'COMPLETED') {
console.log('\n✅ Conversion completed!');
console.log(`Amount: IDR ${status.data.batch_amount_idr.toLocaleString()}`);
console.log(`USDC: ${status.data.usdc_received}`);
console.log(`Wallet: ${status.data.wallet_address}`);
return status.data;
} else if (currentStatus === 'FAILED') {
console.log('\n❌ Conversion failed');
return status.data;
}
await new Promise(resolve => setTimeout(resolve, 5000));
attempts++;
} catch (error) {
console.error(`Error: ${error.message}`);
break;
}
}
console.log('\n⏰ Timeout: Job still in progress');
return null;
}
// Usage
await trackConversionJob(123);
Admin Daily Conversion Process
async function performDailyConversionProcess() {
console.log('=== DAILY CONVERSION PROCESS ===\n');
// Step 1: Check pending conversions
console.log('Step 1: Checking pending conversions...');
const pending = await brdzSDK.indodax.getPendingConversions();
console.log(`Total pending users: ${pending.data.total}`);
const totalAmount = pending.data.users.reduce(
(sum, user) => sum + user.total_amount_idr,
0
);
console.log(`Total amount: IDR ${totalAmount.toLocaleString()}\n`);
// Step 2: Run daily conversion
console.log('Step 2: Running daily conversion...');
const conversion = await brdzSDK.indodax.runDailyConversion();
console.log(`✅ Jobs created: ${conversion.data.jobs_created}`);
console.log(`Disbursements: ${conversion.data.disbursements_initiated}\n`);
// Step 3: Run balance polling
console.log('Step 3: Running balance polling...');
const polling = await brdzSDK.indodax.runBalancePolling();
console.log(`Jobs checked: ${polling.data.jobs_checked}`);
console.log(`Balance confirmed: ${polling.data.balance_confirmed}\n`);
// Step 4: Run trade execution
if (polling.data.balance_confirmed > 0) {
console.log('Step 4: Running trade execution...');
const trades = await brdzSDK.indodax.runTradeExecution();
console.log(`Trades executed: ${trades.data.trades_executed}`);
console.log(`USDC received: ${trades.data.total_usdc_received}\n`);
}
console.log('=== PROCESS COMPLETE ===');
return {
pending: pending.data.total,
jobs_created: conversion.data.jobs_created,
balance_confirmed: polling.data.balance_confirmed,
trades_executed: polling.data.trades_executed || 0
};
}
// Usage (run as cron job)
await performDailyConversionProcess();
Manual Job Execution (Admin)
async function manualJobExecution(jobId) {
console.log(`=== MANUAL JOB EXECUTION: ${jobId} ===\n`);
try {
// Check current status
let status = await brdzSDK.indodax.getConversionStatus(jobId);
console.log(`Current Status: ${status.data.status}\n`);
// Execute based on status
if (status.data.status === 'PENDING') {
console.log('Executing conversion job...');
await brdzSDK.indodax.executeConversion(jobId);
console.log('✅ Disbursement initiated\n');
await new Promise(resolve => setTimeout(resolve, 3000));
status = await brdzSDK.indodax.getConversionStatus(jobId);
}
if (status.data.status === 'WAITING_INDODAX') {
console.log('Polling Indodax balance...');
const polling = await brdzSDK.indodax.pollJobBalance(jobId);
if (polling.data.balance_confirmed) {
console.log('✅ Balance confirmed\n');
} else {
console.log('⏳ Balance not yet confirmed\n');
return;
}
await new Promise(resolve => setTimeout(resolve, 3000));
status = await brdzSDK.indodax.getConversionStatus(jobId);
}
if (status.data.status === 'TRADING') {
console.log('Executing USDC trade...');
const trade = await brdzSDK.indodax.executeTrade(jobId);
console.log(`✅ Trade executed: ${trade.data.trade_id}`);
console.log(`USDC Received: ${trade.data.usdc_received}\n`);
}
// Final status
const finalStatus = await brdzSDK.indodax.getConversionStatus(jobId);
console.log(`Final Status: ${finalStatus.data.status}`);
return finalStatus.data;
} catch (error) {
console.error(`❌ Execution failed: ${error.message}`);
throw error;
}
}
// Usage
await manualJobExecution(123);
User Conversion Monitoring
async function monitorMyConversion(jobId) {
console.log('Monitoring your conversion...\n');
const status = await brdzSDK.indodax.getConversionStatus(jobId);
const statusText = {
'PENDING': '⏳ Your conversion is being processed',
'WAITING_INDODAX': '⏳ Waiting for exchange confirmation',
'TRADING': '📊 Trading USDC on exchange',
'WITHDRAWING': '💸 Sending USDC to your wallet',
'COMPLETED': '✅ Conversion completed!',
'FAILED': '❌ Conversion failed'
};
console.log(statusText[status.data.status]);
console.log(`Amount: IDR ${status.data.batch_amount_idr.toLocaleString()}\n`);
if (status.data.status === 'COMPLETED') {
console.log('Your USDC has been sent!');
console.log(`USDC Received: ${status.data.usdc_received}`);
console.log(`Wallet: ${status.data.wallet_address}`);
console.log(`Network: ${status.data.network}`);
} else if (status.data.status === 'FAILED') {
console.log('Please contact support for assistance.');
} else {
console.log('Your conversion is in progress.');
console.log('Estimated completion: 2-12 hours');
}
return status.data;
}
// Usage
await monitorMyConversion(123);
Error Handling
// Robust error handling for Indodax operations
class IndodaxErrorHandler {
static async safeConversion(jobId) {
try {
return await brdzSDK.indodax.executeConversion(jobId);
} catch (error) {
return this.handleConversionError(error);
}
}
static handleConversionError(error) {
const errorMap = {
'NO_PRIMARY_WALLET': {
message: 'User has not set up crypto wallet',
action: 'Prompt user to add wallet',
retry: false
},
'INVALID_WALLET_FORMAT': {
message: 'Wallet address format is invalid',
action: 'Verify wallet address format',
retry: false
},
'RECONCILIATION_FAILED': {
message: 'Balance reconciliation failed',
action: 'Admin must review reconciliation',
retry: false
},
'INSUFFICIENT_INDODAX_BALANCE': {
message: 'Indodax account has insufficient balance',
action: 'Top up Indodax account',
retry: false
},
'TRADE_EXECUTION_FAILED': {
message: 'Trade execution failed',
action: 'Check Indodax API status',
retry: true
}
};
const errorCode = error.error_code || 'UNKNOWN_ERROR';
const errorInfo = errorMap[errorCode] || {
message: error.message,
action: 'Contact support',
retry: false
};
console.error(`Error: ${errorInfo.message}`);
console.log(`Action: ${errorInfo.action}`);
return {
success: false,
error_code: errorCode,
...errorInfo
};
}
static async notifyAdmin(subject, data) {
console.log(`🚨 ADMIN ALERT: ${subject}`);
console.log(JSON.stringify(data, null, 2));
}
}
// Usage
const result = await IndodaxErrorHandler.safeConversion(123);
if (!result.success && result.retry) {
console.log('Safe to retry this operation');
}
Platform-Specific Integration
// Automated conversion monitoring
class IndodaxMonitor {
static async monitorPendingConversions() {
const pending = await brdzSDK.indodax.getPendingConversions();
const totalAmount = pending.data.users.reduce(
(sum, user) => sum + user.total_amount_idr,
0
);
if (totalAmount > 100000000) { // 100M IDR
console.warn(`⚠️ High pending amount: IDR ${totalAmount.toLocaleString()}`);
await this.notifyAdmin('High Pending Conversions', {
total_users: pending.data.total,
total_amount: totalAmount
});
}
// Check users without wallets
const noWallet = pending.data.users.filter(u => !u.has_primary_wallet);
if (noWallet.length > 0) {
console.warn(`⚠️ ${noWallet.length} users missing primary wallet`);
}
return pending.data;
}
static async checkJobStatuses() {
// Get all pending conversions and check their statuses
const pending = await brdzSDK.indodax.getPendingConversions();
const statusCounts = {
PENDING: 0,
WAITING_INDODAX: 0,
TRADING: 0,
WITHDRAWING: 0,
COMPLETED: 0,
FAILED: 0
};
for (const user of pending.data.users) {
// Check each user's latest job status
// Implementation depends on job tracking mechanism
}
console.log('Job Status Summary:');
Object.entries(statusCounts).forEach(([status, count]) => {
console.log(` ${status}: ${count}`);
});
return statusCounts;
}
static async notifyAdmin(subject, data) {
console.log(`🚨 ADMIN ALERT: ${subject}`);
console.log(JSON.stringify(data, null, 2));
}
}
// Schedule monitoring
setInterval(async () => {
await IndodaxMonitor.monitorPendingConversions();
await IndodaxMonitor.checkJobStatuses();
}, 60 * 60 * 1000); // Every hour
Cron Job Configuration
const cron = require('node-cron');
// Daily conversion at 2 AM
cron.schedule('0 2 * * *', async () => {
console.log('Running daily conversion...');
await brdzSDK.indodax.runDailyConversion();
});
// Balance polling every hour
cron.schedule('0 * * * *', async () => {
console.log('Running balance polling...');
await brdzSDK.indodax.runBalancePolling();
});
// Trade execution every 2 hours
cron.schedule('0 */2 * * *', async () => {
console.log('Running trade execution...');
await brdzSDK.indodax.runTradeExecution();
});
// Monitor pending conversions every 6 hours
cron.schedule('0 */6 * * *', async () => {
console.log('Monitoring pending conversions...');
await IndodaxMonitor.monitorPendingConversions();
});
Conversion Flow States
The conversion process follows these states:
- PENDING: Job created, awaiting disbursement to Indodax
- WAITING_INDODAX: Funds sent to Indodax, waiting for balance confirmation
- TRADING: Balance confirmed, executing USDC buy order
- WITHDRAWING: Trade completed, sending USDC to user wallet
- COMPLETED: USDC successfully delivered to wallet
- FAILED: Error occurred, requires manual review
Network Support
Supported blockchain networks for USDC withdrawal:
- ERC-20 (Ethereum):
0x+ 40 hex characters - TRC-20 (Tron):
T+ 33 alphanumeric characters - BEP-20 (BSC):
0x+ 40 hex characters - Polygon:
0x+ 40 hex characters - Solana: 32-44 base58 characters
Important Notes
- Users must set up a primary crypto wallet before conversions can be processed
- Complete conversion typically takes 2-12 hours depending on processing stages
- Admin should monitor Indodax account balance to ensure sufficient IDR for trading
- All conversion operations require proper authentication and appropriate role permissions
- Failed conversions require manual admin review and resolution
Best Practices
- Always verify primary wallet existence before initiating conversions
- Use error handling for all conversion operations
- Monitor job statuses regularly for stuck or failed conversions
- Set up proper cron schedules for automated operations
- Log all operations for audit and troubleshooting
- Implement admin notifications for critical errors
- Run reconciliation before disbursements to prevent balance issues