@glowlabs-org/utils 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/index.js CHANGED
@@ -18769,25 +18769,21 @@ async function createWeeklyReportLegacy(args) {
18769
18769
  if (farm.carbonCreditsProduced > 0 &&
18770
18770
  usdgWeight <= 0 &&
18771
18771
  rewardSplit.usdgSplitPercent > 0) {
18772
- console.log(farm.carbonCreditsProduced, usdgWeight, rewardSplit.usdgSplitPercent);
18773
18772
  throw new Error("USDG weight is less than 0 and carbon credits produced is greater than 0");
18774
18773
  }
18775
18774
  if (farm.weeklyPayment > 0 &&
18776
18775
  glowWeight <= 0 &&
18777
18776
  rewardSplit.glowSplitPercent > 0) {
18778
- console.log(farm.weeklyPayment, glowWeight, rewardSplit.glowSplitPercent);
18779
18777
  throw new Error("Glow weight is less than 0 and weekly payment is greater than 0");
18780
18778
  }
18781
18779
  if (farm.carbonCreditsProduced > 0 &&
18782
18780
  usdgWeight <= 0 &&
18783
18781
  rewardSplit.usdgSplitPercent > 0) {
18784
- console.log(farm.carbonCreditsProduced, usdgWeight.toString(), rewardSplit.usdgSplitPercent);
18785
18782
  throw new Error(`USDG weight is less than or equal to 0 and carbon credits produced is greater than 0 for farm ${farm.shortId}`);
18786
18783
  }
18787
18784
  if (farm.weeklyPayment > 0 &&
18788
18785
  glowWeight <= 0 &&
18789
18786
  rewardSplit.glowSplitPercent > 0) {
18790
- console.log(farm.weeklyPayment, glowWeight.toString(), rewardSplit.glowSplitPercent);
18791
18787
  throw new Error(`Glow weight is less than or equal to 0 and weekly payment is greater than 0 for farm ${farm.shortId}`);
18792
18788
  }
18793
18789
  addValueToMapLegacy(rewardSplit.walletAddress, value, mapLegacy);
@@ -18896,30 +18892,85 @@ async function createWeeklyReportLegacy(args) {
18896
18892
  }
18897
18893
  async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
18898
18894
  const map = new Map();
18895
+ // Fetch active farms for the week (source of truth) and audits for the week.
18899
18896
  const [apiResponse, auditsRes] = await Promise.all([
18900
18897
  fetchFarmsForWeek(week, gcaUrls, apiUrl),
18901
18898
  axios.get(`${HUB_URL}/api/audits?omitDocuments=true&weekNumber=${week}`),
18902
18899
  ]);
18903
- const farms = apiResponse.filteredFarms;
18904
- const audits = auditsRes.data;
18900
+ const farms = apiResponse.filteredFarms; // List of farms active this week.
18901
+ const audits = auditsRes.data; // Audits potentially containing adjusted credits.
18905
18902
  const rawData = apiResponse.rawData;
18903
+ // Map to store the final adjusted credit (as bigint) for each active farm shortId.
18906
18904
  const shortIdToAdjustedCredit = new Map();
18905
+ // Set to track shortIds that have been assigned a credit from a valid audit to prevent duplicates.
18906
+ const processedAuditShortIds = new Set();
18907
+ // Set of shortIds from the active farms list for quick lookup.
18908
+ const farmShortIds = new Set(farms.map((f) => String(f.shortId)));
18909
+ // Process each audit to extract adjusted credit values for active farms.
18907
18910
  for (const audit of audits) {
18908
18911
  const activeShortIds = audit.activeShortIds ?? [];
18909
18912
  const adjustedCredit = audit.summary?.carbonFootprintAndProduction?.adjustedWeeklyCarbonCredit?.trim();
18913
+ // Basic validation: Ensure the audit has a valid numeric credit and lists associated shortIds.
18910
18914
  if (!adjustedCredit || !NUMERIC_REGEX.test(adjustedCredit))
18911
18915
  throw new Error(`Invalid adjustedWeeklyCarbonCredit for audit ${audit.id}`);
18912
18916
  if (activeShortIds.length === 0)
18913
- throw new Error(`audit ${audit.id} has empty activeShortIds`);
18914
- const splitValue = new Decimal(adjustedCredit).div(activeShortIds.length);
18917
+ throw new Error(`Audit ${audit.id} has empty activeShortIds`);
18918
+ // Separate the shortIds listed in the audit into those that are active this week and those that are not.
18919
+ const auditActiveFarmSids = [];
18920
+ const auditInactiveFarmSids = [];
18921
+ for (const sid of activeShortIds) {
18922
+ const sidString = String(sid);
18923
+ if (farmShortIds.has(sidString)) {
18924
+ auditActiveFarmSids.push(sidString);
18925
+ }
18926
+ else {
18927
+ auditInactiveFarmSids.push(sidString);
18928
+ }
18929
+ }
18930
+ // --- Audit Validity Checks ---
18931
+ // Data Integrity Check: An audit for credit distribution should not mix active and inactive farms for the current week.
18932
+ if (auditActiveFarmSids.length > 0 && auditInactiveFarmSids.length > 0) {
18933
+ throw new Error(`Audit ${audit.id} for week ${week} contains a mix of active farms (${auditActiveFarmSids.join(", ")}) and inactive farms (${auditInactiveFarmSids.join(", ")}). Audits should only contain active farms when distributing credits.`);
18934
+ }
18935
+ // Skip Audit: If the audit only contains shortIds that are not active this week, it's irrelevant for this report.
18936
+ if (auditActiveFarmSids.length === 0) {
18937
+ continue; // Move to the next audit.
18938
+ }
18939
+ // --- Process Valid Audit for Active Farms ---
18940
+ // Calculate the credit share per active farm within this audit.
18941
+ const splitValue = new Decimal(adjustedCredit).div(auditActiveFarmSids.length // Divide only by the count of *active* farms found in this audit.
18942
+ );
18943
+ // Sanity Check: Ensure the calculated split is not zero.
18915
18944
  if (splitValue.isZero())
18916
- throw new Error(`Zero adjustedWeeklyCarbonCredit split in audit ${audit.id}`);
18945
+ throw new Error(`Zero adjustedWeeklyCarbonCredit split for active farms in audit ${audit.id}`);
18946
+ // Convert the decimal split value to a bigint using the defined precision.
18917
18947
  const splitBigInt = toBigInt(splitValue, USDG_WEIGHT_DECIMAL_PRECISION);
18918
- for (const sid of activeShortIds)
18919
- shortIdToAdjustedCredit.set(String(sid), splitBigInt);
18948
+ // Dust Check: Ensure that the conversion to bigint didn't truncate the value to zero.
18949
+ if (splitBigInt === BigInt(0)) {
18950
+ throw new Error(`Adjusted credit split for audit ${audit.id} resulted in zero BigInt after conversion for precision ${USDG_WEIGHT_DECIMAL_PRECISION}. Original split value: ${splitValue.toString()}. This might indicate dust loss.`);
18951
+ }
18952
+ // Distribute the calculated split value to each active farm associated with this audit.
18953
+ for (const sidString of auditActiveFarmSids) {
18954
+ // Duplicate Check: Ensure this shortId hasn't already received credit from another valid audit this week.
18955
+ if (processedAuditShortIds.has(sidString)) {
18956
+ throw new Error(`ShortId ${sidString} found in multiple valid audits for week ${week}`);
18957
+ }
18958
+ processedAuditShortIds.add(sidString); // Mark this shortId as processed.
18959
+ // Store the adjusted credit for this active farm.
18960
+ shortIdToAdjustedCredit.set(sidString, splitBigInt);
18961
+ }
18962
+ } // End of loop through audits.
18963
+ // Final Validation: Ensure every farm considered active for this week received an adjusted credit value from a valid audit.
18964
+ for (const farmId of farmShortIds) {
18965
+ if (!shortIdToAdjustedCredit.has(farmId)) {
18966
+ throw new Error(`Farm ${farmId} is active for week ${week} but has no corresponding adjusted credit found in any valid audit.`);
18967
+ }
18920
18968
  }
18921
- let totalCreditsProduced18dp = BigInt(0);
18969
+ // --- Start Calculation of Merkle Tree Weights ---
18970
+ let totalCreditsProduced18dp = BigInt(0); // Raw total from farm data, for reporting/comparison.
18971
+ // Iterate through each active farm to calculate its contribution to the Merkle tree leaves.
18922
18972
  for (const farm of farms) {
18973
+ // Basic checks for farm data consistency.
18923
18974
  if (farm.status === "Unassigned") {
18924
18975
  throw new Error(`farm ${farm.shortId} is unassigned`);
18925
18976
  }
@@ -18929,7 +18980,9 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
18929
18980
  console.log(`Farm: ${farm.shortId} has ${farm.carbonCreditsProduced} carbon credits produced`);
18930
18981
  throw new Error("Carbon credits produced is less than 0 for farm " + farm.shortId);
18931
18982
  }
18983
+ // Accumulate the reported carbon credits (18 decimal places).
18932
18984
  totalCreditsProduced18dp += parseUnits(customToFixed(farm.carbonCreditsProduced, 18), 18);
18985
+ // Validate reward split percentages add up to 1 (within tolerance).
18933
18986
  const sumGlow = farm.rewardSplits.reduce((acc, r) => acc + r.glowSplitPercent, 0);
18934
18987
  const sumUSDG = farm.rewardSplits.reduce((acc, r) => acc + r.usdgSplitPercent, 0);
18935
18988
  const percentTolerance = 1e-12;
@@ -18937,57 +18990,76 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
18937
18990
  throw new Error(`Glow splits ≠1 for farm ${farm.shortId}`);
18938
18991
  if (Math.abs(sumUSDG - 1) > percentTolerance)
18939
18992
  throw new Error(`USDG splits ≠1 for farm ${farm.shortId}`);
18993
+ // Get the base GLOW payment for the farm, converted to bigint with appropriate precision.
18940
18994
  const glowBase = parseUnits(customToFixed(farm.weeklyPayment, GLOW_WEIGHT_DECIMAL_PRECISION), GLOW_WEIGHT_DECIMAL_PRECISION);
18995
+ // Retrieve the pre-calculated adjusted credit for this farm. Should always exist due to prior checks.
18941
18996
  const adjustedCreditBigInt = shortIdToAdjustedCredit.get(String(farm.shortId)) ?? BigInt(0);
18997
+ // Sanity check (likely redundant due to earlier checks, but safe).
18942
18998
  if (adjustedCreditBigInt <= BigInt(0))
18943
18999
  throw new Error(`Adjusted credit is less than or equal to 0 for farm ${farm.shortId}`);
19000
+ // Process each reward split recipient for this farm.
18944
19001
  for (const split of farm.rewardSplits) {
19002
+ // Calculate the GLOW weight for this recipient based on the farm's weekly payment and split percentage.
18945
19003
  const glowWeight = multiplyBigIntByDecimalPercentage(glowBase, GLOW_WEIGHT_DECIMAL_PRECISION, split.glowSplitPercent);
19004
+ // Calculate the USDG weight for this recipient based on the farm's *adjusted* credit and split percentage.
18946
19005
  const usdgWeight = multiplyBigIntByDecimalPercentage(adjustedCreditBigInt, USDG_WEIGHT_DECIMAL_PRECISION, split.usdgSplitPercent);
19006
+ // Dust Loss Checks: Ensure non-zero percentages didn't result in zero weight due to precision limits.
18947
19007
  if (split.usdgSplitPercent > 0 &&
18948
19008
  adjustedCreditBigInt > BigInt(0) &&
18949
19009
  usdgWeight === BigInt(0))
18950
- throw new Error(`USDG dust lost for farm ${farm.shortId}`);
19010
+ throw new Error(`USDG dust lost for farm ${farm.shortId} wallet ${split.walletAddress}`);
18951
19011
  if (split.glowSplitPercent > 0 &&
18952
19012
  glowBase > BigInt(0) &&
18953
19013
  glowWeight === BigInt(0))
18954
- throw new Error(`Glow dust lost for farm ${farm.shortId}`);
19014
+ throw new Error(`Glow dust lost for farm ${farm.shortId} wallet ${split.walletAddress}`);
19015
+ // Overflow Check: Ensure individual leaf weights don't exceed the maximum allowed.
18955
19016
  if (glowWeight > MAX_WEIGHT || usdgWeight > MAX_WEIGHT)
18956
19017
  throw new Error(`Leaf weight overflow on wallet ${split.walletAddress}`);
19018
+ // Accumulate the calculated weights into the map for the recipient wallet.
18957
19019
  accumulateLeafWeights(split.walletAddress, { wallet: split.walletAddress, glowWeight, usdgWeight }, map);
18958
19020
  }
18959
19021
  }
