Workflow updating files of tronwallet

This commit is contained in:
RanchiMall Dev 2026-01-12 11:44:18 +00:00
parent 602075ab8e
commit f9801ca065
16 changed files with 20344 additions and 0 deletions

37
tronwallet/README.md Normal file
View File

@ -0,0 +1,37 @@
# RanchiMall Tron Web Wallet
## Introduction
This project implements the RanchiMall Tron Web Wallet, providing multi-chain wallet functionality and seamless integration between Tron, FLO, and Bitcoin blockchains.
## Functions required for RanchiMall Tron Web Wallet
1. **Address Lookup**
- Allow search for any Tron blockchain address and display full transaction history.
2. **FLO Private Key Integration**
- Enable sending of TRX using a valid FLO blockchain private key or using TRON blockchain address private key of the sender.
3. **Multi-Chain Address Generation**
- On creating a new Tron address, automatically generate and display:
- Equivalent FLO address
- Equivalent Bitcoin address
- Associated private keys for all three
4. **Private Key-Based Address Recovery**
- Derive the original Tron address from a valid FLO, Bitcoin, or Tron private key.
5. **Balance Retrieval**
- Show TRX balance for any address, using:
- Tron blockchain address, or
- Corresponding FLO / Bitcoin private keys
6. **Token Transfer**
- Enable sending of TRX using:
- Tron private key, or
- Its corresponding/equivalent FLO and Bitcoin private keys

Binary file not shown.

5093
tronwallet/css/style.css Normal file

File diff suppressed because it is too large Load Diff

11
tronwallet/favicon.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 2680.2 2915.7" style="enable-background:new 0 0 2680.2 2915.7;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EB0029;}
</style>
<path class="st0" d="M1929.1,757.7L332,463.8l840.5,2114.9l1171.1-1426.8L1929.1,757.7z M1903.4,887.2l244.3,232.2l-668.2,121
L1903.4,887.2z M1334.4,1216.2L630.1,632.1l1151.1,211.8L1334.4,1216.2z M1284.2,1319.5l-114.8,949.4L550.2,710.7L1284.2,1319.5z
M1390.5,1369.9l739.9-134l-848.7,1034L1390.5,1369.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 729 B

