feat: Add multi-chain wallet generation and address recovery

- Implemented `genMultichainAddress.js` for generating addresses for FLO, BTC, and TRON from private keys.
- Created `recoverAddress.js` to recover addresses from a given private key, supporting multiple blockchains.
- Added `sendTRX.js` for sending TRX transactions, including WIF to hex conversion and transaction details display.
- Developed `transactionHistory.js` to fetch and display transaction history for TRON addresses with pagination and filtering options.
This commit is contained in:
void-57 2025-08-29 08:38:21 +05:30
parent 05ae9a27e8
commit ef65ac5886
12 changed files with 19024 additions and 0 deletions

Binary file not shown.

4746
css/style.css Normal file

File diff suppressed because it is too large Load Diff

1029
index.html Normal file

File diff suppressed because it is too large Load Diff

102
scripts/balance.js Normal file
View File

@ -0,0 +1,102 @@
async function getBalanceByAddress(address) {
try {
const balance = await tronWeb.trx.getBalance(address);
return balance / 1e6;
} catch (err) {
throw new Error("Failed to fetch balance: " + err.message);
}
}
async function getBalanceByPrivKey(privKey) {
try {
let rawHexKey;
// Detect WIF (BTC/FLO style)
if (/^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(privKey)) {
const decoded = coinjs.wif2privkey(privKey);
if (!decoded || !decoded.privkey) {
throw new Error("Invalid WIF private key");
}
rawHexKey = decoded.privkey;
// Detect 64-char raw hex private key
} else if (/^[0-9a-fA-F]{64}$/.test(privKey)) {
rawHexKey = privKey;
} else {
throw new Error("Unsupported private key format");
}
// Derive Tron address from private key
const tronAddress = tronWeb.address.fromPrivateKey(rawHexKey);
const balance = await getBalanceByAddress(tronAddress);
return { tronAddress, balance };
} catch (err) {
throw new Error("Invalid private key: " + err.message);
}
}
async function runBalanceCheck() {
const inputVal = document.getElementById("balanceAddr").value.trim();
const out = document.getElementById("balanceOutput");
// Set loading state
if (typeof setButtonLoading === "function") {
setButtonLoading("balanceBtn", true);
}
try {
if (inputVal.startsWith("T")) {
// Direct Tron address
const balance = await getBalanceByAddress(inputVal);
out.innerHTML = `
<div class="card balance-info">
<div class="balance-header">
<h3><i class="fas fa-coins"></i> Account Balance</h3>
</div>
<div class="balance-display"><span class="balance-amount">${balance} TRX</span></div>
<div class="account-details">
<div class="detail-row">
<label>Address:</label>
<div class="address-container">
<span class="address-text">${inputVal}</span>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${inputVal}').then(()=>notify && notify('Copied','success'))" title="Copy"><i class="fas fa-copy"></i></button>
</div>
</div>
</div>
</div>
`;
if (typeof notify === "function") notify("Balance loaded", "success");
} else {
// Treat as private key (WIF or HEX)
const { tronAddress, balance } = await getBalanceByPrivKey(inputVal);
out.innerHTML = `
<div class="card balance-info">
<div class="balance-header">
<h3><i class="fas fa-coins"></i> Account Balance</h3>
</div>
<div class="balance-display"><span class="balance-amount">${balance} TRX</span></div>
<div class="account-details">
<div class="detail-row">
<label>Derived Address:</label>
<div class="address-container">
<span class="address-text">${tronAddress}</span>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${tronAddress}').then(()=>notify && notify('Copied','success'))" title="Copy"><i class="fas fa-copy"></i></button>
</div>
</div>
</div>
</div>
`;
if (typeof notify === "function") notify("Balance loaded", "success");
}
} catch (err) {
out.innerHTML = `<div class="error-state"><i class="fas fa-triangle-exclamation"></i>${err.message}</div>`;
if (typeof notify === "function") notify(err.message, "error");
} finally {
// Clear loading state
if (typeof setButtonLoading === "function") {
setButtonLoading("balanceBtn", false);
}
}
}

1499
scripts/btcOperator.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1
scripts/components.min.js vendored Normal file

File diff suppressed because one or more lines are too long

606
scripts/floCrypto.js Normal file
View File

