Viewing File: /home/ubuntu/efiexchange-node-base/src/controllers/userApp/tron.controller.ts
import config from "config";
import BigNumber from "bignumber.js";
import {
CHAIN_INFO,
SupportedTokens,
SPECIAL_CONTRACT_ADDRESS,
} from "../../config/chains";
import TronWeb from "tronweb";
import * as bip39 from "bip39";
import hdkey from "hdkey";
require('dotenv').config();
import { decrypt } from '../../utils/decrypt';
var FCM = require('fcm-node');
var serverKey = config.get("FCM_SERVER_KEY");
import speakeasy from 'speakeasy';
/**
* @method transferTronTokensHandler
*
* @description transfer Tron Tokens
*
* @param req
*
* @param res
*
* @returns json response
*/
export async function transferTronTokensHandler(req: any, res: any) {
try {
let inputs = req.body;
// console.log("inputs", inputs)
if (!inputs.receiver_wallet_address) {
return res.sendError("Receiver Wallet Address is required");
}
if (!inputs.tokens) {
return res.sendError("Amount is required");
}
if (!inputs.token_type) {
return res.sendError("Token Type is required");
}
if (!inputs.network_type) {
return res.sendError("Network Type is required");
}
// const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
const rpcUrl = inputs.network_type == "TRX" ? "https://api.trongrid.io" : inputs.network_type == "SHASTA" ? "https://api.shasta.trongrid.io" : "https://nile.trongrid.io";
if(!rpcUrl){
return res.sendError("Invalid Token Type");
}
// Create a connection to the Tron devnet
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
console.log("Connection Tron @", rpcUrl)
let tx;
let responseData;
let tokensInSUN = Math.round(inputs.tokens * 1e6); // Convert TRX to SUN and round
if(!["USDC", "USDT"].includes(inputs.token_type)){
console.log("Initiating Native Currency Transfer Transaction", tokensInSUN)
tx = await tronWeb.trx.sendTransaction(inputs.receiver_wallet_address, tokensInSUN);
if(tx.txid){
responseData = {
transferTokenResponse: tx.txid,
fromWalletAddress: tronWeb.defaultAddress.base58,
};
}
} else {
let contract_address = SPECIAL_CONTRACT_ADDRESS[inputs.network_type][inputs.token_type];
const contract = await tronWeb.contract().at(contract_address);
tx = await contract.transfer(inputs.receiver_wallet_address, tokensInSUN).send();
if(tx){
responseData = {
transferTokenResponse: tx,
fromWalletAddress: tronWeb.defaultAddress.base58,
};
}
}
return res.sendResponse(responseData, "Token transfer success");
} catch (error) {
return res.sendError(error.message);
}
}
/**
* @method getTronTransactionDetails
*
* @description getTronTransactionDetails
*
* @param req
*
* @param res
*
* @returns json response
*/
export async function getTronTransactionDetails(req: any, res: any) {
try {
let inputs = req.body;
if (!inputs.transaction_hash) {
return res.sendError("Transaction Hash is required");
}
if (!inputs.token_type) {
return res.sendError("Token Type is required");
}
if (!inputs.network_type) {
return res.sendError("Network Type is required");
}
const rpcUrl = inputs.network_type == "TRX" ? "https://api.trongrid.io" : inputs.network_type == "SHASTA" ? "https://api.shasta.trongrid.io" : "https://nile.trongrid.io";
if(!rpcUrl){
return res.sendError("Invalid Token Type");
}
// Create a connection to the Tron devnet
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
const transactionInfo = await tronWeb.trx.getTransaction(inputs.transaction_hash);
const transactionReceipt = await tronWeb.trx.getTransactionInfo(inputs.transaction_hash);
let value = 0;
let tokenValue;
let receiverWalletAddress = '';
let fromWalletAddress = '';
let explorer_url = CHAIN_INFO[SupportedTokens[inputs.network_type]].explorer + "/#/transaction/" + inputs.transaction_hash;
let transactionFee = 0;
let status;
if (transactionInfo && transactionReceipt && transactionInfo.ret) {
fromWalletAddress = tronWeb.address.fromHex(transactionInfo.raw_data.contract[0].parameter.value.owner_address);
transactionFee = tronWeb.fromSun(transactionReceipt.fee)
status = transactionInfo.ret[0].contractRet;
// console.log("receive add", tronWeb.address.fromHex(transactionReceipt.log[0].address));
// Extracting value for native TRX transfers
if (transactionInfo.raw_data.contract[0].type === "TransferContract") {
receiverWalletAddress = tronWeb.address.fromHex(transactionInfo.raw_data.contract[0].parameter.value.to_address);
value = tronWeb.fromSun(transactionInfo.raw_data.contract[0].parameter.value.amount); // Convert from SUN to TRX
} else if (transactionInfo.raw_data.contract[0].type === "TriggerSmartContract") {
const { parameter } = transactionInfo.raw_data.contract[0];
const { value } = parameter;
const { data } = value;
// Decode the receiver address
const receiverAddressHex = "0x" + data.substring(10, 74).substring(24);
// receiverWalletAddress = tronWeb.address.fromHex(receiverAddressHex);
if (!transactionReceipt.log || transactionReceipt.log.length === 0) {
throw new Error("No logs found in the transaction receipt.");
}
// Extract the first log (assuming it's the Transfer event log)
const transferLog = transactionReceipt.log[0];
// Decode the `topics` to get the `to_address`
const receiverHex = "0x" + transferLog.topics[2].slice(24);
receiverWalletAddress = tronWeb.address.fromHex(receiverHex);
}
// Extracting token transfer value for TRC20 tokens (e.g., USDT, USDC) if applicable
if (transactionInfo.raw_data.contract[0].type === "TriggerSmartContract") {
const contractData = transactionInfo.raw_data.contract[0].parameter.value.data;
// Getting decimals dynamically from the token contract
let contract_address = SPECIAL_CONTRACT_ADDRESS[inputs.network_type][inputs.token_type];
// tokenValue = contract_address
const tokenContract = await tronWeb.contract().at(contract_address);
if(tokenContract){
// const decimals = await tokenContract.methods.decimals().call();
const decimals = 6;
const tokenAmountHex = contractData.substring(72);
const tokenAmountBigNum = new BigNumber("0x" + tokenAmountHex);
tokenValue = tokenAmountBigNum.dividedBy(new BigNumber(10).pow(decimals)).toString();
} else {
return res.sendError("Token contract not found");
}
}
}
let responseData = {
transactionReponse: transactionInfo,
transReceipt: transactionReceipt,
value: tokenValue || value,
transaction_fee: transactionFee,
explorer_url: explorer_url,
token_value: tokenValue || value,
from_wallet_address: fromWalletAddress,
receiver_wallet_address: receiverWalletAddress,
status: status,
};
return res.sendResponse(responseData, "Token transfer success");
} catch (error) {
return res.sendError(error.message);
}
}
export async function generateTronWalletAddress(req: any, res: any) {
try {
let inputs = req.body;
if (!inputs.user_id) {
return res.sendError("User Id is required");
}
let user_id = inputs.user_id;
const masterMnemonic = process.env.MASTER_MNEMONIC;
if (!masterMnemonic) {
return res.sendError("Master Mnemonic not found in environment variables.");
}
// Generate seed from mnemonic
const seed = await bip39.mnemonicToSeed(masterMnemonic);
// Create root node
const root = hdkey.fromMasterSeed(seed);
// Tron HD Wallet Path: "m/44'/195'/0'/0/{user_id}"
const childAccountPath = `m/44'/195'/0'/0/${user_id}`;
const childNode = root.derive(childAccountPath);
// Generate private key & address
const privateKeyHex = childNode.privateKey.toString("hex");
const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
const walletAddress = tronWeb.address.fromPrivateKey(privateKeyHex);
let responseData = {
wallet_address: walletAddress,
// privateKey: privateKeyHex,
};
return res.sendResponse(responseData, "Tron wallet address generated successfully");
} catch (error) {
return res.sendError(`Error: ${error.message}`);
}
}
export async function generateTronHDWalletAddress(req: any, res: any) {
try {
let inputs = req.body;
if (!inputs.user_id) {
return res.sendError("User Id is required");
}
if (!inputs.account_number) {
return res.sendError("Account Number is required");
}
let { user_id, account_number } = inputs;
const masterMnemonic = process.env.MASTER_MNEMONIC;
if (!masterMnemonic) {
return res.sendError("Master Mnemonic not found in environment variables.");
}
// Generate seed from mnemonic
const seed = await bip39.mnemonicToSeed(masterMnemonic);
// Create root node
const root = hdkey.fromMasterSeed(seed);
// Tron HD Wallet Path: "m/44'/195'/0'/0/{user_id}"
const childAccountPath = `m/44'/195'/${account_number}'/0/${user_id}`;
const childNode = root.derive(childAccountPath);
// Generate private key & address
const privateKeyHex = childNode.privateKey.toString("hex");
const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
const walletAddress = tronWeb.address.fromPrivateKey(privateKeyHex);
let responseData = {
wallet_address: walletAddress,
// privateKey: privateKeyHex,
};
return res.sendResponse(responseData, "Tron wallet address generated successfully");
} catch (error) {
return res.sendError(`Error: ${error.message}`);
}
}
export async function transferTronTokensToAdmin(req:any, res:any) {
const MASTER_MNEMONIC = process.env.MASTER_MNEMONIC;
const ADMIN_WALLET_PATH = "m/44'/195'/0'/0";
try {
let { user_id, amount, token_type, network_type } = req.body;
let tronWeb;
if (!user_id) return res.sendError("User ID is required");
if (!amount) return res.sendError("Amount is required");
if (!token_type) return res.sendError("Token Type is required");
if (!network_type) return res.sendError("Network Type is required");
const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
if(!rpcUrl){
return res.sendError("Invalid Token Type");
}
tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
});
// Generate Admin Wallet from Master Mnemonic
const masterNode = tronWeb.utils.HDNode.fromMnemonic(MASTER_MNEMONIC);
const adminNode = masterNode.derivePath(`${ADMIN_WALLET_PATH}/0`);
const adminAddress = tronWeb.address.fromPrivateKey(adminNode.privateKey);
// Generate Child Wallet from Master Mnemonic using user_id
const childNode = masterNode.derivePath(`${ADMIN_WALLET_PATH}/${user_id}`);
const childPrivateKey = childNode.privateKey;
const childAddress = tronWeb.address.fromPrivateKey(childPrivateKey);
tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: childPrivateKey,
});
// Check if the child address has enough balance
const balance = await tronWeb.trx.getBalance(childAddress);
const balanceInTRX = tronWeb.fromSun(balance);
if (!["USDC", "USDT"].includes(token_type)) {
const estimatedFee = 1; // Approximate TRX fee
const transferableAmount = parseFloat(amount) - estimatedFee;
if (transferableAmount <= 0) {
return res.sendError("Transaction fee overshot (fee > amount)");
}
if (balanceInTRX < amount) {
return res.sendError("Insufficient TRX balance in child wallet");
}
// Transfer TRX to Admin Wallet
const signedTx = await tronWeb.trx.sign(
await tronWeb.trx.sendTransaction(adminAddress, tronWeb.toSun(transferableAmount)),
childPrivateKey
);
const broadcast = await tronWeb.trx.sendRawTransaction(signedTx);
return res.sendResponse(
{ txID: broadcast.txid, from: childAddress, to: adminAddress },
"TRX transfer successful"
);
} else {
// Transfer TRC-20 Tokens (e.g., USDT, USDC)
let tokenContract = SPECIAL_CONTRACT_ADDRESS[network_type][token_type];
const tokenInstance = await tronWeb.contract().at(tokenContract);
const decimals = await tokenInstance.methods.decimals().call();
const tokenAmount = tronWeb.toSun(amount, decimals);
// Check Token Balance
const childTokenBalance = await tokenInstance.methods.balanceOf(childAddress).call();
if (BigInt(childTokenBalance) < BigInt(tokenAmount)) {
return res.sendError("Insufficient token balance in child wallet");
}
// Transfer TRC-20 Token
const tx = await tokenInstance.methods.transfer(adminAddress, tokenAmount).send({
from: childAddress,
});
return res.sendResponse(
{ txID: tx, from: childAddress, to: adminAddress },
"Token transfer successful"
);
}
} catch (error) {
return res.sendError(`Error: ${error.message}`);
}
}
export async function fetchTronChildWallets(req: any, res: any) {
try {
let inputs = req.body;
if (!inputs.users) {
return res.sendError("Users list is required");
}
if (!inputs.network_type) {
return res.sendError("Network Type is required");
}
if (inputs.password) {
const decryptedPassword = await decrypt(process.env.ADMIN_PASSWORD_ENCRYPTED!, process.env.ADMIN_PASSWORD_KEY!, process.env.ADMIN_PASSWORD_IV!);
if (inputs.password !== decryptedPassword) {
return res.sendError("Invalid Password");
}
} else {
return res.sendError("Password is required");
}
if (inputs.verification_code) {
const decryptedGoogleKey = await decrypt(process.env.GOOGLE_SECRET_ENCRYPTED!, process.env.GOOGLE_SECRET_KEY!, process.env.GOOGLE_SECRET_IV!);
const is2faValid = speakeasy.totp.verify({
secret: decryptedGoogleKey,
encoding: 'base32',
token: req.body.verification_code,
window: 1
});
if (!is2faValid) {
return res.sendError("Invalid verification code");
}
} else {
return res.sendError("Verification Code is required");
}
const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
const masterMnemonic = process.env.MASTER_MNEMONIC;
if (!masterMnemonic) {
return res.sendError("Master Mnemonic not found in environment variables.");
}
// Convert mnemonic to seed
const seed = await bip39.mnemonicToSeed(masterMnemonic);
const root = hdkey.fromMasterSeed(seed);
const users = inputs.users.split(",").map((num: any) => num.trim());
let userWallets = [];
for (let user of users) {
try {
// Derive child wallet
const childAccountPath = `m/44'/195'/0'/0/${user}`;
const childNode = root.derive(childAccountPath);
const privateKeyHex = childNode.privateKey.toString("hex");
const walletAddress = tronWeb.address.fromPrivateKey(privateKeyHex);
// Fetch wallet balance
const balanceSun = await tronWeb.trx.getBalance(walletAddress);
const balance = tronWeb.fromSun(balanceSun);
userWallets.push({
user_id: user,
wallet_address: walletAddress,
// private_key: privateKeyHex,
balance: Number(balance),
symbol: "TRX",
});
} catch (error) {
console.error(`Error processing user ${user}:`, error);
}
}
return res.sendResponse(userWallets, "Tron HD Wallets details fetched successfully.");
} catch (error) {
return res.sendError(`Error: ${error.message}`);
}
}
export async function fetchTronChildWalletsRange(req: any, res: any) {
try {
let inputs = req.body;
if (!inputs.skip) {
return res.sendError("Skip is required");
}
if (!inputs.take) {
return res.sendError("Take is required");
}
if (!inputs.network_type) {
return res.sendError("Network Type is required");
}
if (inputs.password) {
const decryptedPassword = await decrypt(process.env.ADMIN_PASSWORD_ENCRYPTED!, process.env.ADMIN_PASSWORD_KEY!, process.env.ADMIN_PASSWORD_IV!);
if (inputs.password !== decryptedPassword) {
return res.sendError("Invalid Password");
}
} else {
return res.sendError("Password is required");
}
if (inputs.verification_code) {
const decryptedGoogleKey = await decrypt(process.env.GOOGLE_SECRET_ENCRYPTED!, process.env.GOOGLE_SECRET_KEY!, process.env.GOOGLE_SECRET_IV!);
const is2faValid = speakeasy.totp.verify({
secret: decryptedGoogleKey,
encoding: 'base32',
token: req.body.verification_code,
window: 1
});
if (!is2faValid) {
return res.sendError("Invalid verification code");
}
} else {
return res.sendError("Verification Code is required");
}
const rpcUrl = process.env.NODE_ENV == "production" ? "https://api.trongrid.io" : "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb({
fullHost: rpcUrl,
headers: { "TRON-PRO-API-KEY": process.env.TRON_PRO_API_KEY },
privateKey: process.env.ADMIN_PRIVATE_KEY,
});
const masterMnemonic = process.env.MASTER_MNEMONIC;
if (!masterMnemonic) {
return res.sendError("Master Mnemonic not found in environment variables.");
}
// Convert mnemonic to seed
const seed = await bip39.mnemonicToSeed(masterMnemonic);
const root = hdkey.fromMasterSeed(seed);
const rangeArray = (skip:any, take:any) => Array.from({ length: Number(take) }, (_, i) => Number(skip) + i + 1);
const users = rangeArray(inputs.skip, inputs.take);
let userWallets = [];
for (let user of users) {
try {
// Derive child wallet
const childAccountPath = `m/44'/195'/0'/0/${user}`;
const childNode = root.derive(childAccountPath);
const privateKeyHex = childNode.privateKey.toString("hex");
const walletAddress = tronWeb.address.fromPrivateKey(privateKeyHex);
// Fetch wallet balance
// const balanceSun = await tronWeb.trx.getBalance(walletAddress);
// const balance = tronWeb.fromSun(balanceSun);
userWallets.push({
user_id: user,
wallet_address: walletAddress,
// private_key: privateKeyHex,
// balance: Number(balance),
symbol: "TRX",
});
} catch (error) {
console.error(`Error processing user ${user}:`, error);
}
}
return res.sendResponse(userWallets, "Tron HD Wallets details fetched successfully.");
} catch (error) {
return res.sendError(`Error: ${error.message}`);
}
}
Back to Directory
File Manager