1170
tronwallet/index.html Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,470 @@
function updateURLForPage(page, value) {
let params = new URLSearchParams();
if (page === "transaction") {
params.set("page", "transactions");
params.set("tx", value);
} else if (page === "balance" || page === "history") {
params.set("page", "transactions");
params.set("address", value);
}
window.history.replaceState(
{},
"",
`${location.pathname}?${params.toString()}`
);
}
function shareBalanceLink(address) {
const addr =
address || (document.getElementById("balanceAddr").value || "").trim();
if (!addr) return;
const url = new URL(window.location.href);
url.searchParams.set("page", "transactions");
url.searchParams.set("address", addr);
navigator.clipboard.writeText(url.toString()).then(() => {
if (typeof notify === "function")
notify("Shareable balance link copied", "success");
});
}
function shareTxLink(txid) {
const id = txid || (document.getElementById("txHash").value || "").trim();
if (!id) return;
const url = new URL(window.location.href);
url.searchParams.set("page", "transactions");
url.searchParams.set("tx", id);
navigator.clipboard.writeText(url.toString()).then(() => {
if (typeof notify === "function")
notify("Shareable tx link copied", "success");
});
}
async function getTransactionDetails(txHash) {
const url = "https://api.trongrid.io/wallet/gettransactionbyid";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
};
const body = JSON.stringify({
value: txHash,
visible: true,
});
const response = await fetch(url, {
method: "POST",
headers,
body,
});
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log("Transaction details:", data);
return data;
}
async function getTransactionInfoById(txHash) {
const url = "https://api.trongrid.io/wallet/gettransactioninfobyid";
const headers = {
Accept: "application/json",
"Content-Type": "application/json",
};
const body = JSON.stringify({
value: txHash,
});
const response = await fetch(url, {
method: "POST",
headers,
body,
});
if (!response.ok) {
throw new Error(`Error ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log("Transaction info:", data);
return data;
}
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;
console.log("Detected WIF private key:", rawHexKey);
// 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 output = document.getElementById("balanceOutput");
// Set loading state
if (typeof setButtonLoading === "function") {
setButtonLoading("balanceBtn", true);
}
try {
if (inputVal.startsWith("T")) {
// Direct Tron address
const tronAddress = inputVal;
const balance = await getBalanceByAddress(inputVal);
output.innerHTML = `
<div class="card balance-info">
<div class="balance-header">
<h3><i class="fas fa-wallet"></i> Account Balance</h3>
<button class="btn-icon share-btn" onclick="shareBalanceLink('${tronAddress}')" title="Copy shareable balance link">
<i class="fas fa-share-alt"></i>
</button>
</div>
<div class="balance-display">
<div class="balance-amount">
<span class="amount-number">${balance.toLocaleString()} TRX</span>
</div>
</div>
<div class="account-details">
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> Address</label>
<div class="value-container">
<code>${tronAddress}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${tronAddress}').then(()=>notify && notify('Address copied','success'))" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
`;
if (typeof notify === "function") notify("Balance loaded", "success");
loadHistoryFor(tronAddress);
// Save searched address to IndexedDB
if (typeof searchedAddressDB !== "undefined") {
try {
await searchedAddressDB.saveSearchedAddress(
tronAddress,
balance.toLocaleString()
);
await updateSearchedAddressesList();
} catch (dbError) {
console.warn("Failed to save address to IndexedDB:", dbError);
}
}
updateURLForPage("balance", tronAddress);
} else {
// Treat as private key (WIF or HEX)
const { tronAddress, balance } = await getBalanceByPrivKey(inputVal);
let sourceInfo = null;
if (/^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(inputVal)) {
// This is a BTC/FLO WIF key
sourceInfo = {
type: "Private Key",
originalKey: inputVal,
originalAddress: inputVal, // Store the original private key for toggling
blockchain: /^[KL]/.test(inputVal) ? "BTC" : "FLO",
};
}
output.innerHTML = `
<div class="card balance-info">
<div class="balance-header">
<h3><i class="fas fa-wallet"></i> Account Balance</h3>
<button class="btn-icon share-btn" onclick="shareBalanceLink('${tronAddress}')" title="Copy shareable balance link">
<i class="fas fa-share-alt"></i>
</button>
</div>
<div class="balance-display">
<div class="balance-amount">
<span class="amount-number">${balance.toLocaleString()} TRX</span>
</div>
</div>
<div class="account-details">
<div class="detail-row">
<label><i class="fas fa-map-marker-alt"></i> Address</label>
<div class="value-container">
<code>${tronAddress}</code>
<button class="btn-icon" onclick="navigator.clipboard.writeText('${tronAddress}').then(()=>notify && notify('Address copied','success'))" title="Copy Address">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
`;
if (typeof notify === "function") notify("Balance loaded", "success");
loadHistoryFor(tronAddress);
// Save searched address to IndexedDB
if (typeof searchedAddressDB !== "undefined") {
try {
await searchedAddressDB.saveSearchedAddress(
tronAddress,
balance.toLocaleString(),
Date.now(),
sourceInfo
);
await updateSearchedAddressesList();
} catch (dbError) {
console.warn("Failed to save address to IndexedDB:", dbError);
}
}
updateURLForPage("balance", tronAddress);
}
} catch (err) {
output.innerHTML = `<div class="error-state"><div class="error-icon"><i class=\"fas fa-exclamation-triangle\"></i></div><h3>Failed</h3><p>${err.message}</p></div>`;
if (typeof notify === "function") notify(err.message, "error");
} finally {
setButtonLoading("balanceBtn", false);
}
}
async function runTxSearch() {
const input = document.getElementById("txHash");
const output = document.getElementById("txOutput");
const txid = (input.value || "").trim();
if (!txid) {
alert("Please enter a transaction hash");
return;
}
// Validation for Tron transaction hash
if (!/^[a-fA-F0-9]{64}$/.test(txid)) {
if (typeof notify === "function") {
notify("Invalid transaction hash format.", "error");
} else {
alert("Invalid transaction hash format.");
}
return;
}
setButtonLoading("txSearchBtn", true);
try {
if (typeof notify === "function")
notify("Searching transaction...", "success", 1200);
const [tx, txInfo] = await Promise.all([
getTransactionDetails(txid),
getTransactionInfoById(txid),
]);
// Extract transaction details from the response
const id = tx.txID || txid;
const ret = (tx.ret && tx.ret[0] && tx.ret[0].contractRet) || "SUCCESS";
const contract =
(tx.raw_data && tx.raw_data.contract && tx.raw_data.contract[0]) || {};
const type = contract.type || "TransferContract";
const parameter = contract.parameter && contract.parameter.value;
const timestamp =
tx.raw_data && tx.raw_data.timestamp
? new Date(tx.raw_data.timestamp).toLocaleString()
: "-";
const blockNumber = txInfo.blockNumber || "-";
// Extract resources and fees from both transaction objects
let bandwidth = undefined;
if (txInfo.receipt && txInfo.receipt.net_usage) {
bandwidth = txInfo.receipt.net_usage;
} else if (tx.net_usage) {
bandwidth = tx.net_usage;
}
// Check both transaction info and receipt for energy usage
let energy = undefined;
if (txInfo.receipt && txInfo.receipt.energy_usage) {
energy = txInfo.receipt.energy_usage;
} else if (tx.energy_usage) {
energy = tx.energy_usage;
}
// Check for fees from multiple sources
let fee = "-";
if (txInfo.receipt && txInfo.receipt.net_fee) {
fee = txInfo.receipt.net_fee / 1000000;
} else if (txInfo.fee) {
fee = txInfo.fee / 1000000;
} else if (tx.fee) {
fee = tx.fee / 1000000;
}
// Format the resource consumption and fee HTML
let resourcesFeeHtml = "";
let feeParts = [];
if (bandwidth && bandwidth !== "-")
feeParts.push(`<div class="resource-item">${bandwidth} Bandwidth</div>`);
if (energy && energy !== "-")
feeParts.push(`<div class="resource-item">${energy} Energy</div>`);
if (fee && fee !== "-")
feeParts.push(`<div class="resource-item">${fee} TRX</div>`);
if (feeParts.length) {
resourcesFeeHtml = `
<div class='tx-detail-row resource-row'>
<span class='tx-detail-label'><i class='fas fa-cogs'></i> Resources Consumed & Fee:</span>
<span class='tx-detail-value resources-list'>${feeParts.join(
""
)}</span>
</div>
`;
} else {
resourcesFeeHtml = `
<div class='tx-detail-row resource-row'>
<span class='tx-detail-label'><i class='fas fa-cogs'></i> Resources Consumed & Fee:</span>
<span class='tx-detail-value resources-list'><span class="resource-item">- No resources consumed</span></span>
</div>
`;
}
let detailsHtml = "";
let owner = (parameter && parameter.owner_address) || "-";
let to = (parameter && parameter.to_address) || "-";
let amount =
parameter && parameter.amount ? parameter.amount / 1000000 : "-";
let tokenInfo = "";
let resourceInfo = "";
if (type === "TriggerSmartContract") {
let contractAddr =
parameter && parameter.contract_address
? parameter.contract_address
: "-";
let tokenAmount = "-";
let tokenSymbol = "USDT";
let tokenTo = "-";
if (txInfo.log && txInfo.log.length > 0) {
const log = txInfo.log[0];
if (log.topics && log.topics.length >= 3) {
tokenTo = tronWeb.address.fromHex("41" + log.topics[2].slice(-40));
tokenAmount = parseInt(log.data, 16) / 1e6;
}
}
tokenInfo = `<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-coins'></i> Amount:</span><span class='tx-detail-value amount'>${tokenAmount} USDT</span></div>`;
detailsHtml = `
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-check-circle'></i> Status:</span><span class='tx-detail-value success'>${ret}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-exchange-alt'></i> Type:</span><span class='tx-detail-value'>${type}</span></div>
${tokenInfo}
</div>
<div class='tx-detail-card'>
${resourcesFeeHtml}
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-minus'></i> From:</span><span class='tx-detail-value'>${owner}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-plus'></i> To:</span><span class='tx-detail-value'>${tokenTo}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-hashtag'></i> Hash:</span><span class='tx-detail-value'>${id}</span></div>
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-layer-group'></i> Block:</span><span class='tx-detail-value'>${blockNumber}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-clock'></i> Date:</span><span class='tx-detail-value'>${timestamp}</span></div>
</div>
`;
} else if (
type === "DelegateResourceContract" ||
type === "UnDelegateResourceContract"
) {
// Resource delegation/undelegation
let resourceType =
parameter && parameter.resource ? parameter.resource : "BANDWIDTH";
let stakedAmount =
amount !== "-" ? `${amount} TRX` : `${parameter.balance / 1e6} TRX`;
let resourceTakenFrom =
parameter && parameter.receiver_address
? parameter.receiver_address
: "-";
let stakedAssetHtml = "";
let delegatedHtml = "";
let reclaimedHtml = "";
if (type === "DelegateResourceContract") {
stakedAssetHtml = `<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-coins'></i> Staked Asset Withheld:</span><span class='tx-detail-value amount'>${stakedAmount} </span></div>`;
delegatedHtml = `<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-leaf'></i> Delegated Resources:</span><span class='tx-detail-value'>${resourceType}</span></div>`;
} else if (type === "UnDelegateResourceContract") {
stakedAssetHtml = `<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-coins'></i> Staked Asset Released:</span><span class='tx-detail-value amount'>${stakedAmount}</span></div>`;
reclaimedHtml = `<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-leaf'></i> Reclaimed Resources:</span><span class='tx-detail-value'>${resourceType}</span></div>`;
}
detailsHtml = `
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-check-circle'></i> Status:</span><span class='tx-detail-value success'>${ret}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-exchange-alt'></i> Type:</span><span class='tx-detail-value'>${type}</span></div>
${stakedAssetHtml}
${delegatedHtml}
${reclaimedHtml}
</div>
<div class='tx-detail-card'>
${resourcesFeeHtml}
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-minus'></i> Owner Address:</span><span class='tx-detail-value'>${owner}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-plus'></i> Resource Taken From:</span><span class='tx-detail-value'>${resourceTakenFrom}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-hashtag'></i> Hash:</span><span class='tx-detail-value'>${id}</span></div>
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-layer-group'></i> Block:</span><span class='tx-detail-value'>${blockNumber}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-clock'></i> Date:</span><span class='tx-detail-value'>${timestamp}</span></div>
</div>
`;
} else {
// Default rendering (TransferContract, etc)
detailsHtml = `
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-check-circle'></i> Status:</span><span class='tx-detail-value success'>${ret}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-exchange-alt'></i> Type:</span><span class='tx-detail-value'>${type}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-coins'></i> Amount:</span><span class='tx-detail-value amount'>${amount} TRX</span></div>
${resourcesFeeHtml}
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-minus'></i> From:</span><span class='tx-detail-value'>${owner}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-user-plus'></i> To:</span><span class='tx-detail-value'>${to}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-hashtag'></i> Hash:</span><span class='tx-detail-value'>${id}</span></div>
</div>
<div class='tx-detail-card'>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-layer-group'></i> Block:</span><span class='tx-detail-value'>${blockNumber}</span></div>
<div class='tx-detail-row'><span class='tx-detail-label'><i class='fas fa-clock'></i> Date:</span><span class='tx-detail-value'>${timestamp}</span></div>
</div>
`;
}
output.innerHTML = `<div class='card transaction-details'><div class='transaction-details-header'><h3><i class='fas fa-receipt'></i> Transaction Details</h3><button onclick="shareTxLink('${id}')" class='btn-icon share-btn' title='Copy Shareable Link'><i class='fas fa-share-alt'></i></button></div><div class='transaction-details-content'>${detailsHtml}</div></div>`;
if (typeof notify === "function") notify("Transaction found", "success");
} catch (err) {
output.innerHTML = `<div class="error-state"><div class="error-icon"><i class="fas fa-exclamation-triangle"></i></div><h3>Failed</h3><p>${err.message}</p></div>`;
if (typeof notify === "function") notify(err.message, "error");
} finally {
setButtonLoading("txSearchBtn", false);
}
updateURLForPage("transaction", txid);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1
tronwallet/scripts/components.min.js vendored Normal file

File diff suppressed because one or more lines are too long

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.trongrid.io";
const solidityNode = "https://api.trongrid.io";
const eventServer = "https://api.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;

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.trongrid.io",
"https://api.trongrid.io",
"https://api.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);
}
}