19022
+ // --- Finalize Merkle Tree and Generate Report Data ---
19023
+ // Convert the accumulated weights map into the final list of leaves for the tree.
18960
19024
  const finalizedLeaves = Array.from(map.values()).map(({ wallet, glowWeight, usdgWeight }) => ({
18961
19025
  wallet,
18962
- glowWeight: glowWeight.toString(),
19026
+ glowWeight: glowWeight.toString(), // Convert BigInts to strings for hashing/reporting.
18963
19027
  usdgWeight: usdgWeight.toString(),
18964
19028
  }));
19029
+ // Hash each leaf according to the defined structure.
18965
19030
  const hashedLeaves = finalizedLeaves.map((leaf) => hashLeaf({
18966
19031
  address: leaf.wallet,
18967
19032
  glowWeight: leaf.glowWeight,
18968
- usdcWeight: leaf.usdgWeight,
19033
+ usdcWeight: leaf.usdgWeight, // Note: Parameter name inconsistency (usdc vs usdg)
18969
19034
  }));
19035
+ // Build the Merkle tree.
18970
19036
  const merkleTree = new MerkleTree(hashedLeaves, ethers.utils.keccak256, {
18971
- sort: true,
19037
+ sort: true, // Ensure consistent tree structure.
18972
19038
  });
