fix: update API endpoints to use mainnet instead of testnet and improve transaction history rendering

This commit is contained in:
void-57 2025-09-03 07:56:31 +05:30
parent 70331e19f1
commit e5bbc8f872
8 changed files with 403 additions and 276 deletions

View File

@ -2763,7 +2763,7 @@ sm-popup::part(popup) {
transform: scale(1.1); transform: scale(1.1);
} }
/* Responsive adjustments for balance info */ /* Responsive adjustments for balance info */
@media (max-width: 480px) { @media (max-width: 480px) {
.detail-row { .detail-row {
flex-direction: column; flex-direction: column;
@ -2792,6 +2792,38 @@ sm-popup::part(popup) {
} }
} }
/* Resources and fees styling - always display on separate lines */
.tx-detail-value.resources-list {
display: flex !important;
flex-direction: column !important;
align-items: flex-end !important; /* Right-align the resource items */
gap: 0.5rem !important;
text-align: right !important;
width: 100% !important;
}
/* Special styling for resource rows */
.tx-detail-row.resource-row {
display: flex;
align-items: flex-start;
justify-content: space-between;
}
.resource-item {
display: block !important;
padding: 0.25rem 0.5rem !important;
background-color: rgba(59, 130, 246, 0.05) !important;
border-radius: 0.25rem !important;
margin-bottom: 0.375rem !important;
font-family: inherit !important;
text-align: right !important;
width: fit-content !important;
margin-left: auto !important;
}
.resource-item:last-child {
margin-bottom: 0;
}
/* Responsive Design */ /* Responsive Design */
@media (max-width: 768px) { @media (max-width: 768px) {
.hamburger-btn { .hamburger-btn {
@ -4122,7 +4154,6 @@ sm-popup::part(popup) {
max-width: 200px; max-width: 200px;
} }
@media (max-width: 480px) and (max-height: 740px) { @media (max-width: 480px) and (max-height: 740px) {
sm-popup::part(popup) { sm-popup::part(popup) {
max-height: 95vh !important; max-height: 95vh !important;
@ -4426,6 +4457,24 @@ sm-popup::part(popup) {
.tx-detail-label { .tx-detail-label {
min-width: auto; min-width: auto;
} }
/* Resource items on small screens - left aligned */
.tx-detail-value.resources-list {
align-items: flex-start !important;
text-align: left !important;
}
.resource-item {
text-align: left !important;
margin-left: 0 !important;
width: fit-content !important;
}
/* Keep resource rows stacked on mobile */
.tx-detail-row.resource-row {
flex-direction: column;
align-items: flex-start;
}
} }
/* Generate Wallet Page Styles */ /* Generate Wallet Page Styles */

View File

@ -1093,7 +1093,7 @@
__historyAddress = address; __historyAddress = address;
const perPageSelect = document.getElementById("perPageSelect"); const perPageSelect = document.getElementById("perPageSelect");
const limit = perPageSelect ? parseInt(perPageSelect.value, 10) : 10; const limit = perPageSelect ? parseInt(perPageSelect.value, 10) : 10;
const url = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=${limit}`; const url = `https://api.trongrid.io/v1/accounts/${address}/transactions?limit=${limit}`;
const section = document.getElementById("transactionSection"); const section = document.getElementById("transactionSection");
if (section) section.style.display = "block"; if (section) section.style.display = "block";
resetHistoryState(limit); resetHistoryState(limit);
@ -1137,7 +1137,7 @@
perSel.addEventListener("change", () => { perSel.addEventListener("change", () => {
if (!__historyAddress) return; if (!__historyAddress) return;
const limit = parseInt(perSel.value, 10) || 10; const limit = parseInt(perSel.value, 10) || 10;
const url = `https://api.shasta.trongrid.io/v1/accounts/${__historyAddress}/transactions?limit=${limit}`; const url = `https://api.trongrid.io/v1/accounts/${__historyAddress}/transactions?limit=${limit}`;
resetHistoryState(limit); resetHistoryState(limit);
transactionHistory(url, __historyAddress); transactionHistory(url, __historyAddress);
}); });

View File

@ -38,7 +38,7 @@ function shareTxLink(txid) {
}); });
} }
async function getTransactionDetails(txHash) { async function getTransactionDetails(txHash) {
const url = "https://api.shasta.trongrid.io/wallet/gettransactionbyid"; const url = "https://api.trongrid.io/wallet/gettransactionbyid";
const headers = { const headers = {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
@ -64,7 +64,7 @@ async function getTransactionDetails(txHash) {
} }
async function getTransactionInfoById(txHash) { async function getTransactionInfoById(txHash) {
const url = "https://api.shasta.trongrid.io/wallet/gettransactioninfobyid"; const url = "https://api.trongrid.io/wallet/gettransactioninfobyid";
const headers = { const headers = {
Accept: "application/json", Accept: "application/json",
"Content-Type": "application/json", "Content-Type": "application/json",
@ -170,9 +170,12 @@ async function runBalanceCheck() {
loadHistoryFor(tronAddress); loadHistoryFor(tronAddress);
// Save searched address to IndexedDB // Save searched address to IndexedDB
if (typeof searchedAddressDB !== 'undefined') { if (typeof searchedAddressDB !== "undefined") {
try { try {
await searchedAddressDB.saveSearchedAddress(tronAddress, balance.toLocaleString()); await searchedAddressDB.saveSearchedAddress(
tronAddress,
balance.toLocaleString()
);
await updateSearchedAddressesList(); await updateSearchedAddressesList();
} catch (dbError) { } catch (dbError) {
console.warn("Failed to save address to IndexedDB:", dbError); console.warn("Failed to save address to IndexedDB:", dbError);
@ -184,7 +187,6 @@ async function runBalanceCheck() {
// Treat as private key (WIF or HEX) // Treat as private key (WIF or HEX)
const { tronAddress, balance } = await getBalanceByPrivKey(inputVal); const { tronAddress, balance } = await getBalanceByPrivKey(inputVal);
let sourceInfo = null; let sourceInfo = null;
if (/^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(inputVal)) { if (/^[5KLc9RQ][1-9A-HJ-NP-Za-km-z]{50,}$/.test(inputVal)) {
// This is a BTC/FLO WIF key // This is a BTC/FLO WIF key
@ -192,11 +194,10 @@ async function runBalanceCheck() {
type: "Private Key", type: "Private Key",
originalKey: inputVal, originalKey: inputVal,
originalAddress: inputVal, // Store the original private key for toggling originalAddress: inputVal, // Store the original private key for toggling
blockchain: /^[KL]/.test(inputVal) ? "BTC" : "FLO" blockchain: /^[KL]/.test(inputVal) ? "BTC" : "FLO",
}; };
} }
output.innerHTML = ` output.innerHTML = `
<div class="card balance-info"> <div class="card balance-info">
<div class="balance-header"> <div class="balance-header">
@ -227,9 +228,14 @@ async function runBalanceCheck() {
loadHistoryFor(tronAddress); loadHistoryFor(tronAddress);
// Save searched address to IndexedDB // Save searched address to IndexedDB
if (typeof searchedAddressDB !== 'undefined') { if (typeof searchedAddressDB !== "undefined") {
try { try {
await searchedAddressDB.saveSearchedAddress(tronAddress, balance.toLocaleString(), Date.now(), sourceInfo); await searchedAddressDB.saveSearchedAddress(
tronAddress,
balance.toLocaleString(),
Date.now(),
sourceInfo
);
await updateSearchedAddressesList(); await updateSearchedAddressesList();
} catch (dbError) { } catch (dbError) {
console.warn("Failed to save address to IndexedDB:", dbError); console.warn("Failed to save address to IndexedDB:", dbError);
@ -247,7 +253,6 @@ async function runBalanceCheck() {
} }
async function runTxSearch() { async function runTxSearch() {
const input = document.getElementById("txHash"); const input = document.getElementById("txHash");
const output = document.getElementById("txOutput"); const output = document.getElementById("txOutput");
const txid = (input.value || "").trim(); const txid = (input.value || "").trim();
@ -283,102 +288,176 @@ async function runTxSearch() {
(tx.raw_data && tx.raw_data.contract && tx.raw_data.contract[0]) || {}; (tx.raw_data && tx.raw_data.contract && tx.raw_data.contract[0]) || {};
const type = contract.type || "TransferContract"; const type = contract.type || "TransferContract";
const parameter = contract.parameter && contract.parameter.value; const parameter = contract.parameter && contract.parameter.value;
const to = (parameter && parameter.to_address) || "-";
const owner = (parameter && parameter.owner_address) || "-";
const amount =
parameter && parameter.amount ? parameter.amount / 1000000 : "-";
const timestamp = const timestamp =
tx.raw_data && tx.raw_data.timestamp tx.raw_data && tx.raw_data.timestamp
? new Date(tx.raw_data.timestamp).toLocaleString() ? new Date(tx.raw_data.timestamp).toLocaleString()
: "-"; : "-";
const fee = txInfo.fee ? txInfo.fee / 1000000 : "-";
const blockNumber = txInfo.blockNumber || "-"; const blockNumber = txInfo.blockNumber || "-";
output.innerHTML = ` // Extract resources and fees from both transaction objects
<div class="card transaction-details">
<div class="transaction-details-header"> let bandwidth = undefined;
<h3><i class="fas fa-receipt"></i> Transaction Details</h3> if (txInfo.receipt && txInfo.receipt.net_usage) {
<button bandwidth = txInfo.receipt.net_usage;
onclick="shareTxLink('${id}')" } else if (tx.net_usage) {
class="btn-icon share-btn" bandwidth = tx.net_usage;
title="Copy Shareable Link" }
>
<i class="fas fa-share-alt"></i> // Check both transaction info and receipt for energy usage
</button> let energy = undefined;
</div> if (txInfo.receipt && txInfo.receipt.energy_usage) {
<div class="transaction-details-content"> energy = txInfo.receipt.energy_usage;
<div class="tx-detail-card"> } else if (tx.energy_usage) {
<div class="tx-detail-row"> energy = tx.energy_usage;
<span class="tx-detail-label"> }
<i class="fas fa-check-circle"></i>
Status: // Check for fees from multiple sources
</span> let fee = "-";
<span class="tx-detail-value success">${ret}</span> if (txInfo.receipt && txInfo.receipt.net_fee) {
</div> fee = txInfo.receipt.net_fee / 1000000;
<div class="tx-detail-row"> } else if (txInfo.fee) {
<span class="tx-detail-label"> fee = txInfo.fee / 1000000;
<i class="fas fa-exchange-alt"></i> } else if (tx.fee) {
Type: fee = tx.fee / 1000000;
</span> }
<span class="tx-detail-value">${type}</span>
</div> // Format the resource consumption and fee HTML
<div class="tx-detail-row"> let resourcesFeeHtml = "";
<span class="tx-detail-label"> let feeParts = [];
<i class="fas fa-coins"></i> if (bandwidth && bandwidth !== "-")
Amount: feeParts.push(`<div class="resource-item">${bandwidth} Bandwidth</div>`);
</span> if (energy && energy !== "-")
<span class="tx-detail-value amount">${amount} TRX</span> feeParts.push(`<div class="resource-item">${energy} Energy</div>`);
</div> if (fee && fee !== "-")
<div class="tx-detail-row"> feeParts.push(`<div class="resource-item">${fee} TRX</div>`);
<span class="tx-detail-label">
<i class="fas fa-receipt"></i> if (feeParts.length) {
Fee: resourcesFeeHtml = `
</span> <div class='tx-detail-row resource-row'>
<span class="tx-detail-value">${fee} TRX</span> <span class='tx-detail-label'><i class='fas fa-cogs'></i> Resources Consumed & Fee:</span>
</div> <span class='tx-detail-value resources-list'>${feeParts.join(
</div> ""
<div class="tx-detail-card"> )}</span>
<div class="tx-detail-row"> </div>
<span class="tx-detail-label"> `;
<i class="fas fa-user-minus"></i> } else {
From: resourcesFeeHtml = `
</span> <div class='tx-detail-row resource-row'>
<span class="tx-detail-value">${owner}</span> <span class='tx-detail-label'><i class='fas fa-cogs'></i> Resources Consumed & Fee:</span>
</div> <span class='tx-detail-value resources-list'><span class="resource-item">- No resources consumed</span></span>
<div class="tx-detail-row"> </div>
<span class="tx-detail-label"> `;
<i class="fas fa-user-plus"></i> }
To:
</span> let detailsHtml = "";
<span class="tx-detail-value">${to}</span> let owner = (parameter && parameter.owner_address) || "-";
</div> let to = (parameter && parameter.to_address) || "-";
<div class="tx-detail-row"> let amount =
<span class="tx-detail-label"> parameter && parameter.amount ? parameter.amount / 1000000 : "-";
<i class="fas fa-hashtag"></i> let tokenInfo = "";
Hash: let resourceInfo = "";
</span>
<span class="tx-detail-value">${id}</span> if (type === "TriggerSmartContract") {
</div> let contractAddr =
</div> parameter && parameter.contract_address
<div class="tx-detail-card"> ? parameter.contract_address
<div class="tx-detail-row"> : "-";
<span class="tx-detail-label"> let tokenAmount = "-";
<i class="fas fa-layer-group"></i> let tokenSymbol = "USDT";
Block: let tokenTo = "-";
</span> if (txInfo.log && txInfo.log.length > 0) {
<span class="tx-detail-value">${blockNumber}</span> const log = txInfo.log[0];
</div> if (log.topics && log.topics.length >= 3) {
<div class="tx-detail-row"> tokenTo = tronWeb.address.fromHex("41" + log.topics[2].slice(-40));
<span class="tx-detail-label"> tokenAmount = parseInt(log.data, 16) / 1e6;
<i class="fas fa-clock"></i> }
Date: }
</span> 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>`;
<span class="tx-detail-value">${timestamp}</span> detailsHtml = `
</div> <div class='tx-detail-card'>
</div> <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> <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> ${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"); if (typeof notify === "function") notify("Transaction found", "success");
} catch (err) { } catch (err) {

View File

@ -55,9 +55,9 @@ function generateBTCFromPrivateKey(privateKey) {
} }
async function generateTronWallet() { async function generateTronWallet() {
const fullNode = "https://api.shasta.trongrid.io"; const fullNode = "https://api.trongrid.io";
const solidityNode = "https://api.shasta.trongrid.io"; const solidityNode = "https://api.trongrid.io";
const eventServer = "https://api.shasta.trongrid.io"; const eventServer = "https://api.trongrid.io";
const tronWeb = new TronWeb( const tronWeb = new TronWeb(
fullNode, fullNode,

View File

@ -7,9 +7,9 @@ function isWif(str) {
async function recoverAllAddressesFromPrivKey(privKey) { async function recoverAllAddressesFromPrivKey(privKey) {
const tronWeb = new TronWeb( const tronWeb = new TronWeb(
"https://api.shasta.trongrid.io", "https://api.trongrid.io",
"https://api.shasta.trongrid.io", "https://api.trongrid.io",
"https://api.shasta.trongrid.io" "https://api.trongrid.io"
); );
try { try {

View File

@ -125,12 +125,9 @@ class SearchedAddressDB {
} }
} }
const searchedAddressDB = new SearchedAddressDB(); const searchedAddressDB = new SearchedAddressDB();
async function displaySearchedAddresses(addresses) { async function displaySearchedAddresses(addresses) {
let container = document.getElementById("searchedAddressesContainer"); let container = document.getElementById("searchedAddressesContainer");
const transactionSection = document.getElementById("transactionSection"); const transactionSection = document.getElementById("transactionSection");
@ -169,8 +166,7 @@ async function displaySearchedAddresses(addresses) {
${addresses ${addresses
.map((addr, index) => { .map((addr, index) => {
// Check if this was converted from a private key from another blockchain (BTC/FLO) // Check if this was converted from a private key from another blockchain (BTC/FLO)
const hasSourceInfo = const hasSourceInfo = addr.sourceInfo && addr.sourceInfo.originalKey;
addr.sourceInfo && addr.sourceInfo.originalKey;
return ` return `
<div class="searched-address-item ${ <div class="searched-address-item ${
@ -292,9 +288,6 @@ async function toggleAddressType(addressIndex, type) {
addressItem.sourceInfo?.originalKey || addressItem.address; addressItem.sourceInfo?.originalKey || addressItem.address;
addressDisplay.textContent = originalKey; addressDisplay.textContent = originalKey;
addressDisplay.title = originalKey; addressDisplay.title = originalKey;
} }
} }
} catch (error) { } catch (error) {
@ -325,7 +318,9 @@ async function copyCurrentAddress(addressIndex) {
} else { } else {
// Copy the private key for non-Tron selection // Copy the private key for non-Tron selection
valueToCopy = addressItem.sourceInfo?.originalKey || addressItem.address; valueToCopy = addressItem.sourceInfo?.originalKey || addressItem.address;
valueLabel = `${addressItem.sourceInfo?.blockchain || "Original"} private key`; valueLabel = `${
addressItem.sourceInfo?.blockchain || "Original"
} private key`;
} }
await copyAddressToClipboard(valueToCopy, valueLabel); await copyAddressToClipboard(valueToCopy, valueLabel);
@ -347,16 +342,14 @@ async function deleteSearchedAddress(address) {
} }
async function clearAllSearchedAddresses() { async function clearAllSearchedAddresses() {
try {
try { await searchedAddressDB.clearAllSearchedAddresses();
await searchedAddressDB.clearAllSearchedAddresses(); await updateSearchedAddressesList();
await updateSearchedAddressesList(); notify("All searched addresses cleared", "success");
notify("All searched addresses cleared", "success"); } catch (error) {
} catch (error) { console.error("Error clearing searched addresses:", error);
console.error("Error clearing searched addresses:", error); notify("Failed to clear addresses", "error");
notify("Failed to clear addresses", "error"); }
}
} }
async function copyAddressToClipboard(address, label = "Address") { async function copyAddressToClipboard(address, label = "Address") {

View File

@ -1,6 +1,6 @@
const fullNode = "https://api.shasta.trongrid.io"; const fullNode = "https://api.trongrid.io";
const solidityNode = "https://api.shasta.trongrid.io"; const solidityNode = "https://api.trongrid.io";
const eventServer = "https://api.shasta.trongrid.io"; const eventServer = "https://api.trongrid.io";
const tronWeb = new TronWeb(fullNode, solidityNode, eventServer); const tronWeb = new TronWeb(fullNode, solidityNode, eventServer);
async function sendTrx() { async function sendTrx() {
@ -45,14 +45,20 @@ async function sendTrx() {
// Broadcast transaction // Broadcast transaction
const receipt = await tronWeb.trx.sendRawTransaction(signedtxn); const receipt = await tronWeb.trx.sendRawTransaction(signedtxn);
console.log(receipt); console.log(receipt);
if (receipt && receipt.result && receipt.result.code === "INSUFFICIENT_BALANCE") { if (
receipt &&
receipt.result &&
receipt.result.code === "INSUFFICIENT_BALANCE"
) {
throw new Error("Insufficient balance to send transaction."); throw new Error("Insufficient balance to send transaction.");
} }
if (receipt && receipt.code && receipt.code === 'CONTRACT_VALIDATE_ERROR') { if (receipt && receipt.code && receipt.code === "CONTRACT_VALIDATE_ERROR") {
throw new Error("Insufficient balance to send transaction."); throw new Error("Insufficient balance to send transaction.");
} }
if (receipt && !receipt.result) { if (receipt && !receipt.result) {
throw new Error("Transaction failed: " + (receipt.result.message || "Unknown error")); throw new Error(
"Transaction failed: " + (receipt.result.message || "Unknown error")
);
} }
const status = receipt.result ? "✅ Success" : "❌ Failed"; const status = receipt.result ? "✅ Success" : "❌ Failed";
const statusColor = receipt.result ? "green" : "red"; const statusColor = receipt.result ? "green" : "red";
@ -143,7 +149,7 @@ async function sendTrx() {
`; `;
return receipt; return receipt;
} catch (err) { } catch (err) {
const outputDiv = document.getElementById("sendOutput"); const outputDiv = document.getElementById("sendOutput");
outputDiv.innerHTML = `<div class="error-state"> outputDiv.innerHTML = `<div class="error-state">
<div class="error-icon"><i class="fas fa-exclamation-triangle"></i></div> <div class="error-icon"><i class="fas fa-exclamation-triangle"></i></div>
<h3>Transaction Failed</h3> <h3>Transaction Failed</h3>

View File

@ -1,103 +1,50 @@
const options = { method: "GET", headers: { accept: "application/json" } }; const options = { method: "GET", headers: { accept: "application/json" } };
let nextUrl = null; let nextUrl = null;
async function transactionHistory(url, address) { const transactionHistory = async function (url, address) {
try { try {
const response = await fetch(url, options); 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 data = await response.json();
const historyDiv = document.getElementById("historyOutput"); const section = document.getElementById("transactionSection");
historyDiv.innerHTML = ""; if (section) section.style.display = "block";
__currentAddress = address;
__currentUrl = url;
window.lastUsedUrl = url;
if (data && data.data) { if (data && data.data) {
console.log(data.data); console.log(data.data);
data.data.forEach((tx) => { __currentTxs = data.data;
const hash = tx.txID; // track current per-page from url
const block = tx.blockNumber; const m = url.match(/limit=(\d+)/);
const age = new Date(tx.block_timestamp).toLocaleString(); if (m) __perPage = parseInt(m[1], 10) || __perPage;
const type = tx.raw_data.contract[0].type; __renderTransactions();
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) { if (data.meta && data.meta.fingerprint) {
nextUrl = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=10&fingerprint=${encodeURIComponent( __nextUrl = `https://api.trongrid.io/v1/accounts/${address}/transactions?limit=${__perPage}&fingerprint=${encodeURIComponent(
data.meta.fingerprint data.meta.fingerprint
)}`; )}`;
} else { } else {
nextUrl = null; __nextUrl = null;
} }
__updatePagination();
} }
} catch (error) { return data;
console.error(error); } catch (e) {
console.error(e);
if (typeof __origTransactionHistory === "function") {
__origTransactionHistory(url, address);
}
throw e;
} }
} };
function fetchNext(address) { function fetchNext(address) {
if (nextUrl) { if (nextUrl) {
@ -126,50 +73,6 @@ let __currentPage = 1;
let __currentUrl = null; let __currentUrl = null;
let __perPage = 10; 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() { function __renderTransactions() {
const list = document.getElementById("txList"); const list = document.getElementById("txList");
const legacy = document.getElementById("historyOutput"); const legacy = document.getElementById("historyOutput");
@ -247,10 +150,32 @@ function __renderTransactions() {
amountText = Number(raw) / 1e6 + " USDT"; amountText = Number(raw) / 1e6 + " USDT";
} }
icon = "fa-file-signature"; 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 // Set direction and icon based on transaction direction
if (from === __currentAddress) { 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"; directionClass = "outgoing";
icon = "fa-arrow-up"; // upward arrow for sent icon = "fa-arrow-up"; // upward arrow for sent
} else if (to === __currentAddress) { } else if (to === __currentAddress) {
@ -272,7 +197,11 @@ function __renderTransactions() {
<div class="tx-header"> <div class="tx-header">
<div> <div>
<div class="tx-direction">${ <div class="tx-direction">${
directionClass === "incoming" directionClass === "delegate-resource"
? "Delegate Resources"
: directionClass === "reclaim-resource"
? "Reclaim Resources"
: directionClass === "incoming"
? "Received" ? "Received"
: directionClass === "outgoing" : directionClass === "outgoing"
? "Sent" ? "Sent"
@ -309,10 +238,22 @@ function __updatePagination() {
const prevBtn = document.getElementById("prevBtn"); const prevBtn = document.getElementById("prevBtn");
const info = document.getElementById("paginationInfo"); const info = document.getElementById("paginationInfo");
const pageNumbers = document.getElementById("pageNumbers"); const pageNumbers = document.getElementById("pageNumbers");
if (nextBtn) nextBtn.disabled = !__nextUrl; if (nextBtn) nextBtn.disabled = !__nextUrl;
if (prevBtn) prevBtn.disabled = __prevUrls.length === 0; if (prevBtn) prevBtn.disabled = __prevUrls.length === 0;
if (info) info.textContent = `Page ${__currentPage}${__perPage} / page`; if (info) info.textContent = `Page ${__currentPage}${__perPage} / page`;
if (pageNumbers) pageNumbers.innerHTML = __renderPageNumbers();
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) { function setTransactionFilter(filter) {
@ -348,7 +289,9 @@ function goToPreviousPage() {
function __renderPageNumbers() { function __renderPageNumbers() {
const parts = []; const parts = [];
const push = (n, active) => const push = (n, active) =>
`<div class="page-number ${active ? "active" : ""}">${n}</div>`; `<div class="page-number ${
active ? "active" : ""
}" onclick="goToPage(${n})">${n}</div>`;
// Always show 1 // Always show 1
if (__currentPage === 1) parts.push(push(1, true)); if (__currentPage === 1) parts.push(push(1, true));
else parts.push(push(1, false)); else parts.push(push(1, false));
@ -372,3 +315,60 @@ function resetHistoryState(perPage) {
__nextUrl = null; __nextUrl = null;
__perPage = perPage || 10; __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
);
}
}
}
}