View File

@ -0,0 +1,387 @@
// IndexedDB for storing searched addresses
class SearchedAddressDB {
constructor() {
this.dbName = "TronWalletDB";
this.version = 1;
this.storeName = "searchedAddresses";
this.db = null;
}
async init() {
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, this.version);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve();
};
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(this.storeName)) {
const store = db.createObjectStore(this.storeName, {
keyPath: "address",
});
store.createIndex("timestamp", "timestamp", { unique: false });
}
};
});
}
async saveSearchedAddress(
address,
balance,
timestamp = Date.now(),
sourceInfo = null
) {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
// check if this address already exists
const getRequest = store.get(address);
getRequest.onsuccess = () => {
const existingRecord = getRequest.result;
let finalSourceInfo = sourceInfo;
// If record exists and has sourceInfo, preserve it unless we're providing new sourceInfo
if (existingRecord && existingRecord.sourceInfo && !sourceInfo) {
finalSourceInfo = existingRecord.sourceInfo;
}
// If existing record has sourceInfo and new one doesn't, keep the existing one
else if (
existingRecord &&
existingRecord.sourceInfo &&
sourceInfo === null
) {
finalSourceInfo = existingRecord.sourceInfo;
}
const data = {
address, // Tron address
balance,
timestamp,
formattedBalance: `${balance} TRX`,
sourceInfo: finalSourceInfo, //original blockchain info if converted from private key
};
const putRequest = store.put(data);
putRequest.onsuccess = () => resolve();
putRequest.onerror = () => reject(putRequest.error);
};
getRequest.onerror = () => reject(getRequest.error);
});
}
async getSearchedAddresses() {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readonly");
const store = transaction.objectStore(this.storeName);
const index = store.index("timestamp");
// Get all records sorted by timestamp (newest first)
const request = index.getAll();
request.onsuccess = () => {
const results = request.result.sort(
(a, b) => b.timestamp - a.timestamp
);
resolve(results);
};
request.onerror = () => reject(request.error);
});
}
async deleteSearchedAddress(address) {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const request = store.delete(address);
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
async clearAllSearchedAddresses() {
if (!this.db) await this.init();
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], "readwrite");
const store = transaction.objectStore(this.storeName);
const request = store.clear();
request.onsuccess = () => resolve();
request.onerror = () => reject(request.error);
});
}
}
const searchedAddressDB = new SearchedAddressDB();
async function displaySearchedAddresses(addresses) {
let container = document.getElementById("searchedAddressesContainer");
const transactionSection = document.getElementById("transactionSection");
if (!container && addresses.length > 0) {
container = document.createElement("div");
container.id = "searchedAddressesContainer";
container.className = "card searched-addresses-card";
if (transactionSection && transactionSection.parentNode) {
const nextSibling = transactionSection.nextSibling;
transactionSection.parentNode.insertBefore(container, nextSibling);
} else {
const transactionsPage = document.getElementById("transactionsPage");
if (transactionsPage) {
transactionsPage.appendChild(container);
}
}
}
if (!container) return;
if (addresses.length === 0) {
container.style.display = "none";
return;
}
container.style.display = "block";
container.innerHTML = `
<div class="searched-addresses-header">
<h3><i class="fas fa-history"></i> Searched Addresses</h3>
<button onclick="clearAllSearchedAddresses()" class="btn-clear-all" title="Clear all">
<i class="fas fa-trash"></i> Clear All
</button>
</div>
<div class="searched-addresses-list">
${addresses
.map((addr, index) => {
// Check if this was converted from a private key from another blockchain (BTC/FLO)
const hasSourceInfo = addr.sourceInfo && addr.sourceInfo.originalKey;
return `
<div class="searched-address-item ${
hasSourceInfo ? "has-source-info" : ""
}" data-index="${index}" data-current-type="${
hasSourceInfo ? addr.sourceInfo.blockchain.toLowerCase() : "tron"
}">
${
hasSourceInfo
? `
<div class="address-toggle-section">
<div class="address-toggle-group">
<button onclick="toggleAddressType(${index}, '${addr.sourceInfo.blockchain.toLowerCase()}')"
class="btn-toggle-address active"
data-type="${addr.sourceInfo.blockchain.toLowerCase()}"
title="Show ${addr.sourceInfo.blockchain} Private Key">
${addr.sourceInfo.blockchain}
</button>
<button onclick="toggleAddressType(${index}, 'tron')"
class="btn-toggle-address"
data-type="tron"
title="Show Tron Address">
TRON
</button>
</div>
</div>
<div class="address-content-wrapper">
<div class="address-info">
<div class="address-display">
<div class="address-text" id="address-display-${index}" title="${
addr.sourceInfo.originalKey
}">
${addr.sourceInfo.originalKey}
</div>
</div>
</div>
<div class="address-actions">
<button onclick="copyCurrentAddress(${index})" class="btn-copy-current" title="Copy Selected Value">
<i class="fas fa-copy"></i> COPY
</button>
<button onclick="deleteSearchedAddress('${
addr.address
}')" class="btn-delete" title="Delete">
<i class="fas fa-trash"></i>
</button>
<button onclick="checkBalanceForAddress('${
addr.address
}')" class="btn-check" title="Check balance">
Check Balance
</button>
</div>
</div>
`
: `
<div class="address-info">
<div class="address-display">
<div class="address-text" id="address-display-${index}" title="${addr.address}">
${addr.address}
</div>
</div>
</div>
<div class="address-actions">
<button onclick="copyAddressToClipboard('${addr.address}')" class="btn-copy" title="Copy Tron Address">
<i class="fas fa-copy"></i> COPY
</button>
<button onclick="deleteSearchedAddress('${addr.address}')" class="btn-delete" title="Delete">
<i class="fas fa-trash"></i>
</button>
<button onclick="checkBalanceForAddress('${addr.address}')" class="btn-check" title="Check balance">
Check Balance
</button>
</div>
`
}
</div>
`;
})
.join("")}
</div>
`;
}
// Toggle between address types in searched addresses
async function toggleAddressType(addressIndex, type) {
try {
// Get the searched addresses list
const addresses = await searchedAddressDB.getSearchedAddresses();
if (!addresses[addressIndex]) return;
const addressItem = addresses[addressIndex];
const container = document.querySelector(`[data-index="${addressIndex}"]`);
if (!container) return;
// Update toggle button states
const toggleButtons = container.querySelectorAll(".btn-toggle-address");
toggleButtons.forEach((btn) => btn.classList.remove("active"));
const activeButton = container.querySelector(`[data-type="${type}"]`);
if (activeButton) {
activeButton.classList.add("active");
}
// Store the current selection in the container data
container.setAttribute("data-current-type", type);
// Update the displayed address text based on selection
const addressDisplay = container.querySelector(
`#address-display-${addressIndex}`
);
if (addressDisplay) {
if (type === "tron") {
// Tron address
addressDisplay.textContent = addressItem.address;
addressDisplay.title = addressItem.address;
} else {
// Show original blockchain private key (FLO/BTC)
const originalKey =
addressItem.sourceInfo?.originalKey || addressItem.address;
addressDisplay.textContent = originalKey;
addressDisplay.title = originalKey;
}
}
} catch (error) {
console.error("Error toggling address type:", error);
}
}
// Copy the currently selected address
async function copyCurrentAddress(addressIndex) {
try {
// Get the searched addresses list
const addresses = await searchedAddressDB.getSearchedAddresses();
if (!addresses[addressIndex]) return;
const addressItem = addresses[addressIndex];
const container = document.querySelector(`[data-index="${addressIndex}"]`);
if (!container) return;
// Get the current selection type
const currentType = container.getAttribute("data-current-type") || "tron";
let valueToCopy;
let valueLabel;
if (currentType === "tron") {
valueToCopy = addressItem.address;
valueLabel = "Tron address";
} else {
// Copy the private key for non-Tron selection
valueToCopy = addressItem.sourceInfo?.originalKey || addressItem.address;
valueLabel = `${
addressItem.sourceInfo?.blockchain || "Original"
} private key`;
}
await copyAddressToClipboard(valueToCopy, valueLabel);
} catch (error) {
console.error("Error copying current value:", error);
notify("Failed to copy value", "error");
}
}
async function deleteSearchedAddress(address) {
try {
await searchedAddressDB.deleteSearchedAddress(address);
await updateSearchedAddressesList();
notify("Address removed from history", "success");
} catch (error) {
console.error("Error deleting searched address:", error);
notify("Failed to remove address", "error");
}
}
async function clearAllSearchedAddresses() {
try {
await searchedAddressDB.clearAllSearchedAddresses();
await updateSearchedAddressesList();
notify("All searched addresses cleared", "success");
} catch (error) {
console.error("Error clearing searched addresses:", error);
notify("Failed to clear addresses", "error");
}
}
async function copyAddressToClipboard(address, label = "Address") {
try {
await navigator.clipboard.writeText(address);
notify(`${label} copied to clipboard`, "success");
} catch (error) {
console.error("Error copying to clipboard:", error);
notify("Failed to copy address", "error");
}
}
function checkBalanceForAddress(address) {
document.getElementById("balanceAddr").value = address;
setSearchType("balance");
runBalanceCheck();
}
async function updateSearchedAddressesList() {
try {
const searchedAddresses = await searchedAddressDB.getSearchedAddresses();
displaySearchedAddresses(searchedAddresses);
} catch (error) {
console.error("Error loading searched addresses:", error);
}
}
// Initialize the searched addresses list when the script loads
document.addEventListener("DOMContentLoaded", async () => {
try {
await updateSearchedAddressesList();
} catch (error) {
console.error("Failed to initialize searched addresses:", error);
}
});