18973
- const merkleRoot = merkleTree.getHexRoot();
19039
+ const merkleRoot = merkleTree.getHexRoot(); // Get the root hash.
19040
+ // Calculate the total weights across all finalized leaves.
18974
19041
  const totalGlowWeight = finalizedLeaves.reduce((acc, { glowWeight }) => acc.add(glowWeight), ethers.BigNumber.from(0));
18975
19042
  const totalUSDGWeight = finalizedLeaves.reduce((acc, { usdgWeight }) => acc.add(usdgWeight), ethers.BigNumber.from(0));
19043
+ // Total Weight Overflow Checks: Ensure sums don't exceed maximum.
18976
19044
  if (totalGlowWeight.toBigInt() > MAX_WEIGHT)
18977
19045
  throw new Error("Total glow weight overflow");
18978
19046
  if (totalUSDGWeight.toBigInt() > MAX_WEIGHT)
18979
19047
  throw new Error("Total USDG weight overflow");
19048
+ // Generate Merkle proofs for each leaf and verify them.
18980
19049
  const leavesWithProofs = finalizedLeaves.map((leaf) => {
18981
19050
  const hashed = hashLeaf({
18982
19051
  address: leaf.wallet,
18983
19052
  glowWeight: leaf.glowWeight,
18984
- usdcWeight: leaf.usdgWeight,
19053
+ usdcWeight: leaf.usdgWeight, // Note: Parameter name inconsistency
18985
19054
  });
18986
19055
  const proof = merkleTree.getHexProof(hashed);
18987
19056
  if (!merkleTree.verify(proof, hashed, merkleRoot))
19057
+ // Verify proof against the calculated root.
18988
19058
  throw new Error("Invalid proof for " + leaf.wallet);
18989
19059
  return { ...leaf, proof };
18990
19060
  });
