Workflow updating files of floethereum

This commit is contained in:
RanchiMall Dev 2026-01-31 12:37:06 +00:00
parent 87220ae53e
commit 3a2f5b7ec9
2 changed files with 154 additions and 93 deletions

View File

@ -822,56 +822,71 @@
loadTransactionsPage(ethAddress, floAddress, currentPage); loadTransactionsPage(ethAddress, floAddress, currentPage);
} }
let allTransactionsCache = [];
let currentBalances = { eth: '0', usdc: '0', usdt: '0' };
async function loadTransactionsPage(ethAddress, floAddress, page) { async function loadTransactionsPage(ethAddress, floAddress, page) {
buttonLoader('check_balance_button', true); buttonLoader('check_balance_button', true);
try { try {
const results = await Promise.allSettled([ let transactions = [];
ethOperator.getBalance(ethAddress), let etherBalance = '0', usdcBalance = '0', usdtBalance = '0';
ethOperator.getTokenBalance(ethAddress, 'usdc'),
ethOperator.getTokenBalance(ethAddress, 'usdt'), // Fetch new data if it's the first page (Search or Refresh)
ethOperator.getTransactionHistory(ethAddress, { if (page === 1) {
page: page, const results = await Promise.allSettled([
offset: TRANSACTIONS_PER_PAGE, ethOperator.getBalance(ethAddress),
sort: 'desc' ethOperator.getTokenBalance(ethAddress, 'usdc'),
ethOperator.getTokenBalance(ethAddress, 'usdt'),
ethOperator.getTransactionHistory(ethAddress, {
page: 1, // Always Page 1 of API
offset: 500, // Batch size 500
sort: 'desc'
})
]);
etherBalance = results[0].status === 'fulfilled' ? results[0].value : '0';
usdcBalance = results[1].status === 'fulfilled' ? results[1].value : '0';
usdtBalance = results[2].status === 'fulfilled' ? results[2].value : '0';
transactions = results[3].status === 'fulfilled' ? results[3].value : [];
// Update Cache
allTransactionsCache = transactions;
currentBalances = { eth: etherBalance, usdc: usdcBalance, usdt: usdtBalance };
// Log warnings
if (results[0].status === 'rejected') console.warn('Failed to fetch ETH balance:', results[0].reason);
if (results[1].status === 'rejected') console.warn('Failed to fetch USDC balance:', results[1].reason);
if (results[2].status === 'rejected') console.warn('Failed to fetch USDT balance:', results[2].reason);
if (results[3].status === 'rejected') console.warn('Failed to fetch transaction history:', results[3].reason);
} else {
// Use Cache for pagination
transactions = allTransactionsCache;
({ eth: etherBalance, usdc: usdcBalance, usdt: usdtBalance } = currentBalances);
}
// Local Pagination / Slicing
const startIndex = (page - 1) * TRANSACTIONS_PER_PAGE;
const endIndex = startIndex + TRANSACTIONS_PER_PAGE;
const paginatedTransactions = transactions.slice(startIndex, endIndex);
const hasNextPage = transactions.length > endIndex;
// Sync Contacts (only on fresh load really, but safe here)
if (page === 1) {
compactIDB.readData('contacts', floAddress || ethAddress).then(result => {
if (result) return
compactIDB.addData('contacts', {
ethAddress,
}, floAddress || ethAddress).then(() => {
renderSearchedAddressList()
}).catch((error) => {
console.error(error)
})
}) })
]);
// Extract balance and transaction data, using defaults if any request failed
const etherBalance = results[0].status === 'fulfilled' ? results[0].value : '0';
const usdcBalance = results[1].status === 'fulfilled' ? results[1].value : '0';
const usdtBalance = results[2].status === 'fulfilled' ? results[2].value : '0';
const transactions = results[3].status === 'fulfilled' ? results[3].value : [];
// Store transactions for filtering
allTransactions = transactions;
// Log warnings if any API requests failed
if (results[0].status === 'rejected') {
console.warn('Failed to fetch ETH balance:', results[0].reason);
}
if (results[1].status === 'rejected') {
console.warn('Failed to fetch USDC balance:', results[1].reason);
}
if (results[2].status === 'rejected') {
console.warn('Failed to fetch USDT balance:', results[2].reason);
}
if (results[3].status === 'rejected') {
console.warn('Failed to fetch transaction history:', results[3].reason);
} }
compactIDB.readData('contacts', floAddress || ethAddress).then(result => { renderBalanceAndTransactions(ethAddress, floAddress, etherBalance, usdcBalance, usdtBalance, paginatedTransactions, page, hasNextPage);
if (result) return
compactIDB.addData('contacts', {
ethAddress,
}, floAddress || ethAddress).then(() => {
renderSearchedAddressList()
}).catch((error) => {
console.error(error)
})
})
renderBalanceAndTransactions(ethAddress, floAddress, etherBalance, usdcBalance, usdtBalance, transactions, page);
} catch (error) { } catch (error) {
notify(error.message || error, 'error'); notify(error.message || error, 'error');
@ -880,7 +895,7 @@
} }
} }
function renderBalanceAndTransactions(ethAddress, floAddress, etherBalance, usdcBalance, usdtBalance, transactions, page) { function renderBalanceAndTransactions(ethAddress, floAddress, etherBalance, usdcBalance, usdtBalance, transactions, page, hasNextPage) {
// Update URL to reflect the current address being viewed // Update URL to reflect the current address being viewed
const url = new URL(window.location); const url = new URL(window.location);
url.searchParams.set('address', ethAddress); url.searchParams.set('address', ethAddress);
@ -890,7 +905,7 @@
window.history.pushState({}, '', url.pathname + url.search + url.hash); window.history.pushState({}, '', url.pathname + url.search + url.hash);
// Determine if pagination buttons should be enabled // Determine if pagination buttons should be enabled
const hasNextPage = transactions.length >= TRANSACTIONS_PER_PAGE; // hasNextPage is now passed in
const hasPrevPage = page > 1; const hasPrevPage = page > 1;
renderElem(getRef('eth_balance_wrapper'), html` renderElem(getRef('eth_balance_wrapper'), html`
@ -1124,14 +1139,32 @@
</div> </div>
<div class="grid gap-0-5"> <div class="grid gap-0-5">
<div class="label">To</div> <div class="label">To (Contract)</div>
<sm-copy value=${txDetails.to}></sm-copy> <sm-copy value=${txDetails.to}></sm-copy>
</div> </div>
${txDetails.tokenTransfer ? html` ${txDetails.tokenTransfers && txDetails.tokenTransfers.length > 0 ? html`
<div class="grid gap-0-5"> <div class="grid gap-0-5">
<div class="label">Token Transfer</div> <div class="label">Token Transfers</div>
<strong>${txDetails.tokenTransfer.value} ${txDetails.tokenTransfer.symbol}</strong> <div class="grid gap-0-5" style="padding: 0.75rem; background: rgba(var(--text-color), 0.05); border-radius: 0.5rem;">
${txDetails.tokenTransfers.map((transfer, index) => html`
<div class="grid gap-0-3" style="${index !== txDetails.tokenTransfers.length - 1 ? 'border-bottom: 1px solid rgba(var(--text-color), 0.1); padding-bottom: 0.75rem; margin-bottom: 0.25rem;' : ''}">
<div class="flex space-between align-center">
<strong>${transfer.value} ${transfer.symbol}</strong>
</div>
<div class="grid gap-0-5" style="font-size: 0.8rem;">
<div class="grid gap-0">
<span class="color-0-7">From</span>
<sm-copy value=${transfer.from} style="--font-size: 0.8rem;"></sm-copy>
</div>
<div class="grid gap-0">
<span class="color-0-7">To</span>
<sm-copy value=${transfer.to} style="--font-size: 0.8rem;"></sm-copy>
</div>
</div>
</div>
`)}
</div>
</div> </div>
` : html` ` : html`
<div class="grid gap-0-5"> <div class="grid gap-0-5">

View File

@ -417,7 +417,17 @@
const tokenTransfers = tokenTxData.status === '1' ? tokenTxData.result : []; const tokenTransfers = tokenTxData.status === '1' ? tokenTxData.result : [];
// Combine and sort transactions // Combine and sort transactions
const allTransactions = [...normalTxData.result, ...tokenTransfers]; // Filter out normal transactions that are already present in token transfers (duplicate hash) AND have 0 value
// This prevents showing "0 ETH to Contract" alongside the actual "Token Transfer"
const tokenTxHashes = new Set(tokenTransfers.map(tx => tx.hash));
const uniqueNormalTxs = normalTxData.result.filter(tx => {
if (tokenTxHashes.has(tx.hash) && tx.value === '0') {
return false;
}
return true;
});
const allTransactions = [...uniqueNormalTxs, ...tokenTransfers];
// Sort by timestamp (descending) // Sort by timestamp (descending)
allTransactions.sort((a, b) => parseInt(b.timeStamp) - parseInt(a.timeStamp)); allTransactions.sort((a, b) => parseInt(b.timeStamp) - parseInt(a.timeStamp));
@ -504,33 +514,51 @@
parseFloat(ethers.utils.formatEther(gasUsed.mul(effectiveGasPrice))) : null; parseFloat(ethers.utils.formatEther(gasUsed.mul(effectiveGasPrice))) : null;
// Check if it's a token transfer by examining logs // Check if it's a token transfer by examining logs
let tokenTransfer = null; let tokenTransfers = [];
// Simple in-memory cache for token metadata to avoid rate limiting
const TOKEN_METADATA_CACHE = ethOperator.TOKEN_METADATA_CACHE || {};
ethOperator.TOKEN_METADATA_CACHE = TOKEN_METADATA_CACHE;
if (receipt && receipt.logs.length > 0) { if (receipt && receipt.logs.length > 0) {
// Try to decode ERC20 Transfer event // Try to decode ERC20 Transfer event
const transferEventSignature = ethers.utils.id('Transfer(address,address,uint256)'); const transferEventSignature = ethers.utils.id('Transfer(address,address,uint256)');
const transferLog = receipt.logs.find(log => log.topics[0] === transferEventSignature); const transferLogs = receipt.logs.filter(log => log.topics[0] === transferEventSignature);
if (transferLog) { if (transferLogs.length > 0) {
try { try {
const tokenContract = new ethers.Contract(transferLog.address, ERC20ABI, provider); // Process all transfer logs
const [symbol, decimals] = await Promise.all([ tokenTransfers = await Promise.all(transferLogs.map(async (transferLog) => {
tokenContract.symbol().catch(() => 'TOKEN'), const contractAddress = transferLog.address;
tokenContract.decimals().catch(() => 18) let symbol, decimals;
]);
const from = ethers.utils.getAddress('0x' + transferLog.topics[1].slice(26)); // Check cache first
const to = ethers.utils.getAddress('0x' + transferLog.topics[2].slice(26)); if (TOKEN_METADATA_CACHE[contractAddress]) {
const value = parseFloat(ethers.utils.formatUnits(transferLog.data, decimals)); ({ symbol, decimals } = TOKEN_METADATA_CACHE[contractAddress]);
} else {
// Fetch from network if not cached
const tokenContract = new ethers.Contract(contractAddress, ERC20ABI, provider);
[symbol, decimals] = await Promise.all([
tokenContract.symbol().catch(() => 'TOKEN'),
tokenContract.decimals().catch(() => 18)
]);
// Store in cache
TOKEN_METADATA_CACHE[contractAddress] = { symbol, decimals };
}
tokenTransfer = { const from = ethers.utils.getAddress('0x' + transferLog.topics[1].slice(26));
from, const to = ethers.utils.getAddress('0x' + transferLog.topics[2].slice(26));
to, const value = parseFloat(ethers.utils.formatUnits(transferLog.data, decimals));
value,
symbol, return {
contractAddress: transferLog.address from,
}; to,
value,
symbol,
contractAddress
};
}));
} catch (e) { } catch (e) {
console.warn('Could not decode token transfer:', e); console.warn('Could not decode token transfers:', e);
} }
} }
} }
@ -552,7 +580,7 @@
input: tx.data, input: tx.data,
status: receipt ? (receipt.status === 1 ? 'success' : 'failed') : 'pending', status: receipt ? (receipt.status === 1 ? 'success' : 'failed') : 'pending',
isError: receipt ? receipt.status !== 1 : false, isError: receipt ? receipt.status !== 1 : false,
tokenTransfer: tokenTransfer, tokenTransfers: tokenTransfers,
logs: receipt ? receipt.logs : [], logs: receipt ? receipt.logs : [],
type: tx.type type: tx.type
}; };