const xrpl = require('xrpl');
require('dotenv').config();
// XRP Transaction Handler
async function main() {
try {
// Parse arguments first
const args = parseCommandLineArgs();
// Validate required arguments
if (args.action === 'balance' && !args.address) {
console.error('Error: Address is required for balance check. Use --address=rXXX');
return;
}
if (args.action === 'send') {
if (!args.destAddress) {
console.error('Error: Destination address is required. Use --destAddress=rXXX');
return;
}
if (!args.amount) {
console.error('Error: Amount is required. Use --amount=X.XX');
return;
}
if (!args.sourceSeed) {
console.error('Error: Source seed is required. Use --sourceSeed=sXXX');
return;
}
}
// Connect to XRP Ledger
console.log('Connecting to XRP Ledger...');
const client = new xrpl.Client('wss://s1.ripple');
await client.connect();
console.log('Connected to XRP Ledger');
// Process the requested action
if (args.action === 'balance') {
await checkBalance(client, args.address);
} else if (args.action === 'send') {
// Create wallet object from arguments
const sourceWallet = {
seed: args.sourceSeed,
address: args.sourceAddress || '' // Address will be derived from seed if not provided
};
await sendTransaction(client, sourceWallet, args.destAddress, args.amount);
} else {
console.log('Invalid action. Use --action=balance or --action=send');
}
// Disconnect from the ledger
await client.disconnect();
console.log('Disconnected from XRP Ledger');
} catch (err) {
console.error('Error:', err);
}
}
// Parse command line arguments and environment variables
function parseCommandLineArgs() {
const args = {
action: null,
address: null,
destAddress: null,
amount: null,
sourceSeed: process.env.XRP_WALLET_SEED || null,
sourceAddress: process.env.XRP_WALLET_ADDRESS || null
};
// Get command line arguments - these override environment variables
process.argv.slice(2).forEach(arg => {
if (arg.startsWith('--')) {
const [key, value] = arg.slice(2).split('=');
args[key] = value;
}
});
// HTTP request handling for web server use
if (process.env.REQUEST_METHOD === 'GET') {
try {
const url = new URL(process.env.REQUEST_URI, 'http://localhost');
if (url.searchParams.has('address')) {
args.action = 'balance';
args.address = url.searchParams.get('address');
}
} catch (error) {
console.error('Error parsing request URI:', error);
}
}
if (process.env.REQUEST_METHOD === 'POST') {
args.action = 'send';
args.destAddress = process.env.destAddress;
args.amount = process.env.amount;
}
return args;
}
// Check XRP balance
async function checkBalance(client, address) {
try {
// Validate address
if (!xrpl.isValidAddress(address)) {
throw new Error('Invalid XRP address');
}
const response = await client.request({
command: 'account_info',
account: address,
ledger_index: 'validated'
});
const balance = xrpl.dropsToXrp(response.result.account_data.Balance);
const result = {
address: address,
balance: balance,
status: 'success',
timestamp: new Date().toISOString()
};
console.log(JSON.stringify(result, null, 2));
return result;
} catch (err) {
const errorResult = {
address: address,
status: 'error',
error: err.message,
timestamp: new Date().toISOString()
};
console.error(JSON.stringify(errorResult, null, 2));
return errorResult;
}
}
// Send XRP transaction
async function sendTransaction(client, sourceWallet, destAddress, amount) {
try {
// Validate destination address
if (!xrpl.isValidAddress(destAddress)) {
throw new Error('Invalid destination XRP address');
}
// Amount validation
const xrpAmount = parseFloat(amount);
if (isNaN(xrpAmount) || xrpAmount <= 0) {
throw new Error('Invalid amount');
}
// Create wallet from seed
const wallet = xrpl.Wallet.fromSeed(sourceWallet.seed);
console.log(`Using wallet address: ${wallet.address}`);
// Check current balance before sending
const accountInfo = await client.request({
command: 'account_info',
account: wallet.address,
ledger_index: 'validated'
});
const currentBalance = xrpl.dropsToXrp(accountInfo.result.account_data.Balance);
console.log(`Current balance: ${currentBalance} XRP`);
// Ensure sufficient balance (including reserve and transaction fee)
if (currentBalance < (xrpAmount + 0.00001)) { // Adding a small amount for fee
throw new Error(`Insufficient balance. Current balance: ${currentBalance} XRP, Attempting to send: ${xrpAmount} XRP`);
}
// Prepare transaction
const payment = {
TransactionType: 'Payment',
Account: wallet.address,
Destination: destAddress,
Amount: xrpl.xrpToDrops(xrpAmount)
};
console.log('Preparing transaction...');
const prepared = await client.autofill(payment);
const signed = wallet.sign(prepared);
console.log('Submitting transaction...');
const result = await client.submitAndWait(signed.tx_blob);
const txResult = {
sourceAddress: wallet.address,
destinationAddress: destAddress,
amount: xrpAmount,
transactionHash: result.result.hash,
status: result.result.meta.TransactionResult,
timestamp: new Date().toISOString()
};
console.log(JSON.stringify(txResult, null, 2));
return txResult;
} catch (err) {
const errorResult = {
destinationAddress: destAddress,
amount: amount,
status: 'error',
error: err.message,
timestamp: new Date().toISOString()
};
// Add source address if available
if (sourceWallet && sourceWallet.address) {
errorResult.sourceAddress = sourceWallet.address;
}
console.error(JSON.stringify(errorResult, null, 2));
return errorResult;
}
}
main();