Skip to content

feature: add new price equilibration analysis dashboard#26

Open
web3skeptic wants to merge 1 commit intomainfrom
feature/new-price-dashboard
Open

feature: add new price equilibration analysis dashboard#26
web3skeptic wants to merge 1 commit intomainfrom
feature/new-price-dashboard

Conversation

@web3skeptic
Copy link

@web3skeptic web3skeptic commented Jan 26, 2026

Preview available at https://web3skeptic.github.io/CirclesTools/priceInsights.html (might load for few seconds to get observations)

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Price Insights Dashboard featuring price snapshots with detailed statistics and token counts.
    • Added interactive price charts with zoom and pan controls for detailed price analysis.
    • Implemented liquidity heatmap visualisation with customisable time range, metrics, and colour coding.
    • Included token details with avatar and ownership information.
  • Updates

    • Navigation link now accesses a local price insights page.

✏️ Tip: You can customize this high-level summary in your review settings.

@web3skeptic web3skeptic requested a review from sh-rn January 26, 2026 10:37
@coderabbitai
Copy link

coderabbitai bot commented Jan 26, 2026

Walkthrough

The pull request adds a new Price Insights Dashboard accessible via a local page link in index.html. The dashboard displays price snapshots with statistics, price charts with zoom controls, token details tables, and liquidity heatmaps with configurable time ranges and metrics.

Changes

