@glowlabs-org/utils 0.0.9 → 0.1.1

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.
Files changed (130) hide show
  1. package/.github/workflows/publish.yml +14 -4
  2. package/dist/cjs/constants/regex.d.ts +1 -0
  3. package/dist/cjs/constants/urls.d.ts +2 -0
  4. package/dist/cjs/constants/weights.d.ts +12 -0
  5. package/dist/cjs/index.d.ts +1 -1
  6. package/dist/cjs/index.js +18988 -89
  7. package/dist/cjs/index.js.map +1 -1
  8. package/dist/cjs/lib/create-weekly-report/index.d.ts +73 -0
  9. package/dist/cjs/lib/create-weekly-report/types/Audit.d.ts +40 -0
  10. package/dist/cjs/lib/create-weekly-report/types/index.d.ts +7 -0
  11. package/dist/cjs/lib/create-weekly-report/types/merkle.d.ts +10 -0
  12. package/dist/cjs/lib/create-weekly-report/utils/accumulate-leaf-weights.d.ts +2 -0
  13. package/dist/cjs/lib/create-weekly-report/utils/custom-to-fixed.d.ts +1 -0
  14. package/dist/cjs/lib/create-weekly-report/utils/fetch-farms-for-week.d.ts +12 -0
  15. package/dist/cjs/lib/create-weekly-report/utils/from-bigint.d.ts +7 -0
  16. package/dist/cjs/lib/create-weekly-report/utils/greater-than-max-deviation.d.ts +1 -0
  17. package/dist/cjs/lib/create-weekly-report/utils/hashLeaf.d.ts +7 -0
  18. package/dist/cjs/lib/create-weekly-report/utils/multiply-bigInt-by-decimal-percentage.d.ts +1 -0
  19. package/dist/cjs/lib/create-weekly-report/utils/to-bigint.d.ts +7 -0
  20. package/dist/cjs/utils/custom-to-fixed.d.ts +1 -0
  21. package/dist/cjs/utils/greater-than-max-deviation.d.ts +1 -0
  22. package/dist/cjs/utils/hash-leaf.d.ts +7 -0
  23. package/dist/esm/constants/regex.d.ts +1 -0
  24. package/dist/esm/constants/urls.d.ts +2 -0
  25. package/dist/esm/constants/weights.d.ts +12 -0
  26. package/dist/esm/index.d.ts +1 -1
  27. package/dist/esm/index.js +18988 -89
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/lib/create-weekly-report/index.d.ts +73 -0
  30. package/dist/esm/lib/create-weekly-report/types/Audit.d.ts +40 -0
  31. package/dist/esm/lib/create-weekly-report/types/RewardSplit copy.d.ts +5 -0
  32. package/dist/esm/lib/create-weekly-report/types/RewardSplit.d.ts +5 -0
  33. package/dist/esm/lib/create-weekly-report/types/RewardSplits copy.d.ts +2 -0
  34. package/dist/esm/lib/create-weekly-report/types/RewardSplits.d.ts +2 -0
  35. package/dist/esm/lib/create-weekly-report/types/index.d.ts +7 -0
  36. package/dist/esm/lib/create-weekly-report/types/merkle.d.ts +10 -0
  37. package/dist/esm/lib/create-weekly-report/utils/accumulate-leaf-weights.d.ts +2 -0
  38. package/dist/esm/lib/create-weekly-report/utils/custom-to-fixed.d.ts +1 -0
  39. package/dist/esm/lib/create-weekly-report/utils/fetch-farms-for-week.d.ts +12 -0
  40. package/dist/esm/lib/create-weekly-report/utils/from-bigint.d.ts +7 -0
  41. package/dist/esm/lib/create-weekly-report/utils/greater-than-max-deviation.d.ts +1 -0
  42. package/dist/esm/lib/create-weekly-report/utils/hashLeaf.d.ts +7 -0
  43. package/dist/esm/lib/create-weekly-report/utils/multiply-bigInt-by-decimal-percentage.d.ts +1 -0
  44. package/dist/esm/lib/create-weekly-report/utils/to-bigint.d.ts +7 -0
  45. package/dist/esm/utils/custom-to-fixed.d.ts +1 -0
  46. package/dist/esm/utils/greater-than-max-deviation.d.ts +1 -0
  47. package/dist/esm/utils/hash-leaf.d.ts +7 -0
  48. package/package.json +13 -9
  49. package/rollup.config.js +2 -0
  50. package/src/constants/regex.ts +2 -0
  51. package/src/constants/urls.ts +3 -0
  52. package/src/constants/weights.ts +15 -0
  53. package/src/index.ts +1 -1
  54. package/src/lib/create-weekly-report/index.ts +691 -0
  55. package/src/lib/create-weekly-report/types/Audit.ts +41 -0
  56. package/src/{types → lib/create-weekly-report/types}/AuditCompleted.ts +3 -1
  57. package/src/lib/create-weekly-report/types/AuditInherited.ts +8 -0
  58. package/src/lib/create-weekly-report/types/Device.ts +7 -0
  59. package/src/lib/create-weekly-report/types/DuplicatedButPastAuditCompleted.ts +10 -0
  60. package/src/lib/create-weekly-report/types/Farm.ts +22 -0
  61. package/src/{types → lib/create-weekly-report/types}/FarmStatus.ts +6 -6
  62. package/src/{types → lib/create-weekly-report/types}/GCAServerResponse.ts +5 -1
  63. package/src/lib/create-weekly-report/types/GetEquipmentDataHandlerParams.ts +7 -0
  64. package/src/lib/create-weekly-report/types/PayoutWalletAndSplit.ts +25 -0
  65. package/src/lib/create-weekly-report/types/PayoutWalletAndSplitWithRewardSplits.ts +23 -0
  66. package/src/lib/create-weekly-report/types/RewardSplit copy.ts +7 -0
  67. package/src/lib/create-weekly-report/types/RewardSplit.ts +7 -0
  68. package/src/{types/RewardSplit.ts → lib/create-weekly-report/types/RewardSplits copy.ts } +2 -1
  69. package/src/{types → lib/create-weekly-report/types}/RewardSplits.ts +1 -1
  70. package/src/{types → lib/create-weekly-report/types}/SlotRange.ts +4 -1
  71. package/src/{types → lib/create-weekly-report/types}/WeekAndSlotRange.ts +4 -1
  72. package/src/lib/create-weekly-report/types/index.ts +7 -0
  73. package/src/lib/create-weekly-report/types/merkle.ts +11 -0
  74. package/src/lib/create-weekly-report/types/stashed-Farm.ts +22 -0
  75. package/src/lib/create-weekly-report/utils/accumulate-leaf-weights.ts +17 -0
  76. package/src/lib/create-weekly-report/utils/custom-to-fixed.ts +9 -0
  77. package/src/lib/create-weekly-report/utils/fetch-farms-for-week.ts +30 -0
  78. package/src/lib/create-weekly-report/utils/from-bigint.ts +12 -0
  79. package/src/lib/create-weekly-report/utils/greater-than-max-deviation.ts +9 -0
  80. package/src/lib/create-weekly-report/utils/hashLeaf.ts +20 -0
  81. package/src/lib/create-weekly-report/utils/multiply-bigInt-by-decimal-percentage.ts +15 -0
  82. package/src/lib/create-weekly-report/utils/to-bigint.ts +16 -0
  83. package/src/utils/custom-to-fixed.ts +9 -0
  84. package/src/utils/greater-than-max-deviation.ts +9 -0
  85. package/src/utils/hash-leaf.ts +22 -0
  86. package/tsconfig.json +2 -2
  87. package/dist/cjs/types/GCAAndServerUrl.d.ts +0 -5
  88. package/dist/cjs/types/index.d.ts +0 -14
  89. package/dist/esm/types/GCAAndServerUrl.d.ts +0 -5
  90. package/dist/esm/types/index.d.ts +0 -14
  91. package/src/types/AuditInherited.ts +0 -3
  92. package/src/types/Device.ts +0 -3
  93. package/src/types/DuplicatedButPastAuditCompleted.ts +0 -4
  94. package/src/types/Farm.ts +0 -22
  95. package/src/types/GCAAndServerUrl.ts +0 -5
  96. package/src/types/GetEquipmentDataHandlerParams.ts +0 -3
  97. package/src/types/PayoutWalletAndSplit.ts +0 -5
  98. package/src/types/PayoutWalletAndSplitWithRewardSplits.ts +0 -6
  99. package/src/types/index.ts +0 -14
  100. package/src/types/stashed-Farm.ts +0 -22
  101. /package/dist/cjs/{types → lib/create-weekly-report/types}/AuditCompleted.d.ts +0 -0
  102. /package/dist/cjs/{types → lib/create-weekly-report/types}/AuditInherited.d.ts +0 -0
  103. /package/dist/cjs/{types → lib/create-weekly-report/types}/Device.d.ts +0 -0
  104. /package/dist/cjs/{types → lib/create-weekly-report/types}/DuplicatedButPastAuditCompleted.d.ts +0 -0
  105. /package/dist/cjs/{types → lib/create-weekly-report/types}/Farm.d.ts +0 -0
  106. /package/dist/cjs/{types → lib/create-weekly-report/types}/FarmStatus.d.ts +0 -0
  107. /package/dist/cjs/{types → lib/create-weekly-report/types}/GCAServerResponse.d.ts +0 -0
  108. /package/dist/cjs/{types → lib/create-weekly-report/types}/GetEquipmentDataHandlerParams.d.ts +0 -0
  109. /package/dist/cjs/{types → lib/create-weekly-report/types}/PayoutWalletAndSplit.d.ts +0 -0
  110. /package/dist/cjs/{types → lib/create-weekly-report/types}/PayoutWalletAndSplitWithRewardSplits.d.ts +0 -0
  111. /package/dist/cjs/{types/RewardSplit.d.ts → lib/create-weekly-report/types/RewardSplit copy.d.ts} +0 -0
  112. /package/dist/{esm → cjs/lib/create-weekly-report}/types/RewardSplit.d.ts +0 -0
  113. /package/dist/cjs/{types/RewardSplits.d.ts → lib/create-weekly-report/types/RewardSplits copy.d.ts} +0 -0
  114. /package/dist/{esm → cjs/lib/create-weekly-report}/types/RewardSplits.d.ts +0 -0
  115. /package/dist/cjs/{types → lib/create-weekly-report/types}/SlotRange.d.ts +0 -0
  116. /package/dist/cjs/{types → lib/create-weekly-report/types}/WeekAndSlotRange.d.ts +0 -0
  117. /package/dist/cjs/{types → lib/create-weekly-report/types}/stashed-Farm.d.ts +0 -0
  118. /package/dist/esm/{types → lib/create-weekly-report/types}/AuditCompleted.d.ts +0 -0
  119. /package/dist/esm/{types → lib/create-weekly-report/types}/AuditInherited.d.ts +0 -0
  120. /package/dist/esm/{types → lib/create-weekly-report/types}/Device.d.ts +0 -0
  121. /package/dist/esm/{types → lib/create-weekly-report/types}/DuplicatedButPastAuditCompleted.d.ts +0 -0
  122. /package/dist/esm/{types → lib/create-weekly-report/types}/Farm.d.ts +0 -0
  123. /package/dist/esm/{types → lib/create-weekly-report/types}/FarmStatus.d.ts +0 -0
  124. /package/dist/esm/{types → lib/create-weekly-report/types}/GCAServerResponse.d.ts +0 -0
  125. /package/dist/esm/{types → lib/create-weekly-report/types}/GetEquipmentDataHandlerParams.d.ts +0 -0
  126. /package/dist/esm/{types → lib/create-weekly-report/types}/PayoutWalletAndSplit.d.ts +0 -0
  127. /package/dist/esm/{types → lib/create-weekly-report/types}/PayoutWalletAndSplitWithRewardSplits.d.ts +0 -0
  128. /package/dist/esm/{types → lib/create-weekly-report/types}/SlotRange.d.ts +0 -0
  129. /package/dist/esm/{types → lib/create-weekly-report/types}/WeekAndSlotRange.d.ts +0 -0
  130. /package/dist/esm/{types → lib/create-weekly-report/types}/stashed-Farm.d.ts +0 -0
