From 6dd2036c7cdca3d0ae400aec0f83632893e60d71 Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Thu, 18 Dec 2025 14:51:44 -0800 Subject: [PATCH 1/5] trying to fix usagePercent calculation --- docs/snippets/PriceProvider.jsx | 40 +++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/snippets/PriceProvider.jsx b/docs/snippets/PriceProvider.jsx index 863766a77..95b8f6f22 100644 --- a/docs/snippets/PriceProvider.jsx +++ b/docs/snippets/PriceProvider.jsx @@ -265,13 +265,45 @@ export const PriceProvider = ({ children, component: Component }) => { const basePrice = basePricesResult[0]; const maxPrice = maxPricesResult[0]; - // Calculate usage percent: (current - base) / (max - base) * 100 - // Average the prices from all nodes + // Calculate usage percent by finding which usage percentage produces the current price + // Average the prices from all nodes to get current market price let calculatedUsage = 0; if (nodePriceData.length > 0 && !maxPrice.eq(basePrice)) { - const avgPrice = nodePriceData.reduce((sum, node) => sum.add(node.price), ethers.BigNumber.from(0)).div(nodePriceData.length); - calculatedUsage = avgPrice.sub(basePrice).mul(100).div(maxPrice.sub(basePrice)).toNumber(); + // Sum all node prices and calculate average + const priceSum = nodePriceData.reduce((sum, node) => { + const price = ethers.BigNumber.from(node.price); + return sum.add(price); + }, ethers.BigNumber.from(0)); + const avgPrice = priceSum.div(nodePriceData.length); + + // Debug: log values to understand why calculation might fail + console.log('Usage calculation:', { + nodePrices: nodePriceData.map(n => n.price.toString()), + avgPrice: avgPrice.toString(), + basePrice: basePrice.toString(), + maxPrice: maxPrice.toString(), + avgVsBase: avgPrice.lt(basePrice) ? 'avg < base' : avgPrice.eq(basePrice) ? 'avg = base' : 'avg > base', + }); + + // If avgPrice equals basePrice, usage is 0% + if (avgPrice.lte(basePrice)) { + calculatedUsage = 0; + } + // If avgPrice equals or exceeds maxPrice, usage is 100% + else if (avgPrice.gte(maxPrice)) { + calculatedUsage = 100; + } + // Otherwise, calculate: (avgPrice - basePrice) * 100 / (maxPrice - basePrice) + else { + const priceDiff = avgPrice.sub(basePrice); + const maxBaseDiff = maxPrice.sub(basePrice); + // Multiply by 10000 first to preserve precision, then divide by 100 + calculatedUsage = priceDiff.mul(10000).div(maxBaseDiff).div(100).toNumber(); + // Ensure it's between 0 and 100 + calculatedUsage = Math.max(0, Math.min(100, calculatedUsage)); + } } + console.log('Calculated usage:', calculatedUsage); setUsagePercent(calculatedUsage); const currentPricesResult = await contract.usagePercentToPrices(calculatedUsage, PRODUCT_IDS); From 9852fc05cf325915dbdd44bda64e8ecf06cc4b30 Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Thu, 18 Dec 2025 14:53:17 -0800 Subject: [PATCH 2/5] clarity on cost based component --- docs/node-ops/staking-and-delegation.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/node-ops/staking-and-delegation.mdx b/docs/node-ops/staking-and-delegation.mdx index 824316d6d..544cc368c 100644 --- a/docs/node-ops/staking-and-delegation.mdx +++ b/docs/node-ops/staking-and-delegation.mdx @@ -21,7 +21,7 @@ This page explains how staking and delegation works, including how to stake as a The Lit Protocol employs a dual reward structure to compensate node operators: 1. **Cost-Based Component** -A baseline reward is allocated to each node operator to offset the expenses associated with the required hardware and infrastructure. The baseline reward amount may be adjusted periodically via governance to cover the real-world costs associated with node operations (e.g., server hosting) in their entirety, ensuring operators always break even on the costs associated with running a node and never operate at a net loss. The goal is to preserve a stable pool of node operators even during periods of market volatility, essential for maintaining the shared cryptographic secrets maintained by the network in perpetuity. +A baseline reward of $1,500 in LITKEY tokens is allocated to each node operator to offset the expenses associated with the required hardware and infrastructure. The baseline reward amount may be adjusted periodically via governance to cover the real-world costs associated with node operations (e.g., server hosting) in their entirety, ensuring operators always break even on the costs associated with running a node and never operate at a net loss. The goal is to preserve a stable pool of node operators even during periods of market volatility, essential for maintaining the shared cryptographic secrets maintained by the network in perpetuity. Several configurable parameters go into setting the cost-based component of the Lit node rewards budget, including the price of the \$LITKEY token, the costs associated with running a node (denominated in USD), and a target profit margin factor. From 4d2c43d97afa33dcc02f76455cc04c930165aca7 Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Thu, 18 Dec 2025 15:37:21 -0800 Subject: [PATCH 3/5] median for utilization --- docs/snippets/PriceProvider.jsx | 43 +++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/docs/snippets/PriceProvider.jsx b/docs/snippets/PriceProvider.jsx index 95b8f6f22..28e46e1b3 100644 --- a/docs/snippets/PriceProvider.jsx +++ b/docs/snippets/PriceProvider.jsx @@ -266,36 +266,49 @@ export const PriceProvider = ({ children, component: Component }) => { const maxPrice = maxPricesResult[0]; // Calculate usage percent by finding which usage percentage produces the current price - // Average the prices from all nodes to get current market price + // Use median price from all nodes to get current market price let calculatedUsage = 0; if (nodePriceData.length > 0 && !maxPrice.eq(basePrice)) { - // Sum all node prices and calculate average - const priceSum = nodePriceData.reduce((sum, node) => { - const price = ethers.BigNumber.from(node.price); - return sum.add(price); - }, ethers.BigNumber.from(0)); - const avgPrice = priceSum.div(nodePriceData.length); + // Extract and sort all node prices + const prices = nodePriceData.map(node => ethers.BigNumber.from(node.price)); + prices.sort((a, b) => { + if (a.lt(b)) return -1; + if (a.gt(b)) return 1; + return 0; + }); + + // Calculate median + let medianPrice; + const mid = Math.floor(prices.length / 2); + if (prices.length % 2 === 0) { + // Even number of prices: average the two middle values + medianPrice = prices[mid - 1].add(prices[mid]).div(2); + } else { + // Odd number of prices: use the middle value + medianPrice = prices[mid]; + } // Debug: log values to understand why calculation might fail console.log('Usage calculation:', { nodePrices: nodePriceData.map(n => n.price.toString()), - avgPrice: avgPrice.toString(), + sortedPrices: prices.map(p => p.toString()), + medianPrice: medianPrice.toString(), basePrice: basePrice.toString(), maxPrice: maxPrice.toString(), - avgVsBase: avgPrice.lt(basePrice) ? 'avg < base' : avgPrice.eq(basePrice) ? 'avg = base' : 'avg > base', + medianVsBase: medianPrice.lt(basePrice) ? 'median < base' : medianPrice.eq(basePrice) ? 'median = base' : 'median > base', }); - // If avgPrice equals basePrice, usage is 0% - if (avgPrice.lte(basePrice)) { + // If medianPrice equals basePrice, usage is 0% + if (medianPrice.lte(basePrice)) { calculatedUsage = 0; } - // If avgPrice equals or exceeds maxPrice, usage is 100% - else if (avgPrice.gte(maxPrice)) { + // If medianPrice equals or exceeds maxPrice, usage is 100% + else if (medianPrice.gte(maxPrice)) { calculatedUsage = 100; } - // Otherwise, calculate: (avgPrice - basePrice) * 100 / (maxPrice - basePrice) + // Otherwise, calculate: (medianPrice - basePrice) * 100 / (maxPrice - basePrice) else { - const priceDiff = avgPrice.sub(basePrice); + const priceDiff = medianPrice.sub(basePrice); const maxBaseDiff = maxPrice.sub(basePrice); // Multiply by 10000 first to preserve precision, then divide by 100 calculatedUsage = priceDiff.mul(10000).div(maxBaseDiff).div(100).toNumber(); From ca978449d3b72cae48f8d18e82496be1f37c1873 Mon Sep 17 00:00:00 2001 From: Chris Cassano Date: Thu, 18 Dec 2025 15:52:12 -0800 Subject: [PATCH 4/5] remove lit action from product_ids because it's entirely charged from dynamic pricing components --- docs/snippets/CurrentPricesTable.jsx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/snippets/CurrentPricesTable.jsx b/docs/snippets/CurrentPricesTable.jsx index cf3a24555..fdd7e87a9 100644 --- a/docs/snippets/CurrentPricesTable.jsx +++ b/docs/snippets/CurrentPricesTable.jsx @@ -17,7 +17,6 @@ export const CurrentPricesTable = ({ priceData }) => { const PRODUCT_IDS = [ ProductId.PkpSign, ProductId.EncSign, - ProductId.LitAction, ProductId.SignSessionKey, ]; From 9ec1ee4300fec26c0dc900ce848f3789eff8c8cd Mon Sep 17 00:00:00 2001 From: Chris Cassano <1285652+glitch003@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:33:43 +0700 Subject: [PATCH 5/5] Update docs/snippets/PriceProvider.jsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Chris Cassano <1285652+glitch003@users.noreply.github.com> --- docs/snippets/PriceProvider.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/snippets/PriceProvider.jsx b/docs/snippets/PriceProvider.jsx index 28e46e1b3..0d3dd191d 100644 --- a/docs/snippets/PriceProvider.jsx +++ b/docs/snippets/PriceProvider.jsx @@ -310,8 +310,8 @@ export const PriceProvider = ({ children, component: Component }) => { else { const priceDiff = medianPrice.sub(basePrice); const maxBaseDiff = maxPrice.sub(basePrice); - // Multiply by 10000 first to preserve precision, then divide by 100 - calculatedUsage = priceDiff.mul(10000).div(maxBaseDiff).div(100).toNumber(); + // Calculate percentage (0-100) with integer precision + calculatedUsage = priceDiff.mul(100).div(maxBaseDiff).toNumber(); // Ensure it's between 0 and 100 calculatedUsage = Math.max(0, Math.min(100, calculatedUsage)); }