19061
+ // --- Sanity Checks on Final Weights ---
19062
+ // Verify that summing weights from leavesWithProofs matches the earlier reduce calculation.
18991
19063
  const glowSumProofLoop = leavesWithProofs.reduce((acc, l) => acc.add(l.glowWeight), ethers.BigNumber.from(0));
18992
19064
  if (!glowSumProofLoop.eq(totalGlowWeight)) {
18993
19065
  console.error("Glow Weight Sum Mismatch (Post-73):");
@@ -19002,43 +19074,55 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
19002
19074
  console.error("Sum from reduce:", totalUSDGWeight.toString());
19003
19075
  throw new Error("USDG sum mismatch");
19004
19076
  }
19077
+ // Calculate the total expected credits based on the adjusted values used.
19005
19078
  const totalExpectedCredits = farms.reduce((acc, f) => {
19006
19079
  const adj = shortIdToAdjustedCredit.get(String(f.shortId)) ?? BigInt(0);
19007
19080
  return acc.plus(fromBigInt(adj, USDG_WEIGHT_DECIMAL_PRECISION));
19008
19081
  }, new Decimal(0));
19082
+ // Convert total USDG weight back to human-readable decimal for deviation check.
19009
19083
  const totalUSDGWeightHuman = new Decimal(formatUnits(BigInt(totalUSDGWeight.toString()), USDG_WEIGHT_DECIMAL_PRECISION));