View File

@ -0,0 +1,172 @@
const fullNode = "https://api.trongrid.io";
const solidityNode = "https://api.trongrid.io";
const eventServer = "https://api.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);
console.log(receipt);
if (
receipt &&
receipt.result &&
receipt.result.code === "INSUFFICIENT_BALANCE"
) {
throw new Error("Insufficient balance to send transaction.");
}
if (receipt && receipt.code && receipt.code === "CONTRACT_VALIDATE_ERROR") {
throw new Error("Insufficient balance to send transaction.");
}
if (receipt && !receipt.result) {
throw new Error(
"Transaction failed: " + (receipt.result.message || "Unknown error")
);
}
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-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>
<a
class="detail-link"
href="index.html?page=transactions&address=${fromAddress}"
target="_blank"
rel="noopener"
title="View address details"
>${fromAddress}</a>
</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>
<a
class="detail-link"
href="index.html?page=transactions&address=${toAddress}"
target="_blank"
rel="noopener"
title="View address details"
>${toAddress}</a>
</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>
<a
class="detail-link"
href="index.html?page=transactions&tx=${receipt.txid}"
target="_blank"
rel="noopener"
title="View transaction details"
>${txid}</a>
</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) {
const outputDiv = document.getElementById("sendOutput");
outputDiv.innerHTML = `<div class="error-state">
<div class="error-icon"><i class="fas fa-exclamation-triangle"></i></div>
<h3>Transaction Failed</h3>
<p>${err.message}</p>
</div>`;
if (typeof notify === "function") notify(err.message, "error");
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,374 @@
const options = { method: "GET", headers: { accept: "application/json" } };
let nextUrl = null;
const 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) {
console.log(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.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 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;
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";
} else if (
type === "DelegateResourceContract" ||
type === "UnDelegateResourceContract"
) {
// Handle resource delegation/undelegation
const v = tx.raw_data.contract[0].parameter.value;
from = tronWeb.address.fromHex(v.owner_address);
to = v.receiver_address
? tronWeb.address.fromHex(v.receiver_address)
: "";
amountText =
v.balance / 1e6 +
" TRX (" +
(v.resource ? v.resource : "Bandwidth") +
")";
directionClass = "resource";
}
// Set direction and icon based on transaction direction
if (type === "DelegateResourceContract") {
directionClass = "delegate-resource";
icon = "fa-exchange-alt"; // custom icon for delegate
} else if (type === "UnDelegateResourceContract") {
directionClass = "reclaim-resource";
icon = "fa-exchange-alt"; // custom icon for undelegate
} else 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 === "delegate-resource"
? "Delegate Resources"
: directionClass === "reclaim-resource"
? "Reclaim Resources"
: 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"
onclick="window.open('index.html?page=transactions&address=${from}','_blank')"
title="View address details">${from}</span></div>
<div class="tx-address-row"><span class="address-label">To</span><span class="address-value"
onclick="window.open('index.html?page=transactions&address=${to}','_blank')"
title="View address details">${to}</span></div>
<div class="tx-hash"><span class="hash-label">Hash</span><span class="hash-value"><span class="detail-link"
onclick="window.open('index.html?page=transactions&tx=${hash}','_blank')"
title="View transaction details">${hash}</span></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();
document.querySelectorAll(".page-number").forEach((button) => {
const pageNum = parseInt(button.textContent);
if (!isNaN(pageNum)) {
button.style.cursor = "pointer";
button.addEventListener("click", () => goToPage(pageNum));
}
});
}
}
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" : ""
}" onclick="goToPage(${n})">${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;
}
// Function to go to a specific page number
function goToPage(pageNumber) {
if (pageNumber === __currentPage) {
return; // Already on the page
}
// If going to page 1, just reset and load initial data
if (pageNumber === 1) {
__prevUrls = [];
__currentPage = 1;
const baseUrl = `https://api.trongrid.io/v1/accounts/${__currentAddress}/transactions?limit=${__perPage}`;
transactionHistory(baseUrl, __currentAddress);
return;
}
// If trying to go forward
if (pageNumber > __currentPage) {
// We can only go one page forward at a time due to API pagination limitations
if (pageNumber === __currentPage + 1 && __nextUrl) {
goToNextPage();
return;
}
}
// If trying to go backward
if (pageNumber < __currentPage) {
// Check if we have the page in our history
const targetPrevUrl = __prevUrls.find((prev) => prev.page === pageNumber);
if (targetPrevUrl) {
// We found the exact page in history
while (
__prevUrls.length > 0 &&
__prevUrls[__prevUrls.length - 1].page >= pageNumber
) {
__prevUrls.pop();
}
__currentPage = pageNumber;
transactionHistory(targetPrevUrl.url, __currentAddress);
return;
} else {
// We need to go back to page 1 and build our way up
const baseUrl = `https://api.trongrid.io/v1/accounts/${__currentAddress}/transactions?limit=${__perPage}`;
__prevUrls = [];
__currentPage = 1;
transactionHistory(baseUrl, __currentAddress);
if (typeof notify === "function") {
notify(
"Navigating to page 1 due to API pagination limitations",
"info",
3000
);
}
}
}
}