Skip to main content

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

MethodDescriptionAuth RequiredHTTP Endpoint
saveCryptoWalletSave/update crypto walletPOST /indodax/crypto-wallet/save
getCryptoWalletListList all walletsGET /indodax/crypto-wallet/list
getPrimaryCryptoWalletGet primary walletGET /indodax/crypto-wallet/primary
deleteCryptoWalletDelete walletPOST /indodax/crypto-wallet/delete
getSupportedNetworksGet supported networksGET /indodax/crypto-wallet/networks
runDailyConversionRun daily conversion✅ AdminPOST /indodax/conversion/run-daily
executeConversionExecute specific job✅ AdminPOST /indodax/conversion/execute/:job_id
getPendingConversionsGet pending conversions✅ AdminGET /indodax/conversion/pending
runBalancePollingRun balance polling✅ AdminPOST /indodax/conversion/poll-balance
pollJobBalancePoll specific job✅ AdminPOST /indodax/conversion/poll-job/:job_id
runTradeExecutionRun trade execution✅ AdminPOST /indodax/conversion/run-trades
executeTradeExecute specific trade✅ AdminPOST /indodax/conversion/execute-trade/:job_id
getConversionStatusGet job statusGET /indodax/conversion/status/:job_id
startDisbursementStart manual disbursementPOST /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 address
  • network (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:

  1. PENDING: Job created, awaiting disbursement to Indodax
  2. WAITING_INDODAX: Funds sent to Indodax, waiting for balance confirmation
  3. TRADING: Balance confirmed, executing USDC buy order
  4. WITHDRAWING: Trade completed, sending USDC to user wallet
  5. COMPLETED: USDC successfully delivered to wallet
  6. 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

  1. Always verify primary wallet existence before initiating conversions
  2. Use error handling for all conversion operations
  3. Monitor job statuses regularly for stuck or failed conversions
  4. Set up proper cron schedules for automated operations
  5. Log all operations for audit and troubleshooting
  6. Implement admin notifications for critical errors
  7. Run reconciliation before disbursements to prevent balance issues