19010
- if (greaterThanMaxDeviation(totalExpectedCredits.toNumber(), totalUSDGWeightHuman.toNumber(), 0.001)) {
19084
+ // Check deviation between total adjusted credits used and the final sum of USDG weights.
19085
+ if (greaterThanMaxDeviation(totalExpectedCredits.toNumber(), totalUSDGWeightHuman.toNumber(), 0.001 // 0.1% tolerance
19086
+ )) {
19011
19087
  console.error("Deviation Check Failed (Post-73): Expected Credits vs USDG Weight");
19012
19088
  console.error("Total Expected Credits:", totalExpectedCredits.toNumber());
19013
19089
  console.error("Total USDG Weight:", totalUSDGWeightHuman.toNumber());
19014
19090
  console.error("Allowed Deviation:", 0.001);
19015
19091
  throw new Error("totalExpectedCredits vs USDG weight deviation >0.1% ");
19016
19092
  }
19093
+ // Convert total Glow weight back to human-readable decimal.
19017
19094
  const totalGlowWeightHuman = new Decimal(formatUnits(BigInt(totalGlowWeight.toString()), GLOW_WEIGHT_DECIMAL_PRECISION));
19095
+ // Sum the original weekly protocol fee payments from farm data.
19018
19096
  const totalProtocolFeePayments = farms.reduce((acc, f) => acc + f.weeklyPayment, 0);
19019
- if (greaterThanMaxDeviation(totalGlowWeightHuman.toNumber(), totalProtocolFeePayments, 0.001)) {
19097
+ // Check deviation between total glow weight and total protocol fees paid.
19098
+ if (greaterThanMaxDeviation(totalGlowWeightHuman.toNumber(), totalProtocolFeePayments, 0.001 // 0.1% tolerance
19099
+ )) {
19020
19100
  console.error("Deviation Check Failed (Post-73): Glow Weight vs Protocol Fees");
19021
19101
  console.error("Total Glow Weight:", totalGlowWeightHuman.toNumber());
19022
19102
  console.error("Total Protocol Fees:", totalProtocolFeePayments);
19023
19103
  console.error("Allowed Deviation:", 0.001);
19024
19104
  throw new Error("totalGlowWeight vs protocol fees deviation >0.1% ");
19025
19105
  }
19106
+ // --- Prepare Output Data ---
19107
+ // Key statistics for the report.
19026
19108
  const headlineStats = {
19027
19109
  weekNumber: week,
19028
- totalCreditsProduced: formatUnits(totalCreditsProduced18dp, 18),
19110
+ totalCreditsProduced: formatUnits(totalCreditsProduced18dp, 18), // Original reported total
19029
19111
  totalCreditsProducedBN: totalCreditsProduced18dp.toString(),
19030
- totalGlowWeightInFinalized: totalGlowWeight.toString(),
19031
- totalGlowWeightHuman: totalGlowWeightHuman.toString(),
19112
+ totalGlowWeightInFinalized: totalGlowWeight.toString(), // Total weight as BigNumber string
19113
+ totalGlowWeightHuman: totalGlowWeightHuman.toString(), // Total weight as human-readable decimal
19032
19114
  totalUSDGWeightInFinalized: totalUSDGWeight.toString(),
19033
19115
  totalUSDGWeightHuman: totalUSDGWeightHuman.toString(),
19034
19116
  root: merkleRoot,
19035
19117
  glowWeightDecimals: GLOW_WEIGHT_DECIMAL_PRECISION,
19036
19118
  usdgWeightDecimals: USDG_WEIGHT_DECIMAL_PRECISION,
19037
19119
  };
19120
+ // List of shortIds and their final adjusted credit value (human-readable).
19038
19121
  const shortIdAdjustedList = Array.from(shortIdToAdjustedCredit.entries()).map(([shortId, creditBigInt]) => ({
19039
19122
  shortId,
19040
19123
  adjustedCredit: fromBigInt(creditBigInt, USDG_WEIGHT_DECIMAL_PRECISION).toString(),
19041
19124
  }));
19125
+ // Detailed comparison between originally reported credits and adjusted credits for each farm.
19042
19126
  const creditsDeviationList = farms.map((farm) => {
19043
19127
  const adj = shortIdToAdjustedCredit.get(String(farm.shortId)) ?? BigInt(0);
19044
19128
  const adjusted = fromBigInt(adj, USDG_WEIGHT_DECIMAL_PRECISION);
@@ -19046,6 +19130,7 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
19046
19130
  let deviation;
19047
19131
  let absDeviation;
19048
19132
  let deviationPercent;
19133
+ // Handle division by zero if produced is zero.
19049
19134
  if (produced.isZero() && adjusted.isZero()) {
19050
19135
  deviation = new Decimal(0);
19051
19136
  absDeviation = new Decimal(0);
@@ -19055,7 +19140,7 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
19055
19140
  deviation = produced.minus(adjusted);
19056
19141
  absDeviation = deviation.abs();
19057
19142
  deviationPercent = deviation
19058
- .div(produced.isZero() ? 1 : produced)
19143
+ .div(produced.isZero() ? 1 : produced) // Avoid division by zero
19059
19144
  .mul(100);
19060
19145
  }
19061
19146
  return {
@@ -19067,14 +19152,15 @@ async function createWeeklyReport({ week, gcaUrls, apiUrl, }) {
19067
19152
  deviationPercent: deviationPercent.toNumber(),
19068
19153
  };
19069
19154
  });
19155
+ // Return all calculated data.
19070
19156
  return {
19071
19157
  headlineStats,
19072
- finalizedLeaves,
19073
- leavesWithProofs,
19074
- farms,
19075
- rawData,
19076
- shortIdAdjustedList,
19077
- creditsDeviationList,
19158
+ finalizedLeaves, // Leaves without proofs (for potential use cases)
19159
+ leavesWithProofs, // Leaves with proofs (primary data for distribution)
19160
+ farms, // Original filtered farm data
19161
+ rawData, // Raw GCA responses
19162
+ shortIdAdjustedList, // List of adjusted credits per farm
19163
+ creditsDeviationList, // Detailed deviation report per farm
19078
19164
  };
19079
19165
  }
19080
19166