Blended APY
${overallAPY.toFixed(2)}%
Detailed Breakdown
| Farming Pool | Daily Earnings | Total Profit | Final Value |
|---|---|---|---|
| ${detail.poolName} (${detail.poolApy}%) | $${detail.dailyEarnings.toFixed(2)} | $${detail.totalProfit.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} | $${detail.finalValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})} |
$
Select Farming Pools
${farmingPools.map(pool => `
`).join('')}
APY: ${pool.apy}% - Risk: ${pool.risk}
Edit Pool
${poolForms}
`;
};
// --- CHART & PDF ---
const renderChart = () => {
if (myChart) {
myChart.destroy();
}
const ctx = document.getElementById('yieldChart');
if (!ctx || !results) return;
const labels = results.details.map(d => d.poolName);
const profitData = results.details.map(d => d.totalProfit);
myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Total Profit ($)',
data: profitData,
backgroundColor: 'rgba(79, 70, 229, 0.8)',
borderColor: 'rgba(79, 70, 229, 1)',
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return '$' + value.toLocaleString();
}
}
}
},
plugins: {
legend: {
display: false
},
title: {
display: true,
text: `Projected Profit over ${results.periodDays} Days`,
font: { size: 16 }
}
}
}
});
};
window.downloadPDF = () => {
if (!results) {
console.error("No results to download.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const today = new Date().toLocaleDateString('en-US');
// Title
doc.setFontSize(20);
doc.text("DeFi Yield Farming Report", doc.internal.pageSize.getWidth() / 2, 20, { align: 'center' });
doc.setFontSize(12);
doc.text(`Report Generated: ${today}`, doc.internal.pageSize.getWidth() / 2, 28, { align: 'center' });
// Summary Text
const summaryText = `
Initial Investment: $${results.totalInitialInvestment.toLocaleString()}
Investment Period: ${results.periodDays} days
Total Ending Balance: $${results.totalEndingBalance.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
Total Profit: $${(results.totalEndingBalance - results.totalInitialInvestment).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})}
`;
doc.setFontSize(11);
doc.text(summaryText, 14, 40);
// Add Chart Image
let finalY = 70;
if (myChart) {
const chartImage = myChart.toBase64Image();
const imgProps = doc.getImageProperties(chartImage);
const pdfWidth = doc.internal.pageSize.getWidth() - 28;
const pdfHeight = (imgProps.height * pdfWidth) / imgProps.width;
doc.addImage(chartImage, 'PNG', 14, finalY, pdfWidth, pdfHeight);
finalY += pdfHeight + 10;
}
// Detailed Table
const head = [['Farming Pool', 'APY (%)', 'Daily Earnings ($)', 'Total Profit ($)', 'Final Value ($)']];
const body = results.details.map(d => [d.poolName, d.poolApy, d.dailyEarnings.toFixed(2), d.totalProfit.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}), d.finalValue.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2})]);
doc.autoTable({
startY: finalY,
head: head,
body: body,
theme: 'striped',
headStyles: { fillColor: [79, 70, 229] }
});
doc.save('DeFi-Yield-Farming-Report.pdf');
};
// --- EVENT HANDLERS & DATA UPDATERS ---
window.updateInvestment = (key, value) => {
investment[key] = parseFloat(value) || 0;
};
window.togglePoolSelection = (poolId) => {
const index = investment.selectedPoolIds.indexOf(poolId);
if (index > -1) {
investment.selectedPoolIds.splice(index, 1);
} else {
investment.selectedPoolIds.push(poolId);
}
};
const handleCalculation = () => {
if (investment.selectedPoolIds.length === 0) {
// We can't use alert, so we'll show a subtle message
const calcButton = document.getElementById('calculate-btn');
if(calcButton) {
calcButton.innerText = 'Please select at least one pool!';
setTimeout(() => { calcButton.innerText = 'Calculate Returns'; }, 2000);
}
return;
}
const details = [];
let totalEndingBalance = 0;
const investmentPerPool = investment.amount / investment.selectedPoolIds.length;
investment.selectedPoolIds.forEach(poolId => {
const pool = farmingPools.find(p => p.id === poolId);
if (pool) {
const dailyRate = (pool.apy / 100) / 365;
const dailyEarnings = investmentPerPool * dailyRate;
const totalProfit = dailyEarnings * investment.periodDays;
const finalValue = investmentPerPool + totalProfit;
details.push({
poolName: pool.name,
poolApy: pool.apy,
dailyEarnings,
totalProfit,
finalValue
});
totalEndingBalance += finalValue;
}
});
results = {
totalInitialInvestment: investment.amount,
periodDays: investment.periodDays,
totalEndingBalance,
details
};
switchTab(0); // Go to dashboard
};
window.updatePool = (poolId, key, value) => {
const pool = farmingPools.find(p => p.id === poolId);
if (pool) {
pool[key] = (key === 'apy') ? parseFloat(value) || 0 : value;
}
};
const handleAddPool = () => {
farmingPools.push({
id: Date.now(),
name: 'New Custom Pool',
apy: 10.0,
risk: 'Medium'
});
renderCurrentTab();
};
window.handleDeletePool = (poolId) => {
farmingPools = farmingPools.filter(p => p.id !== poolId);
// Also remove it from selection if it was selected
investment.selectedPoolIds = investment.selectedPoolIds.filter(id => id !== poolId);
renderCurrentTab();
};
// --- INITIALIZATION ---
nextBtn.addEventListener('click', () => switchTab(currentTab + 1));
prevBtn.addEventListener('click', () => switchTab(currentTab - 1));
tabButtons.forEach((button, index) => {
button.addEventListener('click', () => switchTab(index));
});
renderCurrentTab();
updateNavButtons();
});
