`;
resultsHtml += `
`;
resultsHtml += ``;
resultsOutputEl.innerHTML = resultsHtml;
const pdfBtnSection = document.getElementById('defiPdfDownloadSectionResults');
if(pdfBtnSection) pdfBtnSection.style.display = 'block';
},
getCompoundingPeriods(freqString) { // Copied from thought block
switch(freqString) {
case 'daily': return 365;
case 'weekly': return 52;
case 'monthly': return 12;
case 'annually': return 1;
case 'continuous': return Infinity;
default: return 0;
}
},
formatCurrency: function(value, addSymbol = true) { // General helper
if (isNaN(value) || value === null || !isFinite(value)) return addSymbol ? '$ --' : '--';
const symbol = addSymbol ? '$ ' : '';
return symbol + Number(value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
},
formatPercent: function(value, decimals = 2) { // General helper
if (isNaN(value) || value === null || !isFinite(value)) return '-- %';
return Number(value).toFixed(decimals) + ' %';
},
generatePDF: async function() { // Copied and adapted from thought block
if (!window.jspdf || !window.html2canvas || Object.keys(this.userInputData.opportunity).length === 0) {
alert('Please input opportunity details and perform an analysis first.'); return;
}
await new Promise(resolve => setTimeout(resolve, 50));
const pdfExportContainer = document.createElement('div');
pdfExportContainer.classList.add('defirra-pdf-export-content');
pdfExportContainer.style.width = '800px';
let pdfHtml = `
Your Risk Assessment Summary
| Risk Level | Count |
|---|---|
| High Concern / Significant Risk | ${riskSummary.HighConcern} |
| Medium Concern / Needs Monitoring | ${riskSummary.MediumConcern} |
| Low Concern / Well Mitigated | ${riskSummary.LowConcern} |
| Not Assessed / N/A | ${riskSummary.NotAssessed} |
Overall Considerations Based on Your Assessment:
- `;
if(errorInRewards){ resultsHtml += `
- Could not calculate reward projection due to missing/invalid inputs for opportunity details. `; } if (riskSummary.HighConcern > 0) { resultsHtml += `
- You've identified ${riskSummary.HighConcern} area(s) of 'High Concern'. These represent significant risks that you perceive for this opportunity. Thoroughly review these before proceeding. `; } if (riskSummary.MediumConcern > 0) { resultsHtml += `
- Your assessment includes ${riskSummary.MediumConcern} area(s) of 'Medium Concern'. These warrant careful monitoring and further investigation. `; } if (riskSummary.HighConcern === 0 && riskSummary.MediumConcern === 0 && riskSummary.LowConcern > 0) { resultsHtml += `
- Based on your ratings, you perceive mostly 'Low Concern' or well-mitigated risks for the assessed criteria. `; } if (riskSummary.NotAssessed > 0 && totalRiskCriteria > riskSummary.NotAssessed) { resultsHtml += `
- There are ${riskSummary.NotAssessed} criteria you marked as 'Not Assessed / N/A'. Completing these assessments could provide a more comprehensive view. `; } else if (totalRiskCriteria === riskSummary.NotAssessed && totalRiskCriteria > 0){ resultsHtml += `
- All risk criteria were marked as 'Not Assessed / N/A'. A thorough risk assessment is crucial in DeFi. `;} resultsHtml += `
- Always weigh the potential rewards (Projected APY: ${errorInRewards ? 'N/A': this.formatPercent(calculatedApy)}) against your self-assessed risks. Higher returns in DeFi often correlate with higher risks. `; resultsHtml += `
- This analysis is a reflection of YOUR inputs and understanding. Continue to DYOR and consider consulting multiple sources. `; resultsHtml += `
DeFi Risk & Reward Analysis Report
`; pdfHtml += `Disclaimer: This report is based on user-inputted data and subjective risk assessments. It is for educational purposes only and NOT investment advice. DeFi is high-risk. DYOR.
`;
const opp = this.userInputData.opportunity || {};
pdfHtml += `Opportunity Details & Reward Projection
`;
pdfHtml += `
`;
pdfHtml += `Platform: ${opp.platformName || 'N/A'}
`;
pdfHtml += `Protocol/Pool: ${opp.protocolName || 'N/A'}
`;
pdfHtml += `Token(s): ${opp.tokensInvolved || 'N/A'}
`;
pdfHtml += `Investment Amount: ${this.formatCurrency(parseFloat(opp.investmentAmount), false)}
`;
pdfHtml += `Quoted Rate: ${parseFloat(opp.quotedRate).toFixed(2)} (${opp.rateType || 'N/A'})
`;
if (opp.rateType === 'APR') { pdfHtml += `Compounding: ${opp.compoundingFrequencyOpp || 'N/A'}
`; }
pdfHtml += `Term: ${opp.investmentTermOpp} ${opp.termUnitOpp}
`;
pdfHtml += `Est. Setup Fees: ${this.formatCurrency(parseFloat(opp.setupFees) || 0, false)}
`;
const reward = this.userInputData.calculatedReward || {};
pdfHtml += `Projected Rewards (Illustrative):
`; pdfHtml += `Calculated APY: ${this.formatPercent(reward.calculatedApy).replace(' %','')}
`;
pdfHtml += `Future Value: ${this.formatCurrency(reward.futureValue, false)}
`;
pdfHtml += `Gross Interest: ${this.formatCurrency(reward.totalInterest, false)}
`;
pdfHtml += `Net Return (after setup fees): ${this.formatCurrency(reward.netReturn, false)}
`;
pdfHtml += `Your Risk Assessment Details
`;
pdfHtml += `
`;
const riskSummary = this.userInputData.riskSummary || { HighConcern: 0, MediumConcern: 0, LowConcern: 0, NotAssessed: 0 };
pdfHtml += `
`;
const qualitativeFeedbackEl = document.querySelector('#defiResultsOutput .defirra-qualitative-feedback');
if(qualitativeFeedbackEl) {
pdfHtml += `| Category | Criterion | Your Rating | Your Notes |
|---|---|---|---|
| ${category.title} | ` : ''}${item.text} | ${ratingText} | ${(riskData.notes || '--').replace(/\n/g, ' ')} |
Risk Summary Counts:
`; pdfHtml += `High Concern: ${riskSummary.HighConcern} | Medium Concern: ${riskSummary.MediumConcern} | Low Concern: ${riskSummary.LowConcern} | Not Assessed: ${riskSummary.NotAssessed}
`; pdfHtml += `Overall Considerations (Based on Your Assessment)
${qualitativeFeedbackEl.innerHTML.replace(/
`;
}
pdfExportContainer.innerHTML = pdfHtml;
document.body.appendChild(pdfExportContainer);
try {
const canvas = await html2canvas(pdfExportContainer, { scale: 1.2, useCORS: true, logging: false, windowWidth: pdfExportContainer.scrollWidth, windowHeight: pdfExportContainer.scrollHeight });
document.body.removeChild(pdfExportContainer);
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = pdf.internal.pageSize.getHeight();
const imgProps = pdf.getImageProperties(imgData);
let imgFinalWidth = pdfWidth - 40;
let imgFinalHeight = (imgProps.height * imgFinalWidth) / imgProps.width;
if (imgFinalHeight > pdfHeight - 40) { // Check if it exceeds one page height
// Simple approach for now: scale to fit one page width, height will be proportional
// For true multi-page from a single large canvas, more complex slicing of imgData is needed
// Or, ensure content is short enough, or accept scrolling in image if PDF viewer supports.
// Forcing it onto one page by potentially reducing scale further if too tall:
if (imgFinalHeight > pdfHeight - 40) {
imgFinalHeight = pdfHeight - 40; // Max height
imgFinalWidth = (imgProps.width * imgFinalHeight) / imgProps.height; // Adjust width to maintain aspect ratio
}
}
let position = 20;
// This simple addImage might not handle multi-page perfectly for very long content.
// For a robust multi-page for very long content, one would typically segment the HTML
// or use more advanced jsPDF features with `addHTML` (which has its own complexities)
// or split the canvas. For now, it will attempt to fit on one or more pages by scaling.
// The loop for multiple pages from previous thought block is good if image can be sliced.
// Here's a simplified version that just adds the (potentially long) image.
let currentY = position;
const pageMargin = 20;
const pageHeightAvailable = pdfHeight - 2 * pageMargin;
let heightProcessed = 0;
while(heightProcessed < imgHeight) {
let pageSegmentHeight = Math.min(imgHeight - heightProcessed, pageHeightAvailable);
// Create a temporary canvas for the segment
const segmentCanvas = document.createElement('canvas');
segmentCanvas.width = canvas.width; // Use original canvas width for source slicing
segmentCanvas.height = (pageSegmentHeight / imgHeight) * canvas.height; // Proportional height from original canvas
const sctx = segmentCanvas.getContext('2d');
// Source y, source height
sctx.drawImage(canvas, 0, (heightProcessed / imgHeight) * canvas.height, canvas.width, segmentCanvas.height, 0, 0, segmentCanvas.width, segmentCanvas.height);
const segmentImgData = segmentCanvas.toDataURL('image/png');
const segmentImgProps = pdf.getImageProperties(segmentImgData);
let segmentFinalWidth = pdfWidth - 2 * pageMargin;
let segmentFinalHeight = (segmentImgProps.height * segmentFinalWidth) / segmentImgProps.width;
// Ensure segment fits on current page (it should, as pageSegmentHeight was capped)
if(segmentFinalHeight > pageHeightAvailable) { // Should not happen if logic above is correct
segmentFinalHeight = pageHeightAvailable;
segmentFinalWidth = (segmentImgProps.width * segmentFinalHeight) / segmentImgProps.height;
}
if(heightProcessed > 0) pdf.addPage();
pdf.addImage(segmentImgData, 'PNG', pageMargin, pageMargin, segmentFinalWidth, segmentFinalHeight);
heightProcessed += pageSegmentHeight * (imgHeight / canvas.height) * (canvas.height / segmentCanvas.height); // track original image height processed
}
pdf.save('DeFi_Risk_Reward_Analysis.pdf');
} catch (error) {
console.error("Error generating PDF:", error);
alert("An error occurred while generating PDF.");
if (document.body.contains(pdfExportContainer)) document.body.removeChild(pdfExportContainer);
}
}
};
document.addEventListener('DOMContentLoaded', function() {
defiRRAApp.init();
});
