diff --git a/css/style.css b/css/style.css index 0c91d98..5f9afd2 100644 --- a/css/style.css +++ b/css/style.css @@ -1294,7 +1294,7 @@ body { } .tx-direction.offer-create { - color: #87ceeb; + color: #87ceeb; } .transaction-card.offer .tx-direction { @@ -1331,7 +1331,7 @@ body { } .transaction-card.offer .tx-amount { - color: #87ceeb; + color: #87ceeb; } .transaction-card.offer .tx-direction, @@ -2763,7 +2763,7 @@ sm-popup::part(popup) { transform: scale(1.1); } -/* Responsive adjustments for balance info */ + /* Responsive adjustments for balance info */ @media (max-width: 480px) { .detail-row { flex-direction: column; @@ -2791,7 +2791,39 @@ sm-popup::part(popup) { text-overflow: ellipsis; } } - + + /* 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 */ @media (max-width: 768px) { .hamburger-btn { @@ -4122,7 +4154,6 @@ sm-popup::part(popup) { max-width: 200px; } - @media (max-width: 480px) and (max-height: 740px) { sm-popup::part(popup) { max-height: 95vh !important; @@ -4202,7 +4233,7 @@ sm-popup::part(popup) { padding: 0.75rem 1rem; font-size: 0.8rem; font-weight: 600; - min-height: 44px; + min-height: 44px; } /* Success popup specific adjustments */ @@ -4426,6 +4457,24 @@ sm-popup::part(popup) { .tx-detail-label { 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 */ diff --git a/index.html b/index.html index fd0f947..3084398 100644 --- a/index.html +++ b/index.html @@ -1093,7 +1093,7 @@ __historyAddress = address; const perPageSelect = document.getElementById("perPageSelect"); 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"); if (section) section.style.display = "block"; resetHistoryState(limit); @@ -1137,7 +1137,7 @@ perSel.addEventListener("change", () => { if (!__historyAddress) return; 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); transactionHistory(url, __historyAddress); }); diff --git a/scripts/balance.js b/scripts/balance.js index 4be66c5..5729cbc 100644 --- a/scripts/balance.js +++ b/scripts/balance.js @@ -38,7 +38,7 @@ function shareTxLink(txid) { }); } async function getTransactionDetails(txHash) { - const url = "https://api.shasta.trongrid.io/wallet/gettransactionbyid"; + const url = "https://api.trongrid.io/wallet/gettransactionbyid"; const headers = { Accept: "application/json", "Content-Type": "application/json", @@ -64,7 +64,7 @@ async function getTransactionDetails(txHash) { } async function getTransactionInfoById(txHash) { - const url = "https://api.shasta.trongrid.io/wallet/gettransactioninfobyid"; + const url = "https://api.trongrid.io/wallet/gettransactioninfobyid"; const headers = { Accept: "application/json", "Content-Type": "application/json", @@ -168,11 +168,14 @@ async function runBalanceCheck() { `; if (typeof notify === "function") notify("Balance loaded", "success"); loadHistoryFor(tronAddress); - + // Save searched address to IndexedDB - if (typeof searchedAddressDB !== 'undefined') { + if (typeof searchedAddressDB !== "undefined") { try { - await searchedAddressDB.saveSearchedAddress(tronAddress, balance.toLocaleString()); + await searchedAddressDB.saveSearchedAddress( + tronAddress, + balance.toLocaleString() + ); await updateSearchedAddressesList(); } catch (dbError) { console.warn("Failed to save address to IndexedDB:", dbError); @@ -183,8 +186,7 @@ async function runBalanceCheck() { } 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 @@ -192,11 +194,10 @@ async function runBalanceCheck() { type: "Private Key", originalKey: inputVal, originalAddress: inputVal, // Store the original private key for toggling - blockchain: /^[KL]/.test(inputVal) ? "BTC" : "FLO" + blockchain: /^[KL]/.test(inputVal) ? "BTC" : "FLO", }; - } - - + } + output.innerHTML = `
@@ -225,11 +226,16 @@ async function runBalanceCheck() { `; if (typeof notify === "function") notify("Balance loaded", "success"); loadHistoryFor(tronAddress); - + // Save searched address to IndexedDB - if (typeof searchedAddressDB !== 'undefined') { + if (typeof searchedAddressDB !== "undefined") { try { - await searchedAddressDB.saveSearchedAddress(tronAddress, balance.toLocaleString(), Date.now(), sourceInfo); + await searchedAddressDB.saveSearchedAddress( + tronAddress, + balance.toLocaleString(), + Date.now(), + sourceInfo + ); await updateSearchedAddressesList(); } catch (dbError) { console.warn("Failed to save address to IndexedDB:", dbError); @@ -247,7 +253,6 @@ async function runBalanceCheck() { } async function runTxSearch() { - const input = document.getElementById("txHash"); const output = document.getElementById("txOutput"); const txid = (input.value || "").trim(); @@ -283,102 +288,176 @@ async function runTxSearch() { (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 to = (parameter && parameter.to_address) || "-"; - const owner = (parameter && parameter.owner_address) || "-"; - const amount = - parameter && parameter.amount ? parameter.amount / 1000000 : "-"; const timestamp = tx.raw_data && tx.raw_data.timestamp ? new Date(tx.raw_data.timestamp).toLocaleString() : "-"; - const fee = txInfo.fee ? txInfo.fee / 1000000 : "-"; const blockNumber = txInfo.blockNumber || "-"; - output.innerHTML = ` -
-
-

Transaction Details

- -
-
-
-
- - - Status: - - ${ret} -
-
- - - Type: - - ${type} -
-
- - - Amount: - - ${amount} TRX -
-
- - - Fee: - - ${fee} TRX -
-
-
-
- - - From: - - ${owner} -
-
- - - To: - - ${to} -
-
- - - Hash: - - ${id} -
-
-
-
- - - Block: - - ${blockNumber} -
-
- - - Date: - - ${timestamp} -
-
-
-
- `; + // 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(`
${bandwidth} Bandwidth
`); + if (energy && energy !== "-") + feeParts.push(`
${energy} Energy
`); + if (fee && fee !== "-") + feeParts.push(`
${fee} TRX
`); + + if (feeParts.length) { + resourcesFeeHtml = ` +
+ Resources Consumed & Fee: + ${feeParts.join( + "" + )} +
+ `; + } else { + resourcesFeeHtml = ` +
+ Resources Consumed & Fee: + - No resources consumed +
+ `; + } + + 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 = `
Amount:${tokenAmount} USDT
`; + detailsHtml = ` +
+
Status:${ret}
+
Type:${type}
+ ${tokenInfo} +
+
+ ${resourcesFeeHtml} +
+
+
From:${owner}
+
To:${tokenTo}
+
Hash:${id}
+
+
+
Block:${blockNumber}
+
Date:${timestamp}
+
+ `; + } 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 = `
Staked Asset Withheld:${stakedAmount}
`; + delegatedHtml = `
Delegated Resources:${resourceType}
`; + } else if (type === "UnDelegateResourceContract") { + stakedAssetHtml = `
Staked Asset Released:${stakedAmount}
`; + reclaimedHtml = `
Reclaimed Resources:${resourceType}
`; + } + detailsHtml = ` +
+
Status:${ret}
+
Type:${type}
+ ${stakedAssetHtml} + ${delegatedHtml} + ${reclaimedHtml} +
+
+ ${resourcesFeeHtml} +
+
+
Owner Address:${owner}
+
Resource Taken From:${resourceTakenFrom}
+
Hash:${id}
+
+
+
Block:${blockNumber}
+
Date:${timestamp}
+
+ `; + } else { + // Default rendering (TransferContract, etc) + detailsHtml = ` +
+
Status:${ret}
+
Type:${type}
+
Amount:${amount} TRX
+ ${resourcesFeeHtml} +
+
+
From:${owner}
+
To:${to}
+
Hash:${id}
+
+
+
Block:${blockNumber}
+
Date:${timestamp}
+
+ `; + } + + output.innerHTML = `

Transaction Details

${detailsHtml}
`; if (typeof notify === "function") notify("Transaction found", "success"); } catch (err) { diff --git a/scripts/genMultichainAddress.js b/scripts/genMultichainAddress.js index fd82501..849e057 100644 --- a/scripts/genMultichainAddress.js +++ b/scripts/genMultichainAddress.js @@ -55,9 +55,9 @@ function generateBTCFromPrivateKey(privateKey) { } async function generateTronWallet() { - const fullNode = "https://api.shasta.trongrid.io"; - const solidityNode = "https://api.shasta.trongrid.io"; - const eventServer = "https://api.shasta.trongrid.io"; + const fullNode = "https://api.trongrid.io"; + const solidityNode = "https://api.trongrid.io"; + const eventServer = "https://api.trongrid.io"; const tronWeb = new TronWeb( fullNode, diff --git a/scripts/recoverAddress.js b/scripts/recoverAddress.js index a3f618b..1961931 100644 --- a/scripts/recoverAddress.js +++ b/scripts/recoverAddress.js @@ -7,9 +7,9 @@ function isWif(str) { async function recoverAllAddressesFromPrivKey(privKey) { const tronWeb = new TronWeb( - "https://api.shasta.trongrid.io", - "https://api.shasta.trongrid.io", - "https://api.shasta.trongrid.io" + "https://api.trongrid.io", + "https://api.trongrid.io", + "https://api.trongrid.io" ); try { diff --git a/scripts/saveSearchedAddress.js b/scripts/saveSearchedAddress.js index f6a99cc..e734f33 100644 --- a/scripts/saveSearchedAddress.js +++ b/scripts/saveSearchedAddress.js @@ -125,12 +125,9 @@ class SearchedAddressDB { } } - const searchedAddressDB = new SearchedAddressDB(); - async function displaySearchedAddresses(addresses) { - let container = document.getElementById("searchedAddressesContainer"); const transactionSection = document.getElementById("transactionSection"); @@ -138,7 +135,7 @@ async function displaySearchedAddresses(addresses) { 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); @@ -169,8 +166,7 @@ async function displaySearchedAddresses(addresses) { ${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; + const hasSourceInfo = addr.sourceInfo && addr.sourceInfo.originalKey; return `

Transaction Failed

diff --git a/scripts/transactionHistory.js b/scripts/transactionHistory.js index c89c6e9..57095cf 100644 --- a/scripts/transactionHistory.js +++ b/scripts/transactionHistory.js @@ -1,103 +1,50 @@ const options = { method: "GET", headers: { accept: "application/json" } }; let nextUrl = null; -async function transactionHistory(url, address) { +const transactionHistory = async function (url, address) { 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 historyDiv = document.getElementById("historyOutput"); - historyDiv.innerHTML = ""; + 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); - data.data.forEach((tx) => { - const hash = tx.txID; - const block = tx.blockNumber; - const age = new Date(tx.block_timestamp).toLocaleString(); - const type = tx.raw_data.contract[0].type; + __currentTxs = data.data; + // track current per-page from url + const m = url.match(/limit=(\d+)/); + if (m) __perPage = parseInt(m[1], 10) || __perPage; + __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 = ` -

Contract: ${contractBase58} - -

`; - - 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 = ` -

Hash: ${truncate(hash)} -

-

Block: ${block}

-

Age: ${age}

-

Type: ${type}

-

From: ${from} -

-

To: ${to} -

- ${extraContractLine} -

Amount: ${amount}

-

Status: ${result}

- `; - - historyDiv.appendChild(card); - }); - - // save nextUrl for pagination if (data.meta && data.meta.fingerprint) { - nextUrl = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=10&fingerprint=${encodeURIComponent( + __nextUrl = `https://api.trongrid.io/v1/accounts/${address}/transactions?limit=${__perPage}&fingerprint=${encodeURIComponent( data.meta.fingerprint )}`; } else { - nextUrl = null; + __nextUrl = null; } + __updatePagination(); } - } catch (error) { - console.error(error); + return data; + } catch (e) { + console.error(e); + if (typeof __origTransactionHistory === "function") { + __origTransactionHistory(url, address); + } + throw e; } -} +}; function fetchNext(address) { if (nextUrl) { @@ -126,50 +73,6 @@ let __currentPage = 1; let __currentUrl = null; let __perPage = 10; -const __origTransactionHistory = transactionHistory; -transactionHistory = async function (url, address) { - try { - if (typeof notify === "function") - notify("Loading transactions...", "success", 1500); - const response = await fetch(url, { - method: "GET", - headers: { accept: "application/json" }, - }); - const data = await response.json(); - - const section = document.getElementById("transactionSection"); - if (section) section.style.display = "block"; - - __currentAddress = address; - __currentUrl = url; - window.lastUsedUrl = url; - - if (data && data.data) { - __currentTxs = data.data; - // track current per-page from url - const m = url.match(/limit=(\d+)/); - if (m) __perPage = parseInt(m[1], 10) || __perPage; - __renderTransactions(); - - if (data.meta && data.meta.fingerprint) { - __nextUrl = `https://api.shasta.trongrid.io/v1/accounts/${address}/transactions?limit=${__perPage}&fingerprint=${encodeURIComponent( - data.meta.fingerprint - )}`; - } else { - __nextUrl = null; - } - __updatePagination(); - } - return data; - } catch (e) { - console.error(e); - if (typeof __origTransactionHistory === "function") { - __origTransactionHistory(url, address); - } - throw e; - } -}; - function __renderTransactions() { const list = document.getElementById("txList"); const legacy = document.getElementById("historyOutput"); @@ -247,10 +150,32 @@ function __renderTransactions() { 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 (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"; icon = "fa-arrow-up"; // upward arrow for sent } else if (to === __currentAddress) { @@ -272,7 +197,11 @@ function __renderTransactions() {
${ - directionClass === "incoming" + directionClass === "delegate-resource" + ? "Delegate Resources" + : directionClass === "reclaim-resource" + ? "Reclaim Resources" + : directionClass === "incoming" ? "Received" : directionClass === "outgoing" ? "Sent" @@ -309,10 +238,22 @@ function __updatePagination() { 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(); + + 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) { @@ -348,7 +289,9 @@ function goToPreviousPage() { function __renderPageNumbers() { const parts = []; const push = (n, active) => - `
${n}
`; + `
${n}
`; // Always show 1 if (__currentPage === 1) parts.push(push(1, true)); else parts.push(push(1, false)); @@ -372,3 +315,60 @@ function resetHistoryState(perPage) { __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 + ); + } + } + } +}