@@ -0,0 +1,691 @@
1
+ import axios from "axios";
2
+ import { formatUnits, parseUnits } from "viem";
3
+ import { MerkleTree } from "merkletreejs";
4
+ import { BigNumber, ethers } from "ethers";
5
+ import { greaterThanMaxDeviation } from "./utils/greater-than-max-deviation";
6
+ import { customToFixed } from "./utils/custom-to-fixed";
7
+ import { HUB_URL } from "../../constants/urls";
8
+ import Decimal from "decimal.js";
9
+ import { multiplyBigIntByDecimalPercentage } from "./utils/multiply-bigInt-by-decimal-percentage";
10
+ import { toBigInt } from "./utils/to-bigint";
11
+ import { fromBigInt } from "./utils/from-bigint";
12
+ import { fetchFarmsForWeek } from "./utils/fetch-farms-for-week";
13
+ import { accumulateLeafWeights } from "./utils/accumulate-leaf-weights";
14
+ import { FinalizedLeaf, MerkleLeaf } from "./types/merkle";
15
+ import { Audit } from "./types/Audit";
16
+ import { NUMERIC_REGEX } from "../../constants/regex";
17
+ import {
18
+ GLOW_WEIGHT_DECIMAL_PRECISION,
19
+ MAX_WEIGHT,
20
+ USDG_WEIGHT_DECIMAL_PRECISION,
21
+ } from "../../constants/weights";
22
+ import { hashLeaf } from "./utils/hashLeaf";
23
+
24
+ export type MerkleLeafIntermediary = {
25
+ wallet: string;
26
+ glowWeight: bigint;
27
+ usdgWeight: bigint;
28
+ };
29
+
30
+ export type FinalLeaf = {
31
+ wallet: string;
32
+ glowWeight: string;
33
+ usdgWeight: string;
34
+ };
35
+
36
+ export type CreateWeeklyReportArgs = {
37
+ week: number;
38
+ gcaUrls: string[];
39
+ apiUrl: string;
40
+ };
41
+
42
+ async function createWeeklyReportLegacy(args: CreateWeeklyReportArgs) {
43
+ const mapLegacy = new Map<string, MerkleLeafIntermediary>();
44
+
45
+ function addValueToMapLegacy(
46
+ key: string,
47
+ value: MerkleLeafIntermediary,
48
+ map: Map<string, MerkleLeafIntermediary>
49
+ ) {
50
+ if (map.has(key)) {
51
+ const existingValue = map.get(key)!;
52
+ existingValue.glowWeight += value.glowWeight;
53
+ existingValue.usdgWeight += value.usdgWeight;
54
+ if (
55
+ existingValue.glowWeight > MAX_WEIGHT ||
56
+ existingValue.usdgWeight > MAX_WEIGHT
57
+ ) {
58
+ throw new Error(`Accumulated weight overflow on wallet ${key}`);
59
+ }
60
+ map.set(key, existingValue);
61
+ } else {
62
+ if (value.glowWeight > MAX_WEIGHT || value.usdgWeight > MAX_WEIGHT) {
63
+ throw new Error(`Initial weight overflow on wallet ${key}`);
64
+ }
65
+ map.set(key, value);
66
+ }
67
+ }
68
+
69
+ const apiResposne = await fetchFarmsForWeek(
70
+ args.week,
71
+ args.gcaUrls,
72
+ args.apiUrl
73
+ );
74
+ const farms = apiResposne.filteredFarms;
75
+ const rawData = apiResposne.rawData;
76
+ let totalCreditsProduced: bigint = BigInt(0);
77
+
78
+ for (const farm of farms) {
79
+ if (farm.status === "Unassigned") {
80
+ throw new Error(`Farm ${farm.shortId} is unassigned`);
81
+ }
82
+
83
+ if (farm.carbonCreditsProduced < 0) {
84
+ console.log(
85
+ `Farm: ${farm.shortId} has ${farm.carbonCreditsProduced} carbon credits produced`
86
+ );
87
+ throw new Error("Carbon credits produced is less than 0");
88
+ }
89
+ totalCreditsProduced += parseUnits(
90
+ customToFixed(farm.carbonCreditsProduced, 18),
91
+ 18
92
+ );
93
+
94
+ if (!farm.rewardSplits)
95
+ throw new Error(`No reward splits for farm ${farm.shortId}`);
96
+
97
+ const sumOfRewardSplitsGlowWeight = farm.rewardSplits.reduce(
98
+ (acc, rewardSplit) => acc + rewardSplit.glowSplitPercent,
99
+ 0
100
+ );
101
+ const sumOfRewardSplitsUSDGWeight = farm.rewardSplits.reduce(
102
+ (acc, rewardSplit) => acc + rewardSplit.usdgSplitPercent,
103
+ 0
104
+ );
105
+ const percentTolerance = 1e-12;
106
+ if (Math.abs(sumOfRewardSplitsGlowWeight - 1) > percentTolerance)
107
+ throw new Error(
108
+ `Glow weight splits do not add up to 1 for farm ${farm.shortId}`
109
+ );
110
+ if (Math.abs(sumOfRewardSplitsUSDGWeight - 1) > percentTolerance)
111
+ throw new Error(
112
+ `USDG weight splits do not add up to 1 for farm ${farm.shortId}`
113
+ );
114
+
115
+ for (const rewardSplit of farm.rewardSplits) {
116
+ const glowBigInt: bigint = BigInt(
117
+ parseUnits(
118
+ customToFixed(farm.weeklyPayment, GLOW_WEIGHT_DECIMAL_PRECISION),
119
+ GLOW_WEIGHT_DECIMAL_PRECISION
120
+ )
121
+ );
122
+ const usdgBigInt: bigint = BigInt(
123
+ parseUnits(
124
+ customToFixed(
125
+ farm.carbonCreditsProduced,
126
+ USDG_WEIGHT_DECIMAL_PRECISION
127
+ ),
128
+ USDG_WEIGHT_DECIMAL_PRECISION
129
+ )
130
+ );
131
+ const glowWeight = multiplyBigIntByDecimalPercentage(
132
+ glowBigInt,
133
+ GLOW_WEIGHT_DECIMAL_PRECISION,
134
+ rewardSplit.glowSplitPercent
135
+ );
136
+ const usdgWeight = multiplyBigIntByDecimalPercentage(
137
+ usdgBigInt,
138
+ USDG_WEIGHT_DECIMAL_PRECISION,
139
+ rewardSplit.usdgSplitPercent
140
+ );
141
+ const value: MerkleLeafIntermediary = {
142
+ wallet: rewardSplit.walletAddress,
143
+ glowWeight,
144
+ usdgWeight,
145
+ };
146
+ //Make sure the usdgWeight and the glowWeight are > 0 if the number is > 0
147
+ if (
148
+ farm.carbonCreditsProduced > 0 &&
149
+ usdgWeight <= 0 &&
150
+ rewardSplit.usdgSplitPercent > 0
151
+ ) {
152
+ console.log(
153
+ farm.carbonCreditsProduced,
154
+ usdgWeight,
155
+ rewardSplit.usdgSplitPercent
156
+ );
157
+ throw new Error(
158
+ "USDG weight is less than 0 and carbon credits produced is greater than 0"
159
+ );
160
+ }
161
+ if (
162
+ farm.weeklyPayment > 0 &&
163
+ glowWeight <= 0 &&
164
+ rewardSplit.glowSplitPercent > 0
165
+ ) {
166
+ console.log(
167
+ farm.weeklyPayment,
168
+ glowWeight,
169
+ rewardSplit.glowSplitPercent
170
+ );
171
+ throw new Error(
172
+ "Glow weight is less than 0 and weekly payment is greater than 0"
173
+ );
174
+ }
175
+
176
+ if (
177
+ farm.carbonCreditsProduced > 0 &&
178
+ usdgWeight <= 0 &&
179
+ rewardSplit.usdgSplitPercent > 0
180
+ ) {
181
+ console.log(
182
+ farm.carbonCreditsProduced,
183
+ usdgWeight.toString(),
184
+ rewardSplit.usdgSplitPercent
185
+ );
186
+ throw new Error(
187
+ `USDG weight is less than or equal to 0 and carbon credits produced is greater than 0 for farm ${farm.shortId}`
188
+ );
189
+ }
190
+ if (
191
+ farm.weeklyPayment > 0 &&
192
+ glowWeight <= 0 &&
193
+ rewardSplit.glowSplitPercent > 0
194
+ ) {
195
+ console.log(
196
+ farm.weeklyPayment,
197
+ glowWeight.toString(),
198
+ rewardSplit.glowSplitPercent
199
+ );
200
+ throw new Error(
201
+ `Glow weight is less than or equal to 0 and weekly payment is greater than 0 for farm ${farm.shortId}`
202
+ );
203
+ }
204
+
205
+ addValueToMapLegacy(rewardSplit.walletAddress, value, mapLegacy);
206
+ }
207
+ }
208
+
209
+ const merkleLeaves = Array.from(mapLegacy.values());
210
+ const finalLeaves: FinalLeaf[] = merkleLeaves.map(
211
+ ({ wallet, glowWeight, usdgWeight }) => ({
212
+ wallet,
213
+ glowWeight: glowWeight.toString(),
214
+ usdgWeight: usdgWeight.toString(),
215
+ })
216
+ );
217
+ const leaves = finalLeaves.map((leaf) =>
218
+ hashLeaf({
219
+ address: leaf.wallet,
220
+ glowWeight: leaf.glowWeight,
221
+ usdcWeight: leaf.usdgWeight,
222
+ })
223
+ );
224
+
225
+ const tree = new MerkleTree(leaves, ethers.utils.keccak256, { sort: true });
226
+ const root = tree.getHexRoot();
227
+
228
+ const totalGlowWeightFinalizedLeavesSum = finalLeaves.reduce(
229
+ (acc, { glowWeight }) => acc.add(glowWeight),
230
+ ethers.BigNumber.from(0)
231
+ );
232
+ const totalGCCWeightFinalizedLeavesSum = finalLeaves.reduce(
233
+ (acc, { usdgWeight }) => acc.add(usdgWeight),
234
+ ethers.BigNumber.from(0)
235
+ );
236
+
237
+ if (totalGlowWeightFinalizedLeavesSum.toBigInt() > MAX_WEIGHT) {
238
+ throw new Error("Total glow weight is greater than max weight");
239
+ }
240
+ if (totalGCCWeightFinalizedLeavesSum.toBigInt() > MAX_WEIGHT) {
241
+ throw new Error("Total gcc weight is greater than max weight");
242
+ }
243
+
244
+ const headlineStats = {
245
+ weekNumber: args.week,
246
+ totalCreditsProduced: formatUnits(totalCreditsProduced, 18),
247
+ totalCreditsProducedBN: totalCreditsProduced.toString(),
248
+ totalGlowWeightInFinalized: totalGlowWeightFinalizedLeavesSum.toString(),
249
+ totalGlowWeightHuman: formatUnits(
250
+ BigInt(totalGlowWeightFinalizedLeavesSum.toString()),
251
+ GLOW_WEIGHT_DECIMAL_PRECISION
252
+ ).toString(),
253
+ totalUSDGWeightInFinalized: totalGCCWeightFinalizedLeavesSum.toString(),
254
+ totalUSDGWeightHuman: formatUnits(
255
+ BigInt(totalGCCWeightFinalizedLeavesSum.toString()),
256
+ USDG_WEIGHT_DECIMAL_PRECISION
257
+ ).toString(),
258
+ root: root,
259
+ glowWeightDecimals: GLOW_WEIGHT_DECIMAL_PRECISION,
260
+ usdgWeightDecimals: USDG_WEIGHT_DECIMAL_PRECISION,
261
+ };
262
+
263
+ const farmsWithMerkleProofs = finalLeaves.map((leaf) => {
264
+ const hashedLeaf = hashLeaf({
265
+ address: leaf.wallet,
266
+ glowWeight: leaf.glowWeight,
267
+ usdcWeight: leaf.usdgWeight,
268
+ });
269
+ const proof = tree.getHexProof(hashedLeaf);
270
+ const isValid = tree.verify(proof, hashedLeaf, root);
271
+ if (!isValid) {
272
+ throw new Error(`Invalid proof for wallet ${leaf.wallet}`);
273
+ }
274
+ return {
275
+ ...leaf,
276
+ proof,
277
+ };
278
+ });
279
+
280
+ let forLoopedGlowWeightSum: ethers.BigNumber = ethers.BigNumber.from(0);
281
+ for (const leafProof of farmsWithMerkleProofs) {
282
+ forLoopedGlowWeightSum = forLoopedGlowWeightSum.add(
283
+ BigNumber.from(leafProof.glowWeight)
284
+ );
285
+ }
286
+
287
+ if (!forLoopedGlowWeightSum.eq(totalGlowWeightFinalizedLeavesSum)) {
288
+ console.error("Glow Weight Sum Mismatch:");
289
+ console.error("Sum from loop:", forLoopedGlowWeightSum.toString());
290
+ console.error(
291
+ "Sum from reduce:",
292
+ totalGlowWeightFinalizedLeavesSum.toString()
293
+ );
294
+ throw new Error("Glow weight sum is not equal");
295
+ }
296
+
297
+ let forLoopedGCCWeightSum: ethers.BigNumber = ethers.BigNumber.from(0);
298
+ for (const leafProof of farmsWithMerkleProofs) {
299
+ forLoopedGCCWeightSum = forLoopedGCCWeightSum.add(
300
+ BigNumber.from(leafProof.usdgWeight)
301
+ );
302
+ }
303
+
304
+ if (!forLoopedGCCWeightSum.eq(totalGCCWeightFinalizedLeavesSum)) {
305
+ console.error("USDG Weight Sum Mismatch:");
306
+ console.error("Sum from loop:", forLoopedGCCWeightSum.toString());
307
+ console.error(
308
+ "Sum from reduce:",
309
+ totalGCCWeightFinalizedLeavesSum.toString()
310
+ );
311
+ throw new Error("GCC weight sum is not equal");
312
+ }
313
+
314
+ {
315
+ const totalCreditsProducedNumber = Number(
316
+ headlineStats.totalCreditsProduced
317
+ );
318
+ const totalUSDGWeightNumber = Number(headlineStats.totalUSDGWeightHuman);
319
+ const maxDeviation = 0.001;
320
+ if (
321
+ greaterThanMaxDeviation(
322
+ totalCreditsProducedNumber,
323
+ totalUSDGWeightNumber,
324
+ maxDeviation
325
+ )
326
+ ) {
327
+ console.error("Deviation Check Failed: Credits Produced vs USDG Weight");
328
+ console.error("Total Credits Produced:", totalCreditsProducedNumber);
329
+ console.error("Total USDG Weight:", totalUSDGWeightNumber);
330
+ console.error("Allowed Deviation:", maxDeviation);
331
+ throw new Error(
332
+ "Total credits produced and total usdg weight deviation > 0.1%"
333
+ );
334
+ }
335
+ }
336
+
337
+ {
338
+ const totalGlowWeightNumber = Number(headlineStats.totalGlowWeightHuman);
339
+ const totalProtocolFeePayments = farms.reduce(
340
+ (acc, farm) => acc + farm.weeklyPayment,
341
+ 0
342
+ );
343
+ const maxDeviation = 0.001;
344
+ if (
345
+ greaterThanMaxDeviation(
346
+ totalGlowWeightNumber,
347
+ totalProtocolFeePayments,
348
+ maxDeviation
349
+ )
350
+ ) {
351
+ console.error("Deviation Check Failed: Glow Weight vs Protocol Fees");
352
+ console.error("Total Glow Weight:", totalGlowWeightNumber);
353
+ console.error("Total Protocol Fees:", totalProtocolFeePayments);
354
+ console.error("Allowed Deviation:", maxDeviation);
355
+ throw new Error(
356
+ "Total glow weight and total protocol fee payments deviation > 0.1%"
357
+ );
358
+ }
359
+ }
360
+
361
+ return {
362
+ headlineStats,
363
+ finalLeaves,
364
+ farmsWithMerkleProofs,
365
+ farms,
366
+ rawData,
367
+ };
368
+ }
369
+
370
+ export async function createWeeklyReport({
371
+ week,
372
+ gcaUrls,
373
+ apiUrl,
374
+ }: CreateWeeklyReportArgs) {
375
+ if (week < 72) {
376
+ return createWeeklyReportLegacy({ week, gcaUrls, apiUrl });
377
+ }
378
+
379
+ const map = new Map<string, MerkleLeaf>();
380
+
381
+ const [apiResponse, auditsRes] = await Promise.all([
382
+ fetchFarmsForWeek(week, gcaUrls, apiUrl),
383
+ axios.get<Audit[]>(
384
+ `${HUB_URL}/api/audits?omitDocuments=true&weekNumber=${week}`
385
+ ),
386
+ ]);
387
+
388
+ const farms = apiResponse.filteredFarms;
389
+ const audits = auditsRes.data;
390
+ const rawData = apiResponse.rawData;
391
+
392
+ const shortIdToAdjustedCredit = new Map<string, bigint>();
393
+
394
+ for (const audit of audits) {
395
+ const activeShortIds = audit.activeShortIds ?? [];
396
+ const adjustedCredit =
397
+ audit.summary?.carbonFootprintAndProduction?.adjustedWeeklyCarbonCredit?.trim();
398
+
399
+ if (!adjustedCredit || !NUMERIC_REGEX.test(adjustedCredit))
400
+ throw new Error(
401
+ `Invalid adjustedWeeklyCarbonCredit for audit ${audit.id}`
402
+ );
403
+ if (activeShortIds.length === 0)
404
+ throw new Error(`audit ${audit.id} has empty activeShortIds`);
405
+
406
+ const splitValue = new Decimal(adjustedCredit).div(activeShortIds.length);
407
+ if (splitValue.isZero())
408
+ throw new Error(
409
+ `Zero adjustedWeeklyCarbonCredit split in audit ${audit.id}`
410
+ );
411
+
412
+ const splitBigInt = toBigInt(splitValue, USDG_WEIGHT_DECIMAL_PRECISION);
413
+ for (const sid of activeShortIds)
414
+ shortIdToAdjustedCredit.set(String(sid), splitBigInt);
415
+ }
416
+
417
+ const farmShortIds = new Set(farms.map((f: any) => String(f.shortId)));
418
+ audits.forEach((audit: any) =>
419
+ audit.activeShortIds?.forEach((sid: string | number) => {
420
+ if (!farmShortIds.has(String(sid)))
421
+ throw new Error(
422
+ `activeShortId ${sid} from audit ${audit.id} missing in filteredFarms`
423
+ );
424
+ })
425
+ );
426
+
427
+ let totalCreditsProduced18dp = BigInt(0);
428
+
429
+ for (const farm of farms) {
430
+ if (farm.status === "Unassigned") {
431
+ throw new Error(`farm ${farm.shortId} is unassigned`);
432
+ }
433
+
434
+ if (!farm.rewardSplits)
435
+ throw new Error(`farm ${farm.shortId} has no rewardSplits`);
436
+
437
+ if (farm.carbonCreditsProduced < 0) {
438
+ console.log(
439
+ `Farm: ${farm.shortId} has ${farm.carbonCreditsProduced} carbon credits produced`
440
+ );
441
+ throw new Error("Carbon credits produced is less than 0");
442
+ }
443
+
444
+ totalCreditsProduced18dp += parseUnits(
445
+ customToFixed(farm.carbonCreditsProduced, 18),
446
+ 18
447
+ );
448
+
449
+ const sumGlow = farm.rewardSplits.reduce(
450
+ (acc: number, r: any) => acc + r.glowSplitPercent,
451
+ 0
452
+ );
453
+ const sumUSDG = farm.rewardSplits.reduce(
454
+ (acc: number, r: any) => acc + r.usdgSplitPercent,
455
+ 0
456
+ );
457
+ const percentTolerance = 1e-12;
458
+ if (Math.abs(sumGlow - 1) > percentTolerance)
459
+ throw new Error(`Glow splits ≠1 for farm ${farm.shortId}`);
460
+ if (Math.abs(sumUSDG - 1) > percentTolerance)
461
+ throw new Error(`USDG splits ≠1 for farm ${farm.shortId}`);
462
+
463
+ const glowBase = parseUnits(
464
+ customToFixed(farm.weeklyPayment, GLOW_WEIGHT_DECIMAL_PRECISION),
465
+ GLOW_WEIGHT_DECIMAL_PRECISION
466
+ );
467
+
468
+ const adjustedCreditBigInt =
469
+ shortIdToAdjustedCredit.get(String(farm.shortId)) ?? BigInt(0);
470
+
471
+ for (const split of farm.rewardSplits) {
472
+ const glowWeight = multiplyBigIntByDecimalPercentage(
473
+ glowBase,
474
+ GLOW_WEIGHT_DECIMAL_PRECISION,
475
+ split.glowSplitPercent
476
+ );
477
+ const usdgWeight = multiplyBigIntByDecimalPercentage(
478
+ adjustedCreditBigInt,
479
+ USDG_WEIGHT_DECIMAL_PRECISION,
480
+ split.usdgSplitPercent
481
+ );
482
+
483
+ if (
484
+ split.usdgSplitPercent > 0 &&
485
+ adjustedCreditBigInt > BigInt(0) &&
486
+ usdgWeight === BigInt(0)
487
+ )
488
+ throw new Error(`USDG dust lost for farm ${farm.shortId}`);
489
+ if (
490
+ split.glowSplitPercent > 0 &&
491
+ glowBase > BigInt(0) &&
492
+ glowWeight === BigInt(0)
493
+ )
494
+ throw new Error(`Glow dust lost for farm ${farm.shortId}`);
495
+
496
+ if (glowWeight > MAX_WEIGHT || usdgWeight > MAX_WEIGHT)
497
+ throw new Error(
498
+ `Leaf weight overflow on wallet ${split.walletAddress}`
499
+ );
500
+
501
+ accumulateLeafWeights(
502
+ split.walletAddress,
503
+ { wallet: split.walletAddress, glowWeight, usdgWeight },
504
+ map
505
+ );
506
+ }
507
+ }
508
+
509
+ const finalizedLeaves: FinalizedLeaf[] = Array.from(map.values()).map(
510
+ ({ wallet, glowWeight, usdgWeight }) => ({
511
+ wallet,
512
+ glowWeight: glowWeight.toString(),
513
+ usdgWeight: usdgWeight.toString(),
514
+ })
515
+ );
516
+
517
+ const hashedLeaves = finalizedLeaves.map((leaf) =>
518
+ hashLeaf({
519
+ address: leaf.wallet,
520
+ glowWeight: leaf.glowWeight,
521
+ usdcWeight: leaf.usdgWeight,
522
+ })
523
+ );
524
+
525
+ const merkleTree = new MerkleTree(hashedLeaves, ethers.utils.keccak256, {
526
+ sort: true,
527
+ });
528
+ const merkleRoot = merkleTree.getHexRoot();
529
+
530
+ const totalGlowWeight = finalizedLeaves.reduce(
531
+ (acc, { glowWeight }) => acc.add(glowWeight),
532
+ ethers.BigNumber.from(0)
533
+ );
534
+ const totalUSDGWeight = finalizedLeaves.reduce(
535
+ (acc, { usdgWeight }) => acc.add(usdgWeight),
536
+ ethers.BigNumber.from(0)
537
+ );
538
+
539
+ if (totalGlowWeight.toBigInt() > MAX_WEIGHT)
540
+ throw new Error("Total glow weight overflow");
541
+ if (totalUSDGWeight.toBigInt() > MAX_WEIGHT)
542
+ throw new Error("Total USDG weight overflow");
543
+
544
+ const leavesWithProofs = finalizedLeaves.map((leaf) => {
545
+ const hashed = hashLeaf({
546
+ address: leaf.wallet,
547
+ glowWeight: leaf.glowWeight,
548
+ usdcWeight: leaf.usdgWeight,
549
+ });
550
+ const proof = merkleTree.getHexProof(hashed);
551
+ if (!merkleTree.verify(proof, hashed, merkleRoot))
552
+ throw new Error("Invalid proof for " + leaf.wallet);
553
+ return { ...leaf, proof };
554
+ });
555
+
556
+ const glowSumProofLoop = leavesWithProofs.reduce(
557
+ (acc, l) => acc.add(l.glowWeight),
558
+ ethers.BigNumber.from(0)
559
+ );
560
+ if (!glowSumProofLoop.eq(totalGlowWeight)) {
561
+ console.error("Glow Weight Sum Mismatch (Post-73):");
562
+ console.error("Sum from loop:", glowSumProofLoop.toString());
563
+ console.error("Sum from reduce:", totalGlowWeight.toString());
564
+ throw new Error("Glow sum mismatch");
565
+ }
566
+
567
+ const usdgSumProofLoop = leavesWithProofs.reduce(
568
+ (acc, l) => acc.add(l.usdgWeight),
569
+ ethers.BigNumber.from(0)
570
+ );
571
+ if (!usdgSumProofLoop.eq(totalUSDGWeight)) {
572
+ console.error("USDG Weight Sum Mismatch (Post-73):");
573
+ console.error("Sum from loop:", usdgSumProofLoop.toString());
574
+ console.error("Sum from reduce:", totalUSDGWeight.toString());
575
+ throw new Error("USDG sum mismatch");
576
+ }
577
+
578
+ const totalExpectedCredits = farms.reduce((acc: Decimal, f: any) => {
579
+ const adj = shortIdToAdjustedCredit.get(String(f.shortId)) ?? BigInt(0);
580
+ return acc.plus(fromBigInt(adj, USDG_WEIGHT_DECIMAL_PRECISION));
581
+ }, new Decimal(0));
582
+
583
+ const totalUSDGWeightHuman = new Decimal(
584
+ formatUnits(
585
+ BigInt(totalUSDGWeight.toString()),
586
+ USDG_WEIGHT_DECIMAL_PRECISION
587
+ )
588
+ );
589
+ if (
590
+ greaterThanMaxDeviation(
591
+ totalExpectedCredits.toNumber(),
592
+ totalUSDGWeightHuman.toNumber(),
593
+ 0.001
594
+ )
595
+ ) {
596
+ console.error(
597
+ "Deviation Check Failed (Post-73): Expected Credits vs USDG Weight"
598
+ );
599
+ console.error("Total Expected Credits:", totalExpectedCredits.toNumber());
600
+ console.error("Total USDG Weight:", totalUSDGWeightHuman.toNumber());
601
+ console.error("Allowed Deviation:", 0.001);
602
+ throw new Error("totalExpectedCredits vs USDG weight deviation >0.1% ");
603
+ }
604
+
605
+ const totalGlowWeightHuman = new Decimal(
606
+ formatUnits(
607
+ BigInt(totalGlowWeight.toString()),
608
+ GLOW_WEIGHT_DECIMAL_PRECISION
609
+ )
610
+ );
611
+ const totalProtocolFeePayments = farms.reduce(
612
+ (acc: number, f: any) => acc + f.weeklyPayment,
613
+ 0
614
+ );
615
+ if (
616
+ greaterThanMaxDeviation(
617
+ totalGlowWeightHuman.toNumber(),
618
+ totalProtocolFeePayments,
619
+ 0.001
620
+ )
621
+ ) {
622
+ console.error(
623
+ "Deviation Check Failed (Post-73): Glow Weight vs Protocol Fees"
624
+ );
625
+ console.error("Total Glow Weight:", totalGlowWeightHuman.toNumber());
626
+ console.error("Total Protocol Fees:", totalProtocolFeePayments);
627
+ console.error("Allowed Deviation:", 0.001);
628
+ throw new Error("totalGlowWeight vs protocol fees deviation >0.1% ");
629
+ }
630
+
631
+ const headlineStats = {
632
+ weekNumber: week,
633
+ totalCreditsProduced: formatUnits(totalCreditsProduced18dp, 18),
634
+ totalCreditsProducedBN: totalCreditsProduced18dp.toString(),
635
+ totalGlowWeightInFinalized: totalGlowWeight.toString(),
636
+ totalGlowWeightHuman: totalGlowWeightHuman.toString(),
637
+ totalUSDGWeightInFinalized: totalUSDGWeight.toString(),
638
+ totalUSDGWeightHuman: totalUSDGWeightHuman.toString(),
639
+ root: merkleRoot,
640
+ glowWeightDecimals: GLOW_WEIGHT_DECIMAL_PRECISION,
641
+ usdgWeightDecimals: USDG_WEIGHT_DECIMAL_PRECISION,
642
+ };
643
+
644
+ const shortIdAdjustedList = Array.from(shortIdToAdjustedCredit.entries()).map(
645
+ ([shortId, creditBigInt]) => ({
646
+ shortId,
647
+ adjustedCredit: fromBigInt(
648
+ creditBigInt,
649
+ USDG_WEIGHT_DECIMAL_PRECISION
650
+ ).toString(),
651
+ })
652
+ );
653
+
654
+ const creditsDeviationList = farms.map((farm: any) => {
655
+ const adj = shortIdToAdjustedCredit.get(String(farm.shortId)) ?? BigInt(0);
656
+ const adjusted = fromBigInt(adj, USDG_WEIGHT_DECIMAL_PRECISION);
657
+ const produced = new Decimal(farm.carbonCreditsProduced);
658
+ let deviation: Decimal;
659
+ let absDeviation: Decimal;
660
+ let deviationPercent: Decimal;
661
+ if (produced.isZero() && adjusted.isZero()) {
662
+ deviation = new Decimal(0);
663
+ absDeviation = new Decimal(0);
664
+ deviationPercent = new Decimal(0);
665
+ } else {
666
+ deviation = produced.minus(adjusted);
667
+ absDeviation = deviation.abs();
668
+ deviationPercent = deviation
669
+ .div(produced.isZero() ? 1 : produced)
670
+ .mul(100);
671
+ }
672
+ return {
673
+ shortId: String(farm.shortId),
674
+ carbonCreditsProduced: produced.toNumber(),
675
+ adjustedWeeklyCarbonCredit: adjusted.toNumber(),
676
+ deviation: deviation.toNumber(),
677
+ absDeviation: absDeviation.toNumber(),
678
+ deviationPercent: deviationPercent.toNumber(),
679
+ };
680
+ });
681
+
682
+ return {
683
+ headlineStats,
684
+ finalizedLeaves,
685
+ leavesWithProofs,
686
+ farms,
687
+ rawData,
688
+ shortIdAdjustedList,
689
+ creditsDeviationList,
690
+ };
691
+ }