@ -0,0 +1,606 @@
(function (EXPORTS) {
//floCrypto v2.3.6a
/* FLO Crypto Operators */
"use strict";
const floCrypto = EXPORTS;
const p = BigInteger(
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F",
16
);
const ecparams = EllipticCurve.getSECCurveByName("secp256k1");
const ascii_alternatives = ` '\n '\n“ "\n” "\n --\n— ---\n≥ >=\n≤ <=\n≠ !=\n× *\n÷ /\n← <-\n→ ->\n↔ <->\n⇒ =>\n⇐ <=\n⇔ <=>`;
const exponent1 = () => p.add(BigInteger.ONE).divide(BigInteger("4"));
coinjs.compressed = true; //defaulting coinjs compressed to true;
function calculateY(x) {
let exp = exponent1();
// x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
return x
.modPow(BigInteger("3"), p)
.add(BigInteger("7"))
.mod(p)
.modPow(exp, p);
}
function getUncompressedPublicKey(compressedPublicKey) {
// Fetch x from compressedPublicKey
let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
const prefix = pubKeyBytes.shift(); // remove prefix
let prefix_modulus = prefix % 2;
pubKeyBytes.unshift(0); // add prefix 0
let x = new BigInteger(pubKeyBytes);
let xDecimalValue = x.toString();
// Fetch y
let y = calculateY(x);
let yDecimalValue = y.toString();
// verify y value
let resultBigInt = y.mod(BigInteger("2"));
let check = resultBigInt.toString() % 2;
if (prefix_modulus !== check) yDecimalValue = y.negate().mod(p).toString();
return {
x: xDecimalValue,
y: yDecimalValue,
};
}
function getSenderPublicKeyString() {
let privateKey = ellipticCurveEncryption.senderRandom();
var senderPublicKeyString =
ellipticCurveEncryption.senderPublicString(privateKey);
return {
privateKey: privateKey,
senderPublicKeyString: senderPublicKeyString,
};
}
function deriveSharedKeySender(receiverPublicKeyHex, senderPrivateKey) {
let receiverPublicKeyString =
getUncompressedPublicKey(receiverPublicKeyHex);
var senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
receiverPublicKeyString.x,
receiverPublicKeyString.y,
senderPrivateKey
);
return senderDerivedKey;
}
function deriveSharedKeyReceiver(senderPublicKeyString, receiverPrivateKey) {
return ellipticCurveEncryption.receiverSharedKeyDerivation(
senderPublicKeyString.XValuePublicString,
senderPublicKeyString.YValuePublicString,
receiverPrivateKey
);
}
function getReceiverPublicKeyString(privateKey) {
return ellipticCurveEncryption.receiverPublicString(privateKey);
}
function wifToDecimal(pk_wif, isPubKeyCompressed = false) {
let pk = Bitcoin.Base58.decode(pk_wif);
pk.shift();
pk.splice(-4, 4);
//If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
if (isPubKeyCompressed == true) pk.pop();
pk.unshift(0);
let privateKeyDecimal = BigInteger(pk).toString();
let privateKeyHex = Crypto.util.bytesToHex(pk);
return {
privateKeyDecimal: privateKeyDecimal,
privateKeyHex: privateKeyHex,
};
}
//generate a random Interger within range
floCrypto.randInt = function (min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(securedMathRandom() * (max - min + 1)) + min;
};
//generate a random String within length (options : alphaNumeric chars only)
floCrypto.randString = function (length, alphaNumeric = true) {
var result = "";
var characters = alphaNumeric
? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():";
for (var i = 0; i < length; i++)
result += characters.charAt(
Math.floor(securedMathRandom() * characters.length)
);
return result;
};
//Encrypt Data using public-key
floCrypto.encryptData = function (data, receiverPublicKeyHex) {
var senderECKeyData = getSenderPublicKeyString();
var senderDerivedKey = deriveSharedKeySender(
receiverPublicKeyHex,
senderECKeyData.privateKey
);
let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
let secret = Crypto.AES.encrypt(data, senderKey);
return {
secret: secret,
senderPublicKeyString: senderECKeyData.senderPublicKeyString,
};
};
//Decrypt Data using private-key
floCrypto.decryptData = function (data, privateKeyHex) {
var receiverECKeyData = {};
if (typeof privateKeyHex !== "string")
throw new Error("No private key found.");
let privateKey = wifToDecimal(privateKeyHex, true);
if (typeof privateKey.privateKeyDecimal !== "string")
throw new Error("Failed to detremine your private key.");
receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
var receiverDerivedKey = deriveSharedKeyReceiver(
data.senderPublicKeyString,
receiverECKeyData.privateKey
);
let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
let decryptMsg = Crypto.AES.decrypt(data.secret, receiverKey);
return decryptMsg;
};
//Sign data using private-key
floCrypto.signData = function (data, privateKeyHex) {
var key = new Bitcoin.ECKey(privateKeyHex);
var messageHash = Crypto.SHA256(data);
var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv);
var sighex = Crypto.util.bytesToHex(messageSign);
return sighex;
};
//Verify signatue of the data using public-key
floCrypto.verifySign = function (data, signatureHex, publicKeyHex) {
var msgHash = Crypto.SHA256(data);
var sigBytes = Crypto.util.hexToBytes(signatureHex);
var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex);
var verify = Bitcoin.ECDSA.verify(msgHash, sigBytes, publicKeyPoint);
return verify;
};
//Generates a new flo ID and returns private-key, public-key and floID
const generateNewID = (floCrypto.generateNewID = function () {
var key = new Bitcoin.ECKey(false);
key.setCompressed(true);
return {
floID: key.getBitcoinAddress(),
pubKey: key.getPubKeyHex(),
privKey: key.getBitcoinWalletImportFormat(),
};
});
Object.defineProperties(floCrypto, {
newID: {
get: () => generateNewID(),
},
hashID: {
value: (str) => {
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), {
asBytes: true,
});
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(
Crypto.SHA256(bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
},
},
tmpID: {
get: () => {
let bytes = Crypto.util.randomBytes(20);
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(
Crypto.SHA256(bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
},
},
});
//Returns public-key from private-key
floCrypto.getPubKeyHex = function (privateKeyHex) {
if (!privateKeyHex) return null;
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null) return null;
key.setCompressed(true);
return key.getPubKeyHex();
};
//Returns flo-ID from public-key or private-key
floCrypto.getFloID = function (keyHex) {
if (!keyHex) return null;
try {
var key = new Bitcoin.ECKey(keyHex);
if (key.priv == null) key.setPub(keyHex);
return key.getBitcoinAddress();
} catch {
return null;
}
};
floCrypto.getAddress = function (privateKeyHex, strict = false) {
if (!privateKeyHex) return;
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null) return null;
key.setCompressed(true);
let pubKey = key.getPubKeyHex(),
version = bitjs.Base58.decode(privateKeyHex)[0];
switch (version) {
case coinjs.priv: //BTC
return coinjs.bech32Address(pubKey).address;
case bitjs.priv: //FLO
return bitjs.pubkey2address(pubKey);
default:
return strict ? false : bitjs.pubkey2address(pubKey); //default to FLO address (if strict=false)
}
};
//Verify the private-key for the given public-key or flo-ID
floCrypto.verifyPrivKey = function (
privateKeyHex,
pubKey_floID,
isfloID = true
) {
if (!privateKeyHex || !pubKey_floID) return false;
try {
var key = new Bitcoin.ECKey(privateKeyHex);
if (key.priv == null) return false;
key.setCompressed(true);
if (isfloID && pubKey_floID == key.getBitcoinAddress()) return true;
else if (
!isfloID &&
pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase()
)
return true;
else return false;
} catch {
return null;
}
};
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
if (!Array.isArray(publicKeyList) || !publicKeyList.length) return null;
if (
!Number.isInteger(requiredSignatures) ||
requiredSignatures < 1 ||
requiredSignatures > publicKeyList.length
)
return null;
try {
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
return multisig;
} catch {
return null;
}
};
floCrypto.decodeRedeemScript = function (redeemScript) {
try {
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
return decoded;
} catch {
return null;
}
};
//Check if the given flo-id is valid or not
floCrypto.validateFloID = function (floID, regularOnly = false) {
if (!floID) return false;
try {
let addr = new Bitcoin.Address(floID);
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
return false;
return true;
} catch {
return false;
}
};
//Check if the given address (any blockchain) is valid or not
floCrypto.validateAddr = function (address, std = true, bech = true) {
let raw = decodeAddress(address);
if (!raw) return false;
if (typeof raw.version !== "undefined") {
//legacy or segwit
if (std == false) return false;
else if (
std === true ||
(!Array.isArray(std) && std === raw.version) ||
(Array.isArray(std) && std.includes(raw.version))
)
return true;
else return false;
} else if (typeof raw.bech_version !== "undefined") {
//bech32
if (bech === false) return false;
else if (
bech === true ||
(!Array.isArray(bech) && bech === raw.bech_version) ||
(Array.isArray(bech) && bech.includes(raw.bech_version))
)
return true;
else return false;
} //unknown
else return false;
};
//Check the public-key (or redeem-script) for the address (any blockchain)
floCrypto.verifyPubKey = function (pubKeyHex, address) {
let raw = decodeAddress(address);
if (!raw) return;
let pub_hash = Crypto.util.bytesToHex(
ripemd160(
Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })
)
);
if (typeof raw.bech_version !== "undefined" && raw.bytes.length == 32)
//bech32-multisig
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
return pub_hash === raw.hex;
};
//Convert the given address (any blockchain) to equivalent floID
floCrypto.toFloID = function (address, options = null) {
if (!address) return;
let raw = decodeAddress(address);
if (!raw) return;
else if (options) {
//if (optional) version check is passed
if (
typeof raw.version !== "undefined" &&
(!options.std || !options.std.includes(raw.version))
)
return;
if (
typeof raw.bech_version !== "undefined" &&
(!options.bech || !options.bech.includes(raw.bech_version))
)
return;
}
raw.bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(
Crypto.SHA256(raw.bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
};
//Convert raw address bytes to floID
floCrypto.rawToFloID = function (raw_bytes) {
if (typeof raw_bytes === "string")
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
if (raw_bytes.length != 20) return null;
raw_bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(
Crypto.SHA256(raw_bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
return bitjs.Base58.encode(raw_bytes.concat(hash.slice(0, 4)));
};
//Convert the given multisig address (any blockchain) to equivalent multisig floID
floCrypto.toMultisigFloID = function (address, options = null) {
if (!address) return;
let raw = decodeAddress(address);
if (!raw) return;
else if (options) {
//if (optional) version check is passed
if (
typeof raw.version !== "undefined" &&
(!options.std || !options.std.includes(raw.version))
)
return;
if (
typeof raw.bech_version !== "undefined" &&
(!options.bech || !options.bech.includes(raw.bech_version))
)
return;
}
if (typeof raw.bech_version !== "undefined") {
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
raw.bytes = ripemd160(raw.bytes, {
asBytes: true,
});
}
raw.bytes.unshift(bitjs.multisig);
let hash = Crypto.SHA256(
Crypto.SHA256(raw.bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
};
//Checks if the given addresses (any blockchain) are same (w.r.t keys)
floCrypto.isSameAddr = function (addr1, addr2) {
if (!addr1 || !addr2) return;
let raw1 = decodeAddress(addr1),
raw2 = decodeAddress(addr2);
if (!raw1 || !raw2) return false;
else {
if (typeof raw1.bech_version !== "undefined" && raw1.bytes.length == 32)
//bech32-multisig
raw1.hex = Crypto.util.bytesToHex(
ripemd160(raw1.bytes, { asBytes: true })
);
if (typeof raw2.bech_version !== "undefined" && raw2.bytes.length == 32)
//bech32-multisig
raw2.hex = Crypto.util.bytesToHex(
ripemd160(raw2.bytes, { asBytes: true })
);
return raw1.hex === raw2.hex;
}
};
const decodeAddress = (floCrypto.decodeAddr = function (address) {
if (!address) return;
else if (address.length == 33 || address.length == 34) {
//legacy encoding
let decode = bitjs.Base58.decode(address);
let bytes = decode.slice(0, decode.length - 4);
let checksum = decode.slice(decode.length - 4),
hash = Crypto.SHA256(
Crypto.SHA256(bytes, {
asBytes: true,
}),
{
asBytes: true,
}
);
return hash[0] != checksum[0] ||
hash[1] != checksum[1] ||
hash[2] != checksum[2] ||
hash[3] != checksum[3]
? null
: {
version: bytes.shift(),
hex: Crypto.util.bytesToHex(bytes),
bytes,
};
} else if (address.length == 42 || address.length == 62) {
//bech encoding
let decode = coinjs.bech32_decode(address);
if (decode) {
let bytes = decode.data;
let bech_version = bytes.shift();
bytes = coinjs.bech32_convert(bytes, 5, 8, false);
return {
bech_version,
hrp: decode.hrp,
hex: Crypto.util.bytesToHex(bytes),
bytes,
};
} else return null;
}
});
//Split the str using shamir's Secret and Returns the shares
floCrypto.createShamirsSecretShares = function (
str,
total_shares,
threshold_limit
) {
try {
if (str.length > 0) {
var strHex = shamirSecretShare.str2hex(str);
var shares = shamirSecretShare.share(
strHex,
total_shares,
threshold_limit
);
return shares;
}
return false;
} catch {
return false;
}
};
//Returns the retrived secret by combining the shamirs shares
const retrieveShamirSecret = (floCrypto.retrieveShamirSecret = function (
sharesArray
) {
try {
if (sharesArray.length > 0) {
var comb = shamirSecretShare.combine(
sharesArray.slice(0, sharesArray.length)
);
comb = shamirSecretShare.hex2str(comb);
return comb;
}
return false;
} catch {
return false;
}
});
//Verifies the shares and str
floCrypto.verifyShamirsSecret = function (sharesArray, str) {
if (!str) return null;
else if (retrieveShamirSecret(sharesArray) === str) return true;
else return false;
};
const validateASCII = (floCrypto.validateASCII = function (
string,
bool = true
) {
if (typeof string !== "string") return null;
if (bool) {
let x;
for (let i = 0; i < string.length; i++) {
x = string.charCodeAt(i);
if (x < 32 || x > 127) return false;
}
return true;
} else {
let x,
invalids = {};
for (let i = 0; i < string.length; i++) {
x = string.charCodeAt(i);
if (x < 32 || x > 127)
if (x in invalids) invalids[string[i]].push(i);
else invalids[string[i]] = [i];
}
if (Object.keys(invalids).length) return invalids;
else return true;
}
});
floCrypto.convertToASCII = function (string, mode = "soft-remove") {
let chars = validateASCII(string, false);
if (chars === true) return string;
else if (chars === null) return null;
let convertor,
result = string,
refAlt = {};
ascii_alternatives.split("\n").forEach((a) => (refAlt[a[0]] = a.slice(2)));
mode = mode.toLowerCase();
if (mode === "hard-unicode")
convertor = (c) =>
`\\u${("000" + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "soft-unicode")
convertor = (c) =>
refAlt[c] || `\\u${("000" + c.charCodeAt().toString(16)).slice(-4)}`;
else if (mode === "hard-remove") convertor = (c) => "";
else if (mode === "soft-remove") convertor = (c) => refAlt[c] || "";
else return null;
for (let c in chars) result = result.replaceAll(c, convertor(c));
return result;
};
floCrypto.revertUnicode = function (string) {
return string.replace(/\\u[\dA-F]{4}/gi, (m) =>
String.fromCharCode(parseInt(m.replace(/\\u/g, ""), 16))
);
};
})("object" === typeof module ? module.exports : (window.floCrypto = {}));

View File

@ -0,0 +1,78 @@
function getRandomPrivateKey() {
const array = new Uint8Array(32);
window.crypto.getRandomValues(array);
return Array.from(array)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
function generateFLOFromPrivateKey(privateKey) {
try {
let flowif = privateKey;
if (/^[0-9a-fA-F]{64}$/.test(privateKey)) {
flowif = coinjs.privkey2wif(privateKey);
}
let floprivateKey = btcOperator.convert.wif(flowif, bitjs.priv);
let floAddress = floCrypto.getFloID(floprivateKey);
if (!floAddress) {
throw new Error("No working FLO address generation method found");
}
return {
address: floAddress,
privateKey: floprivateKey, // Returns the format that actually works
};
} catch (error) {
console.warn("FLO generation not available:", error.message);
return null;
}
}
function generateBTCFromPrivateKey(privateKey) {
try {
if (typeof btcOperator === "undefined") {
throw new Error("btcOperator library not available");
}
// Convert private key to WIF format if it's hex
let wifKey = privateKey;
if (/^[0-9a-fA-F]{64}$/.test(privateKey)) {
wifKey = coinjs.privkey2wif(privateKey);
}
let btcPrivateKey = btcOperator.convert.wif(wifKey);
let btcAddress;
btcAddress = btcOperator.bech32Address(wifKey);
return {
address: btcAddress,
privateKey: btcPrivateKey,
};
} catch (error) {
console.warn("BTC generation error:", error.message);
return null;
}
}
async function generateTronWallet() {
const fullNode = "https://api.shasta.trongrid.io";
const solidityNode = "https://api.shasta.trongrid.io";
const eventServer = "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb(
fullNode,
solidityNode,
eventServer,
getRandomPrivateKey()
);
const wallet = await tronWeb.createAccount();
return {
address: wallet.address.base58,
privateKey: wallet.privateKey,
};
}
window.generateTronWallet = generateTronWallet;
window.generateBTCFromPrivateKey = generateBTCFromPrivateKey;
window.generateFLOFromPrivateKey = generateFLOFromPrivateKey;
window.getRandomPrivateKey = getRandomPrivateKey;

199
scripts/recoverAddress.js Normal file
View File

@ -0,0 +1,199 @@
function isHex64(str) {
return /^[0-9a-fA-F]{64}$/.test(str);
}
function isWif(str) {
return /^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(str); // BTC/FLO WIF regex
}
async function recoverAllAddressesFromPrivKey(privKey) {
const tronWeb = new TronWeb(
"https://api.shasta.trongrid.io",
"https://api.shasta.trongrid.io",
"https://api.shasta.trongrid.io"
);
try {
let hexPrivateKey = privKey;
let source = "Tron";
// Convert WIF to hex if needed
if (isWif(privKey)) {
const decoded = coinjs.wif2privkey(privKey);
if (!decoded || !decoded["privkey"]) {
return { error: "Invalid WIF private key" };
}
hexPrivateKey = decoded["privkey"];
source = "BTC/FLO";
} else if (!isHex64(privKey)) {
return {
error:
"Unsupported private key format. Please use Tron hex (64 characters) or BTC/FLO WIF format.",
};
}
// Generate TRON address
const tronAddress = tronWeb.address.fromPrivateKey(hexPrivateKey);
// Generate FLO address
const floWallet = generateFLOFromPrivateKey(hexPrivateKey);
// Generate BTC address
const btcWallet = generateBTCFromPrivateKey(hexPrivateKey);
return {
source,
hexPrivateKey,
tronAddress,
floWallet,
btcWallet,
};
} catch (err) {
return { error: err.message };
}
}
async function runAddressRecovery() {
const privKey = document.getElementById("recoveryPrivKey").value.trim();
const output = document.getElementById("recoveryOutput");
if (!privKey) {
output.innerHTML = `<div class="error-state"><i class="fas fa-triangle-exclamation"></i>Enter a private key</div>`;
if (typeof notify === "function") notify("Enter a private key", "error");
return;
}
// Set loading state
if (typeof setButtonLoading === "function") {
setButtonLoading("recoverBtn", true);
}
// Show notification
if (typeof notify === "function") {
notify("Recovering address...", "success", 1500);
}
const recovered = await recoverAllAddressesFromPrivKey(privKey);
if (recovered.error) {
output.innerHTML = `<div class="error-state"><i class="fas fa-triangle-exclamation"></i>${recovered.error}</div>`;
if (typeof notify === "function") notify(recovered.error, "error");
} else {
output.innerHTML = `
<div class="wallet-generated-success">
<div class="success-header">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<h3>Addresses Recovered Successfully!</h3>
<p>Your multi-blockchain addresses have been recovered. All addresses are derived from the same private key.</p>
</div>
</div>
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> TRON (TRX)</h4>
<div class="blockchain-badge primary">Primary</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> TRON Address</label>
<div class="value-container">
<code>${recovered.tronAddress}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${
recovered.tronAddress
}').then(()=>notify && notify('TRON address copied','success'))" title="Copy TRON Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> TRON Private Key</label>
<div class="value-container">
<code>${recovered.hexPrivateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${
recovered.hexPrivateKey
}').then(()=>notify && notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
${
recovered.floWallet
? `
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-coins"></i> FLO</h4>
<div class="blockchain-badge secondary">Secondary</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> FLO Address</label>
<div class="value-container">
<code>${recovered.floWallet.address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${recovered.floWallet.address}').then(()=>notify && notify('FLO address copied','success'))" title="Copy FLO Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> FLO Private Key</label>
<div class="value-container">
<code>${recovered.floWallet.privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${recovered.floWallet.privateKey}').then(()=>notify && notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`
: ""
}
${
recovered.btcWallet
? `
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fab fa-btc"></i> Bitcoin (BTC)</h4>
<div class="blockchain-badge secondary">Secondary</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> BTC Address</label>
<div class="value-container">
<code>${recovered.btcWallet.address}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${recovered.btcWallet.address}').then(()=>notify && notify('BTC address copied','success'))" title="Copy BTC Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> BTC Private Key</label>
<div class="value-container">
<code>${recovered.btcWallet.privateKey}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${recovered.btcWallet.privateKey}').then(()=>notify && notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>`
: ""
}
<div class="wallet-security-notice">
<div class="notice-icon">
<i class="fas fa-shield-alt"></i>
</div>
<div class="notice-content">
<h4>Security Reminder</h4>
<p>Keep your private key safe and secure. Never share it with anyone. Consider backing it up in a secure location.</p>
</div>
</div>
`;
if (typeof notify === "function")
notify("All addresses recovered", "success");
}
// Clear loading state
if (typeof setButtonLoading === "function") {
setButtonLoading("recoverBtn", false);
}
}

144
scripts/sendTRX.js Normal file
View File

@ -0,0 +1,144 @@
const fullNode = "https://api.shasta.trongrid.io";
const solidityNode = "https://api.shasta.trongrid.io";
const eventServer = "https://api.shasta.trongrid.io";
const tronWeb = new TronWeb(fullNode, solidityNode, eventServer);
async function sendTrx() {
let privateKey = document.getElementById("privKey").value.trim();
const toAddress = document.getElementById("toAddr").value.trim();
const amount = parseFloat(document.getElementById("amount").value) * 1e6;
const outputDiv = document.getElementById("sendOutput");
outputDiv.innerHTML = "⏳ Sending transaction...";
try {
// Derive fromAddress from private key
let fromAddress;
let source = "Tron";
// (WIF → hex if needed)
if (/^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(privateKey)) {
// Looks like WIF (BTC / FLO style)
const decoded = coinjs.wif2privkey(privateKey);
if (!decoded || !decoded.privkey) {
throw new Error("Invalid WIF private key");
}
privateKey = decoded.privkey; // hex format now
source = "BTC/FLO";
} else if (!/^[0-9a-fA-F]{64}$/.test(privateKey)) {
throw new Error("Private key must be Tron hex or valid WIF");
}
// Derive Tron address from private key
fromAddress = tronWeb.address.fromPrivateKey(privateKey);
// Build transaction
const tradeobj = await tronWeb.transactionBuilder.sendTrx(
toAddress,
amount,
fromAddress
);
// Sign transaction
const signedtxn = await tronWeb.trx.sign(tradeobj, privateKey);
// Broadcast transaction
const receipt = await tronWeb.trx.sendRawTransaction(signedtxn);
const status = receipt.result ? "✅ Success" : "❌ Failed";
const statusColor = receipt.result ? "green" : "red";
const txid = receipt.txid ? truncate(receipt.txid) : "N/A";
outputDiv.innerHTML = `
<div class="transaction-success">
<div class="success-header">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<h3>Transaction Sent Successfully!</h3>
<p>Your TRX transaction has been broadcasted to the network.</p>
</div>
</div>
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-key"></i> Private Key Used</h4>
<div class="blockchain-badge primary">${source}</div>
</div>
<div class="detail-row">
<label><i class="fas fa-key"></i> Private Key</label>
<div class="value-container">
<code>${document.getElementById("privKey").value.trim()}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${document
.getElementById("privKey")
.value.trim()}').then(()=>notify && notify('Private key copied','success'))" title="Copy Private Key">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
<div class="blockchain-section">
<div class="blockchain-header">
<h4><i class="fas fa-exchange-alt"></i> Transaction Details</h4>
<div class="blockchain-badge secondary">TRON</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> From Address</label>
<div class="value-container">
<code>${fromAddress}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${fromAddress}').then(()=>notify && notify('From address copied','success'))" title="Copy From Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> To Address</label>
<div class="value-container">
<code>${toAddress}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${toAddress}').then(()=>notify && notify('To address copied','success'))" title="Copy To Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-coins"></i> Amount</label>
<div class="value-container">
<code style="color: #10b981; font-weight: bold;">${
amount / 1e6
} TRX</code>
</div>
</div>
<div class="detail-row">
<label><i class="fas fa-hashtag"></i> Transaction Hash</label>
<div class="value-container">
<code>${txid}</code>
${
receipt.txid
? `<button class="btn-icon" onclick="navigator.clipboard.writeText('${receipt.txid}').then(()=>notify && notify('Transaction hash copied','success'))" title="Copy Transaction Hash">
<i class="fas fa-copy"></i>
</button>`
: ""
}
</div>
</div>
</div>
`;
return receipt;
} catch (err) {
outputDiv.innerHTML = `<div class="error-state"><i class="fas fa-triangle-exclamation"></i>Error: ${err.message}</div>`;
throw err;
}
}
function truncate(str, len = 12) {
if (!str) return "";
return str.length > len ? str.slice(0, 6) + "..." + str.slice(-6) : str;
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
alert("Copied: " + text);
});
}

View File

@ -0,0 +1,373 @@
const options = { method: "GET", headers: { accept: "application/json" } };
let nextUrl = null;
async function transactionHistory(url, address) {
try {
const response = await fetch(url, options);
const data = await response.json();
const historyDiv = document.getElementById("historyOutput");
historyDiv.innerHTML = "";
if (data && data.data) {
console.log(data.data);
data.data.forEach((tx) => {
const hash = tx.txID;
const block = tx.blockNumber;
const age = new Date(tx.block_timestamp).toLocaleString();
const type = tx.raw_data.contract[0].type;
let from = "";
let to = "";
let amount = "";
let extraContractLine = "";
if (type === "TransferContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
to = tronWeb.address.fromHex(v.to_address);
amount = v.amount / 1e6 + " TRX";
} else if (type === "TriggerSmartContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
const contractBase58 = tronWeb.address.fromHex(v.contract_address);
extraContractLine = `
<p><b>Contract:</b> ${contractBase58}
<button onclick="copyToClipboard('${contractBase58}')"><i class="fas fa-copy"></i></button>
</p>`;
const input = (v.data || "").startsWith("0x")
? v.data.slice(2)
: v.data || "";
const method = input.slice(0, 8).toLowerCase();
if (method === "a9059cbb" && input.length >= 8 + 64 + 64) {
const addrSlot = input.slice(8, 8 + 64);
const amountSlot = input.slice(8 + 64, 8 + 64 + 64);
const evmAddrHex = addrSlot.slice(24);
const tronHex = "41" + evmAddrHex.toLowerCase();
to = tronWeb.address.fromHex(tronHex);
const raw = BigInt("0x" + amountSlot);
amount = Number(raw) / 1e6 + " USDT";
} else {
to = "—";
amount = "—";
}
}
const result = tx.ret?.[0]?.contractRet || "UNKNOWN";
const statusColor = result === "SUCCESS" ? "green" : "red";
// create card
const card = document.createElement("div");
card.className = "tx-card";
card.innerHTML = `
<p><b>Hash:</b> ${truncate(hash)}
<button onclick="copyToClipboard('${hash}')"><i class="fas fa-copy"></i></button></p>
<p><b>Block:</b> ${block}</p>
<p><b>Age:</b> ${age}</p>
<p><b>Type:</b> ${type}</p>
<p><b>From:</b> ${from}
<button onclick="copyToClipboard('${from}')"><i class="fas fa-copy"></i></button></p>
<p><b>To:</b> ${to}
<button onclick="copyToClipboard('${to}')"><i class="fas fa-copy"></i></button></p>
${extraContractLine}
<p><b>Amount:</b> <span style="color:#0f0;font-weight:bold">${amount}</span></p>
<p><b>Status:</b> <span style="color:${statusColor}">${result}</span></p>
`;
historyDiv.appendChild(card);
});
// save nextUrl for pagination
if (data.meta && data.meta.fingerprint) {
nextUrl = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=10&fingerprint=${encodeURIComponent(
data.meta.fingerprint
)}`;
} else {
nextUrl = null;
}
}
} catch (error) {
console.error(error);
}
}
function fetchNext(address) {
if (nextUrl) {
transactionHistory(nextUrl, address);
}
}
function truncate(str, len = 12) {
if (!str) return "";
return str.length > len ? str.slice(0, 6) + "..." + str.slice(-6) : str;
}
function copyToClipboard(text) {
navigator.clipboard.writeText(text).then(() => {
alert("Copied: " + text);
});
}
// State for filtering and pagination
let __nextUrl = null;
let __prevUrls = [];
let __currentAddress = "";
let __currentTxs = [];
let __currentFilter = "all"; // all | received | sent
let __currentPage = 1;
let __currentUrl = null;
let __perPage = 10;
const __origTransactionHistory = transactionHistory;
transactionHistory = async function (url, address) {
try {
if (typeof notify === "function")
notify("Loading transactions...", "success", 1500);
const response = await fetch(url, {
method: "GET",
headers: { accept: "application/json" },
});
const data = await response.json();
const section = document.getElementById("transactionSection");
if (section) section.style.display = "block";
__currentAddress = address;
__currentUrl = url;
window.lastUsedUrl = url;
if (data && data.data) {
__currentTxs = data.data;
// track current per-page from url
const m = url.match(/limit=(\d+)/);
if (m) __perPage = parseInt(m[1], 10) || __perPage;
__renderTransactions();
if (data.meta && data.meta.fingerprint) {
__nextUrl = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=${__perPage}&fingerprint=${encodeURIComponent(
data.meta.fingerprint
)}`;
} else {
__nextUrl = null;
}
__updatePagination();
}
return data;
} catch (e) {
console.error(e);
if (typeof __origTransactionHistory === "function") {
__origTransactionHistory(url, address);
}
throw e;
}
};
function __renderTransactions() {
const list = document.getElementById("txList");
const legacy = document.getElementById("historyOutput");
if (!list) {
if (legacy) legacy.innerHTML = "";
return;
}
list.innerHTML = "";
const filtered = __currentTxs.filter((tx) => {
const type = tx.raw_data?.contract?.[0]?.type || "";
let from = "";
let to = "";
if (type === "TransferContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
to = tronWeb.address.fromHex(v.to_address);
} else if (type === "TriggerSmartContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
const input = (v.data || "").startsWith("0x")
? v.data.slice(2)
: v.data || "";
const method = input.slice(0, 8).toLowerCase();
if (method === "a9059cbb" && input.length >= 8 + 64 + 64) {
const addrSlot = input.slice(8, 8 + 64);
const evmAddrHex = addrSlot.slice(24);
const tronHex = "41" + evmAddrHex.toLowerCase();
to = tronWeb.address.fromHex(tronHex);
}
}
if (__currentFilter === "sent") return from === __currentAddress;
if (__currentFilter === "received") return to === __currentAddress;
return true;
});
if (filtered.length === 0) {
list.innerHTML =
'<div class="no-transactions"><i class="fas fa-inbox"></i>No transactions found</div>';
return;
}
filtered.forEach((tx) => {
const hash = tx.txID;
const block = tx.blockNumber;
const age = new Date(tx.block_timestamp).toLocaleString();
const type = tx.raw_data.contract[0].type;
let from = "";
let to = "";
let amountText = "";
let directionClass = "";
let icon = "fa-arrow-up";
if (type === "TransferContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
to = tronWeb.address.fromHex(v.to_address);
const amount = v.amount / 1e6;
amountText = amount + " TRX";
} else if (type === "TriggerSmartContract") {
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
const input = (v.data || "").startsWith("0x")
? v.data.slice(2)
: v.data || "";
const method = input.slice(0, 8).toLowerCase();
if (method === "a9059cbb" && input.length >= 8 + 64 + 64) {
const addrSlot = input.slice(8, 8 + 64);
const amountSlot = input.slice(8 + 64, 8 + 64 + 64);
const evmAddrHex = addrSlot.slice(24);
const tronHex = "41" + evmAddrHex.toLowerCase();
to = tronWeb.address.fromHex(tronHex);
const raw = BigInt("0x" + amountSlot);
amountText = Number(raw) / 1e6 + " USDT";
}
icon = "fa-file-signature";
}
// Set direction and icon based on transaction direction
if (from === __currentAddress) {
directionClass = "outgoing";
icon = "fa-arrow-up"; // upward arrow for sent
} else if (to === __currentAddress) {
directionClass = "incoming";
icon = "fa-arrow-down"; // downward arrow for received
} else {
directionClass = "";
icon = "fa-exchange-alt"; // default for other transactions
}
const result = tx.ret?.[0]?.contractRet || "UNKNOWN";
const statusClass = result === "SUCCESS" ? "success" : "failed";
const card = document.createElement("div");
card.className = `transaction-card ${directionClass}`;
card.innerHTML = `
<div class="tx-main">
<div class="tx-icon"><i class="fas ${icon}"></i></div>
<div class="tx-info">
<div class="tx-header">
<div>
<div class="tx-direction">${
directionClass === "incoming"
? "Received"
: directionClass === "outgoing"
? "Sent"
: type
}</div>
<div class="tx-date">${age}</div>
</div>
<div class="tx-right-info">
<div class="tx-amount ${
directionClass === "incoming" ? "incoming" : "outgoing"
}">${amountText}</div>
<div class="tx-status ${statusClass}">${result}</div>
</div>
</div>
<div class="tx-addresses">
<div class="tx-address-row"><span class="address-label">From</span><span class="address-value">${from}</span></div>
<div class="tx-address-row"><span class="address-label">To</span><span class="address-value">${
to || "—"
}</span></div>
<div class="tx-hash"><span class="hash-label">Hash</span><span class="hash-value">${hash}</span></div>
</div>
</div>
</div>`;
list.appendChild(card);
});
}
function __updatePagination() {
const nextBtn = document.getElementById("nextBtn");
const prevBtn = document.getElementById("prevBtn");
const info = document.getElementById("paginationInfo");
const pageNumbers = document.getElementById("pageNumbers");
if (nextBtn) nextBtn.disabled = !__nextUrl;
if (prevBtn) prevBtn.disabled = __prevUrls.length === 0;
if (info) info.textContent = `Page ${__currentPage}${__perPage} / page`;
if (pageNumbers) pageNumbers.innerHTML = __renderPageNumbers();
}
function setTransactionFilter(filter) {
__currentFilter = filter;
document.querySelectorAll(".filter-btn").forEach((btn) => {
btn.classList.toggle("active", btn.getAttribute("data-filter") === filter);
});
__renderTransactions();
}
function goToNextPage() {
if (__nextUrl) {
if (__currentUrl) {
__prevUrls.push({ url: __currentUrl, page: __currentPage });
}
__currentPage += 1;
transactionHistory(__nextUrl, __currentAddress);
}
}
function goToPreviousPage() {
if (__prevUrls.length === 0) return;
const prev = __prevUrls.pop();
const prevUrl = prev.url;
__currentPage = Math.max(1, prev.page || __currentPage - 1);
if (prevUrl) {
window.lastUsedUrl = prevUrl;
transactionHistory(prevUrl, __currentAddress);
}
}
// simple numeric pagination with ellipses
function __renderPageNumbers() {
const parts = [];
const push = (n, active) =>
`<div class="page-number ${active ? "active" : ""}">${n}</div>`;
// Always show 1
if (__currentPage === 1) parts.push(push(1, true));
else parts.push(push(1, false));
// Ellipsis if we're beyond page 3
if (__currentPage > 3) parts.push('<div class="page-ellipsis">…</div>');
// Middle window
const start = Math.max(2, __currentPage - 1);
const end = __nextUrl ? __currentPage + 1 : __currentPage; // if has next, show one ahead
for (let n = start; n <= end; n++) {
parts.push(push(n, n === __currentPage));
}
if (__nextUrl) parts.push('<div class="page-ellipsis">…</div>');
return parts.join("");
}
function resetHistoryState(perPage) {
__prevUrls = [];
__currentPage = 1;
__currentUrl = null;
__nextUrl = null;
__perPage = perPage || 10;
}