Cohort / File(s) Summary
Navigation Update
index.html
Updated Equilibration Analysis tool link href from external URL to local priceInsights.html
New Price Insights Dashboard
priceInsights.html
New client-side dashboard with price snapshots section, bar chart with zoom/pan controls, token details table, liquidity heatmap with Chart.js Matrix visualization, batched token info fetching with client-side caching, and auto-load on page initialisation

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI as Price Dashboard UI
    participant Cache as Token Cache
    participant API as Circles API
    participant Chart as Chart.js

    User->>UI: Load priceInsights.html
    activate UI
    UI->>API: Fetch snapshots
    activate API
    API-->>UI: Snapshot list
    deactivate API
    UI->>UI: Populate snapshot dropdown
    
    UI->>API: Fetch snapshot data
    activate API
    API-->>UI: Token count, statistics
    deactivate API
    
    UI->>API: Fetch liquidity data
    activate API
    API-->>UI: Liquidity heatmap data
    deactivate API
    
    UI->>API: Batch fetch token info
    activate API
    API-->>UI: Token details (avatar, owner, price)
    deactivate API
    
    UI->>Cache: Store token info
    activate Cache
    Cache-->>UI: Cache updated
    deactivate Cache
    
    UI->>Chart: Render price bar chart
    Chart-->>UI: Chart rendered
    
    UI->>UI: Populate token table
    UI->>UI: Render heatmap
    UI-->>User: Dashboard ready
    deactivate UI

    Note over User,Chart: User selects different snapshot or<br/>adjusts heatmap filters
    User->>UI: Change snapshot/filters
    activate UI
    UI->>Cache: Retrieve cached token info
    Cache-->>UI: Cached data
    UI->>UI: Regenerate charts and tables
    UI-->>User: Updated view
    deactivate UI
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarises the main change: addition of a new price equilibration analysis dashboard. It is concise, specific, and clearly conveys the primary purpose of the pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@priceInsights.html`:
- Around line 875-885: The updateLiquidityStats function currently divides by
data.pairs.length and yields NaN when the array is empty; add an early guard at
the start of updateLiquidityStats that checks if !data.pairs ||
data.pairs.length === 0 and sets sensible defaults (e.g., totalPairs = 0,
avgLiquidity = "0" or "N/A", successRate = "0%" or "N/A", highLiquidityPairs =
0) by updating the DOM elements with IDs totalPairs, avgLiquidity, successRate,
and highLiquidityPairs, then return early; otherwise proceed with the existing
reduce/filter calculations (ensure subsequent calculations use the non-zero
length).
- Around line 889-938: The populateLiquidityTable function currently builds rows
via innerHTML using API-provided fields (e.g., pair.source_avatar,
pair.target_avatar, pair.avg_liquidity) which is an XSS risk; replace this by
either escaping each dynamic value with a helper (e.g., add an escapeHtml(value)
util that sets div.textContent and returns div.innerHTML) or by building table
rows with createElement/textContent and appending children instead of string
templates; update populateLiquidityTable to use escapeHtml or DOM construction
for all interpolated fields (including formatLiquidity(pair.avg_liquidity),
formatSuccessRate(pair.success_rate), src/tgt prices and priceRatio) and apply
the same fix to the token table and error banner rendering functions so no API
value is ever injected directly into innerHTML.

Comment on lines +875 to +885
function updateLiquidityStats(data) {
document.getElementById('totalPairs').textContent = data.pairs.length;

const avgLiq = data.pairs.reduce((sum, p) => sum + Number(p.avg_liquidity), 0) / data.pairs.length;
document.getElementById('avgLiquidity').textContent = formatLiquidity(avgLiq);

const avgSuccess = data.pairs.reduce((sum, p) => sum + p.success_rate, 0) / data.pairs.length;
document.getElementById('successRate').textContent = formatSuccessRate(avgSuccess);

const highLiqPairs = data.pairs.filter(p => Number(p.avg_liquidity) >= 10e18).length;
document.getElementById('highLiquidityPairs').textContent = highLiqPairs;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against empty liquidity datasets to avoid NaN stats.

When no pairs match the filters, averages divide by zero and produce NaN, which leaks into the UI. Add an early return with sane defaults.

✅ Suggested fix
         function updateLiquidityStats(data) {
-            document.getElementById('totalPairs').textContent = data.pairs.length;
+            if (!data.pairs || data.pairs.length === 0) {
+                document.getElementById('totalPairs').textContent = '0';
+                document.getElementById('avgLiquidity').textContent = '0';
+                document.getElementById('successRate').textContent = '0%';
+                document.getElementById('highLiquidityPairs').textContent = '0';
+                return;
+            }
+
+            document.getElementById('totalPairs').textContent = data.pairs.length;
 
             const avgLiq = data.pairs.reduce((sum, p) => sum + Number(p.avg_liquidity), 0) / data.pairs.length;
             document.getElementById('avgLiquidity').textContent = formatLiquidity(avgLiq);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function updateLiquidityStats(data) {
document.getElementById('totalPairs').textContent = data.pairs.length;
const avgLiq = data.pairs.reduce((sum, p) => sum + Number(p.avg_liquidity), 0) / data.pairs.length;
document.getElementById('avgLiquidity').textContent = formatLiquidity(avgLiq);
const avgSuccess = data.pairs.reduce((sum, p) => sum + p.success_rate, 0) / data.pairs.length;
document.getElementById('successRate').textContent = formatSuccessRate(avgSuccess);
const highLiqPairs = data.pairs.filter(p => Number(p.avg_liquidity) >= 10e18).length;
document.getElementById('highLiquidityPairs').textContent = highLiqPairs;
function updateLiquidityStats(data) {
if (!data.pairs || data.pairs.length === 0) {
document.getElementById('totalPairs').textContent = '0';
document.getElementById('avgLiquidity').textContent = '0';
document.getElementById('successRate').textContent = '0%';
document.getElementById('highLiquidityPairs').textContent = '0';
return;
}
document.getElementById('totalPairs').textContent = data.pairs.length;
const avgLiq = data.pairs.reduce((sum, p) => sum + Number(p.avg_liquidity), 0) / data.pairs.length;
document.getElementById('avgLiquidity').textContent = formatLiquidity(avgLiq);
const avgSuccess = data.pairs.reduce((sum, p) => sum + p.success_rate, 0) / data.pairs.length;
document.getElementById('successRate').textContent = formatSuccessRate(avgSuccess);
const highLiqPairs = data.pairs.filter(p => Number(p.avg_liquidity) >= 10e18).length;
document.getElementById('highLiquidityPairs').textContent = highLiqPairs;
🤖 Prompt for AI Agents
In `@priceInsights.html` around lines 875 - 885, The updateLiquidityStats function
currently divides by data.pairs.length and yields NaN when the array is empty;
add an early guard at the start of updateLiquidityStats that checks if
!data.pairs || data.pairs.length === 0 and sets sensible defaults (e.g.,
totalPairs = 0, avgLiquidity = "0" or "N/A", successRate = "0%" or "N/A",
highLiquidityPairs = 0) by updating the DOM elements with IDs totalPairs,
avgLiquidity, successRate, and highLiquidityPairs, then return early; otherwise
proceed with the existing reduce/filter calculations (ensure subsequent
calculations use the non-zero length).

Comment on lines +889 to +938
function populateLiquidityTable(data) {
const tbody = document.getElementById('liquidityPairTableBody');

// Sort by average liquidity
const sorted = [...data.pairs].sort((a, b) =>
Number(b.avg_liquidity) - Number(a.avg_liquidity)
);

tbody.innerHTML = sorted.map((pair, index) => {
const avgCrc = Number(pair.avg_liquidity) / 1e18;
const liquidityClass = avgCrc >= 10 ? 'liquidity-high' :
avgCrc >= 1 ? 'liquidity-medium' : 'liquidity-low';

const priceRatio = Number(pair.avg_price_ratio || 0);
const priceRatioClass = priceRatio < 0.5 ? 'liquidity-high' :
priceRatio < 0.9 ? 'liquidity-medium' : 'liquidity-low';

// Format prices
const srcPrice = pair.avg_source_price ? (Number(pair.avg_source_price) / 1e18).toFixed(4) : 'N/A';
const tgtPrice = pair.avg_target_price ? (Number(pair.avg_target_price) / 1e18).toFixed(4) : 'N/A';

// Determine opportunity based on price ratio and liquidity
let opportunity;
if (priceRatio < 0.5 && avgCrc >= 1) {
opportunity = 'Excellent';
} else if (priceRatio < 0.7 && avgCrc >= 0.1) {
opportunity = 'Good';
} else if (priceRatio < 0.9) {
opportunity = 'Moderate';
} else if (avgCrc >= 10) {
opportunity = 'High Liquidity';
} else {
opportunity = 'Limited';
}

return `
<tr>
<td>${index + 1}</td>
<td style="font-size: 0.65em; word-break: break-all;">${pair.source_avatar}</td>
<td style="font-size: 0.65em; word-break: break-all;">${pair.target_avatar}</td>
<td class="${liquidityClass}">${formatLiquidity(pair.avg_liquidity)}</td>
<td>${srcPrice}</td>
<td>${tgtPrice}</td>
<td class="${priceRatioClass}">${priceRatio.toFixed(3)}</td>
<td>${formatSuccessRate(pair.success_rate)}</td>
<td>${pair.observation_count}</td>
<td>${opportunity}</td>
</tr>
`;
}).join('');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid injecting API data via innerHTML (XSS risk).

API-sourced fields (avatars, addresses, pool values) are interpolated into HTML strings. If any field is compromised or malformed, this enables script injection. Prefer DOM construction with textContent, or escape values before interpolation. Please apply the same approach to the token table and error banner rendering too.

🔒 Suggested fix (escape dynamic fields)
-                return `
+                return `
                     <tr>
                         <td>${index + 1}</td>
-                        <td style="font-size: 0.65em; word-break: break-all;">${pair.source_avatar}</td>
-                        <td style="font-size: 0.65em; word-break: break-all;">${pair.target_avatar}</td>
+                        <td style="font-size: 0.65em; word-break: break-all;">${escapeHtml(pair.source_avatar)}</td>
+                        <td style="font-size: 0.65em; word-break: break-all;">${escapeHtml(pair.target_avatar)}</td>
                         <td class="${liquidityClass}">${formatLiquidity(pair.avg_liquidity)}</td>
                         <td>${srcPrice}</td>
                         <td>${tgtPrice}</td>
                         <td class="${priceRatioClass}">${priceRatio.toFixed(3)}</td>
                         <td>${formatSuccessRate(pair.success_rate)}</td>
                         <td>${pair.observation_count}</td>
                         <td>${opportunity}</td>
                     </tr>
                 `;

Add a small helper near the other utility functions:

function escapeHtml(value) {
    const div = document.createElement('div');
    div.textContent = value ?? '';
    return div.innerHTML;
}
🤖 Prompt for AI Agents
In `@priceInsights.html` around lines 889 - 938, The populateLiquidityTable
function currently builds rows via innerHTML using API-provided fields (e.g.,
pair.source_avatar, pair.target_avatar, pair.avg_liquidity) which is an XSS
risk; replace this by either escaping each dynamic value with a helper (e.g.,
add an escapeHtml(value) util that sets div.textContent and returns
div.innerHTML) or by building table rows with createElement/textContent and
appending children instead of string templates; update populateLiquidityTable to
use escapeHtml or DOM construction for all interpolated fields (including
formatLiquidity(pair.avg_liquidity), formatSuccessRate(pair.success_rate),
src/tgt prices and priceRatio) and apply the same fix to the token table and
error banner rendering functions so no API value is ever injected directly into
innerHTML.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments