diff --git a/index.js b/index.js index a6d35ba..6b9290e 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,5 @@ -const { exec } = require('child_process'); const express = require('express'); const crypto = require('crypto'); -const axios = require('axios'); const { config, verifyConfig } = require('./config'); const { makeRpcCallBTC } = require('./utils/bitcoin-rpc'); const { execThunderCli } = require('./utils/thunder-cli'); @@ -55,7 +53,15 @@ async function sendToAddressBTC(address, amount) { } - +async function validateAddressBTC(address) { + try { + const info = await makeRpcCallBTC(config, 'validateaddress', [address]); + return { info }; + } catch (error) { + console.error('Failed to validate BTC address:', error); + throw error; + } +} // Thunder interaction @@ -143,10 +149,20 @@ app.get('/balance', async (req, res) => { app.post('/withdraw', async (req, res) => { const { withdrawal_destination, withdrawal_amount, layer_2_chain_name } = req.body; + const missing_fields = []; // Validate required fields - if (!withdrawal_destination || !withdrawal_amount || !layer_2_chain_name) { + if (!withdrawal_destination) { + missing_fields.push('withdrawal_destination'); + } + if (!withdrawal_amount) { + missing_fields.push('withdrawal_amount'); + } + if (!layer_2_chain_name) { + missing_fields.push('layer_2_chain_name'); + } + if (missing_fields.length > 0) { return res.status(400).json({ - error: 'withdrawal_destination, withdrawal_amount, and layer_2_chain_name are required' + error: 'Missing required fields: ' + missing_fields.join(', ') }); } @@ -159,15 +175,35 @@ app.post('/withdraw', async (req, res) => { } // Validate withdrawal_amount is an integer - const amount = parseInt(withdrawal_amount); - if (isNaN(amount) || amount.toString() !== withdrawal_amount) { + const amount = Number.parseFloat(withdrawal_amount); + if (Number.isNaN(amount)) { return res.status(400).json({ - error: 'withdrawal_amount must be an integer' + error: 'withdrawal_amount must be a number' }); } - // TODO validate address format - // TODO validate amount format + if (amount <= 0) { + return res.status(400).json({ + error: 'withdrawal_amount must be positive' + }); + } + + // max withdrawal amount is 10% of our mainchain balance + const balance = await getBalanceBTC(); + const maxWithdrawalAmount = balance.info * 0.1; + if (amount > maxWithdrawalAmount) { + return res.status(400).json({ + error: `withdrawal amount is above max: ${maxWithdrawalAmount.toString()}` + }); + } + + // validate withdrawal destination address + const addressValidation = await validateAddressBTC(withdrawal_destination); + if (!addressValidation.info.isvalid) { + const error = addressValidation.info.error ? `Invalid L1 BTC address: ${addressValidation.info.error}` : "Invalid L1 BTC address"; + return res.status(400).json({ error }); + } + // Check L2 chain name if (layer_2_chain_name != "Thunder" && layer_2_chain_name != "BitNames") { @@ -216,6 +252,7 @@ app.post('/withdraw', async (req, res) => { withdrawalRequest.hash = requestHash; // Track new withdrawal request + console.log("pushing new withdrawal request: ", withdrawalRequest); withdrawalRequests.push(withdrawalRequest); res.json({