Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 47 additions & 10 deletions index.js
Original file line number Diff line number Diff line change
@@ -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');
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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(', ')
});
}

Expand All @@ -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") {
Expand Down Expand Up @@ -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({
Expand Down