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