@namehash/ens-referrals 1.0.0 → 1.1.0
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/index.cjs +492 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +437 -7
- package/dist/index.d.ts +437 -7
- package/dist/index.js +485 -10
- package/dist/index.js.map +1 -1
- package/package.json +10 -5
package/dist/index.js
CHANGED
|
@@ -1,16 +1,129 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
|
|
3
|
-
var
|
|
1
|
+
// src/address.ts
|
|
2
|
+
import { isAddress } from "viem";
|
|
3
|
+
var validateLowercaseAddress = (address) => {
|
|
4
|
+
if (!isAddress(address, { strict: false })) {
|
|
5
|
+
throw new Error(`Invalid address: ${address}. Address must be a valid EVM address.`);
|
|
6
|
+
}
|
|
7
|
+
if (address !== address.toLowerCase()) {
|
|
8
|
+
throw new Error(`Invalid address: ${address}. Address must be in lowercase format.`);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var normalizeAddress = (address) => {
|
|
12
|
+
return address.toLowerCase();
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/number.ts
|
|
16
|
+
var isInteger = (value) => {
|
|
17
|
+
return Number.isInteger(value);
|
|
18
|
+
};
|
|
19
|
+
var isNonNegativeInteger = (value) => {
|
|
20
|
+
return value >= 0 && Number.isInteger(value);
|
|
21
|
+
};
|
|
22
|
+
var isPositiveInteger = (value) => {
|
|
23
|
+
return value >= 1 && Number.isInteger(value);
|
|
24
|
+
};
|
|
25
|
+
var validateNonNegativeInteger = (value) => {
|
|
26
|
+
if (!isNonNegativeInteger(value)) {
|
|
27
|
+
throw new Error(`Invalid non-negative integer: ${value}.`);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var isFiniteNonNegativeNumber = (value) => {
|
|
31
|
+
return value >= 0 && Number.isFinite(value);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
// src/time.ts
|
|
35
|
+
var validateUnixTimestamp = (timestamp) => {
|
|
36
|
+
if (!isInteger(timestamp)) {
|
|
37
|
+
throw new Error(`Invalid Unix timestamp: ${timestamp}. Unix timestamp must be an integer.`);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
var SECONDS_PER_YEAR = 31556952;
|
|
41
|
+
function isValidDuration(duration) {
|
|
42
|
+
return isNonNegativeInteger(duration);
|
|
43
|
+
}
|
|
44
|
+
function validateDuration(duration) {
|
|
45
|
+
if (!isValidDuration(duration)) {
|
|
46
|
+
throw new Error(`Invalid duration: ${duration}. Duration must be a non-negative integer.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/score.ts
|
|
51
|
+
var isValidReferrerScore = (score) => {
|
|
52
|
+
return score >= 0 && Number.isFinite(score);
|
|
53
|
+
};
|
|
54
|
+
var validateReferrerScore = (score) => {
|
|
55
|
+
if (!isValidReferrerScore(score)) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
`Invalid referrer score: ${score}. Referrer score must be a finite non-negative number.`
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
var calcReferrerScore = (totalIncrementalDuration) => {
|
|
62
|
+
return totalIncrementalDuration / SECONDS_PER_YEAR;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/aggregations.ts
|
|
66
|
+
var validateAggregatedReferrerMetrics = (metrics) => {
|
|
67
|
+
validateNonNegativeInteger(metrics.grandTotalReferrals);
|
|
68
|
+
validateDuration(metrics.grandTotalIncrementalDuration);
|
|
69
|
+
validateReferrerScore(metrics.grandTotalQualifiedReferrersFinalScore);
|
|
70
|
+
validateReferrerScore(metrics.minFinalScoreToQualify);
|
|
71
|
+
};
|
|
72
|
+
var buildAggregatedReferrerMetrics = (referrers, rules) => {
|
|
73
|
+
let grandTotalReferrals = 0;
|
|
74
|
+
let grandTotalIncrementalDuration = 0;
|
|
75
|
+
let grandTotalQualifiedReferrersFinalScore = 0;
|
|
76
|
+
let minFinalScoreToQualify = Number.MAX_SAFE_INTEGER;
|
|
77
|
+
for (const referrer of referrers) {
|
|
78
|
+
grandTotalReferrals += referrer.totalReferrals;
|
|
79
|
+
grandTotalIncrementalDuration += referrer.totalIncrementalDuration;
|
|
80
|
+
if (referrer.isQualified) {
|
|
81
|
+
grandTotalQualifiedReferrersFinalScore += referrer.finalScore;
|
|
82
|
+
if (referrer.finalScore < minFinalScoreToQualify) {
|
|
83
|
+
minFinalScoreToQualify = referrer.finalScore;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (minFinalScoreToQualify === Number.MAX_SAFE_INTEGER) {
|
|
88
|
+
if (rules.maxQualifiedReferrers === 0) {
|
|
89
|
+
} else {
|
|
90
|
+
if (referrers.length !== 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"AggregatedReferrerMetrics: There are referrers on the leaderboard, and the rules allow for qualified referrers, but no qualified referrers."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
minFinalScoreToQualify = 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const result = {
|
|
99
|
+
grandTotalReferrals,
|
|
100
|
+
grandTotalIncrementalDuration,
|
|
101
|
+
grandTotalQualifiedReferrersFinalScore,
|
|
102
|
+
minFinalScoreToQualify
|
|
103
|
+
};
|
|
104
|
+
validateAggregatedReferrerMetrics(result);
|
|
105
|
+
return result;
|
|
106
|
+
};
|
|
4
107
|
|
|
5
|
-
// src/
|
|
108
|
+
// src/currency.ts
|
|
109
|
+
function isValidUSDQuantity(value) {
|
|
110
|
+
return isFiniteNonNegativeNumber(value);
|
|
111
|
+
}
|
|
112
|
+
function validateUSDQuantity(value) {
|
|
113
|
+
if (!isValidUSDQuantity(value)) {
|
|
114
|
+
throw new Error(`Invalid USD quantity: ${value}.`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/encoding.ts
|
|
6
119
|
import { getAddress, pad, size, slice, zeroAddress } from "viem";
|
|
7
120
|
var ENCODED_REFERRER_BYTE_OFFSET = 12;
|
|
8
121
|
var ENCODED_REFERRER_BYTE_LENGTH = 32;
|
|
9
|
-
var
|
|
122
|
+
var EXPECTED_ENCODED_REFERRER_PADDING = pad("0x", {
|
|
10
123
|
size: ENCODED_REFERRER_BYTE_OFFSET,
|
|
11
124
|
dir: "left"
|
|
12
125
|
});
|
|
13
|
-
var
|
|
126
|
+
var ZERO_ENCODED_REFERRER = pad("0x", {
|
|
14
127
|
size: ENCODED_REFERRER_BYTE_LENGTH,
|
|
15
128
|
dir: "left"
|
|
16
129
|
});
|
|
@@ -25,7 +138,7 @@ function decodeEncodedReferrer(encodedReferrer) {
|
|
|
25
138
|
);
|
|
26
139
|
}
|
|
27
140
|
const padding = slice(encodedReferrer, 0, ENCODED_REFERRER_BYTE_OFFSET);
|
|
28
|
-
if (padding !==
|
|
141
|
+
if (padding !== EXPECTED_ENCODED_REFERRER_PADDING) {
|
|
29
142
|
return zeroAddress;
|
|
30
143
|
}
|
|
31
144
|
const decodedReferrer = slice(encodedReferrer, ENCODED_REFERRER_BYTE_OFFSET);
|
|
@@ -35,20 +148,382 @@ function decodeEncodedReferrer(encodedReferrer) {
|
|
|
35
148
|
throw new Error(`Decoded referrer value must be a valid EVM address.`);
|
|
36
149
|
}
|
|
37
150
|
}
|
|
151
|
+
|
|
152
|
+
// src/rank.ts
|
|
153
|
+
var validateReferrerRank = (rank) => {
|
|
154
|
+
if (!isPositiveInteger(rank)) {
|
|
155
|
+
throw new Error(`Invalid ReferrerRank: ${rank}. ReferrerRank must be a positive integer.`);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
function isReferrerQualified(rank, rules) {
|
|
159
|
+
return rank <= rules.maxQualifiedReferrers;
|
|
160
|
+
}
|
|
161
|
+
function calcReferrerFinalScoreBoost(rank, rules) {
|
|
162
|
+
if (!isReferrerQualified(rank, rules)) return 0;
|
|
163
|
+
return 1 - (rank - 1) / (rules.maxQualifiedReferrers - 1);
|
|
164
|
+
}
|
|
165
|
+
function calcReferrerFinalScoreMultiplier(rank, rules) {
|
|
166
|
+
return 1 + calcReferrerFinalScoreBoost(rank, rules);
|
|
167
|
+
}
|
|
168
|
+
function calcReferrerFinalScore(rank, totalIncrementalDuration, rules) {
|
|
169
|
+
return calcReferrerScore(totalIncrementalDuration) * calcReferrerFinalScoreMultiplier(rank, rules);
|
|
170
|
+
}
|
|
171
|
+
var compareReferrerMetrics = (a, b) => {
|
|
172
|
+
if (a.totalIncrementalDuration !== b.totalIncrementalDuration) {
|
|
173
|
+
return b.totalIncrementalDuration - a.totalIncrementalDuration;
|
|
174
|
+
}
|
|
175
|
+
if (b.referrer > a.referrer) return 1;
|
|
176
|
+
if (b.referrer < a.referrer) return -1;
|
|
177
|
+
return 0;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// src/referrer-metrics.ts
|
|
181
|
+
var buildReferrerMetrics = (referrer, totalReferrals, totalIncrementalDuration) => {
|
|
182
|
+
const result = {
|
|
183
|
+
referrer: normalizeAddress(referrer),
|
|
184
|
+
totalReferrals,
|
|
185
|
+
totalIncrementalDuration
|
|
186
|
+
};
|
|
187
|
+
validateReferrerMetrics(result);
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
var validateReferrerMetrics = (metrics) => {
|
|
191
|
+
validateLowercaseAddress(metrics.referrer);
|
|
192
|
+
validateNonNegativeInteger(metrics.totalReferrals);
|
|
193
|
+
validateDuration(metrics.totalIncrementalDuration);
|
|
194
|
+
};
|
|
195
|
+
var sortReferrerMetrics = (referrers) => {
|
|
196
|
+
return [...referrers].sort(compareReferrerMetrics);
|
|
197
|
+
};
|
|
198
|
+
var buildScoredReferrerMetrics = (referrer) => {
|
|
199
|
+
const result = {
|
|
200
|
+
...referrer,
|
|
201
|
+
score: calcReferrerScore(referrer.totalIncrementalDuration)
|
|
202
|
+
};
|
|
203
|
+
validateScoredReferrerMetrics(result);
|
|
204
|
+
return result;
|
|
205
|
+
};
|
|
206
|
+
var validateScoredReferrerMetrics = (metrics) => {
|
|
207
|
+
validateReferrerMetrics(metrics);
|
|
208
|
+
validateReferrerScore(metrics.score);
|
|
209
|
+
const expectedScore = calcReferrerScore(metrics.totalIncrementalDuration);
|
|
210
|
+
if (metrics.score !== expectedScore) {
|
|
211
|
+
throw new Error(`Referrer: Invalid score: ${metrics.score}, expected: ${expectedScore}.`);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var validateRankedReferrerMetrics = (metrics, rules) => {
|
|
215
|
+
validateScoredReferrerMetrics(metrics);
|
|
216
|
+
validateReferrerRank(metrics.rank);
|
|
217
|
+
if (metrics.finalScoreBoost < 0 || metrics.finalScoreBoost > 1) {
|
|
218
|
+
throw new Error(
|
|
219
|
+
`Invalid RankedReferrerMetrics: Invalid finalScoreBoost: ${metrics.finalScoreBoost}. finalScoreBoost must be between 0 and 1 (inclusive).`
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
validateReferrerScore(metrics.finalScore);
|
|
223
|
+
const expectedIsQualified = isReferrerQualified(metrics.rank, rules);
|
|
224
|
+
if (metrics.isQualified !== expectedIsQualified) {
|
|
225
|
+
throw new Error(
|
|
226
|
+
`RankedReferrerMetrics: Invalid isQualified: ${metrics.isQualified}, expected: ${expectedIsQualified}.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
const expectedFinalScoreBoost = calcReferrerFinalScoreBoost(metrics.rank, rules);
|
|
230
|
+
if (metrics.finalScoreBoost !== expectedFinalScoreBoost) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`RankedReferrerMetrics: Invalid finalScoreBoost: ${metrics.finalScoreBoost}, expected: ${expectedFinalScoreBoost}.`
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
const expectedFinalScore = calcReferrerFinalScore(
|
|
236
|
+
metrics.rank,
|
|
237
|
+
metrics.totalIncrementalDuration,
|
|
238
|
+
rules
|
|
239
|
+
);
|
|
240
|
+
if (metrics.finalScore !== expectedFinalScore) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`RankedReferrerMetrics: Invalid finalScore: ${metrics.finalScore}, expected: ${expectedFinalScore}.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
var buildRankedReferrerMetrics = (referrer, rank, rules) => {
|
|
247
|
+
const result = {
|
|
248
|
+
...referrer,
|
|
249
|
+
rank,
|
|
250
|
+
isQualified: isReferrerQualified(rank, rules),
|
|
251
|
+
finalScoreBoost: calcReferrerFinalScoreBoost(rank, rules),
|
|
252
|
+
finalScore: calcReferrerFinalScore(rank, referrer.totalIncrementalDuration, rules)
|
|
253
|
+
};
|
|
254
|
+
validateRankedReferrerMetrics(result, rules);
|
|
255
|
+
return result;
|
|
256
|
+
};
|
|
257
|
+
var calcReferrerAwardPoolShare = (referrer, aggregatedMetrics, rules) => {
|
|
258
|
+
if (!isReferrerQualified(referrer.rank, rules)) return 0;
|
|
259
|
+
if (aggregatedMetrics.grandTotalQualifiedReferrersFinalScore === 0) return 0;
|
|
260
|
+
return calcReferrerFinalScore(referrer.rank, referrer.totalIncrementalDuration, rules) / aggregatedMetrics.grandTotalQualifiedReferrersFinalScore;
|
|
261
|
+
};
|
|
262
|
+
var validateAwardedReferrerMetrics = (referrer, rules) => {
|
|
263
|
+
validateRankedReferrerMetrics(referrer, rules);
|
|
264
|
+
if (referrer.awardPoolShare < 0 || referrer.awardPoolShare > 1) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`Invalid AwardedReferrerMetrics: ${referrer.awardPoolShare}. awardPoolShare must be between 0 and 1 (inclusive).`
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (referrer.awardPoolApproxValue < 0 || referrer.awardPoolApproxValue > rules.totalAwardPoolValue) {
|
|
270
|
+
throw new Error(
|
|
271
|
+
`Invalid AwardedReferrerMetrics: ${referrer.awardPoolApproxValue}. awardPoolApproxValue must be between 0 and ${rules.totalAwardPoolValue} (inclusive).`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var buildAwardedReferrerMetrics = (referrer, aggregatedMetrics, rules) => {
|
|
276
|
+
const awardPoolShare = calcReferrerAwardPoolShare(referrer, aggregatedMetrics, rules);
|
|
277
|
+
const result = {
|
|
278
|
+
...referrer,
|
|
279
|
+
awardPoolShare,
|
|
280
|
+
awardPoolApproxValue: awardPoolShare * rules.totalAwardPoolValue
|
|
281
|
+
};
|
|
282
|
+
validateAwardedReferrerMetrics(result, rules);
|
|
283
|
+
return result;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
// src/leaderboard.ts
|
|
287
|
+
var buildReferrerLeaderboard = (allReferrers, rules, accurateAsOf) => {
|
|
288
|
+
const uniqueReferrers = allReferrers.map((referrer) => referrer.referrer);
|
|
289
|
+
if (uniqueReferrers.length !== allReferrers.length) {
|
|
290
|
+
throw new Error(
|
|
291
|
+
"ReferrerLeaderboard: Cannot buildReferrerLeaderboard containing duplicate referrers"
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
if (accurateAsOf < rules.startTime && allReferrers.length > 0) {
|
|
295
|
+
throw new Error(
|
|
296
|
+
`ReferrerLeaderboard: accurateAsOf (${accurateAsOf}) is before startTime (${rules.startTime}) which indicates allReferrers should be empty, but allReferrers is not empty.`
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
const sortedReferrers = sortReferrerMetrics(allReferrers);
|
|
300
|
+
const scoredReferrers = sortedReferrers.map((referrer) => buildScoredReferrerMetrics(referrer));
|
|
301
|
+
const rankedReferrers = scoredReferrers.map((referrer, index) => {
|
|
302
|
+
return buildRankedReferrerMetrics(referrer, index + 1, rules);
|
|
303
|
+
});
|
|
304
|
+
const aggregatedMetrics = buildAggregatedReferrerMetrics(rankedReferrers, rules);
|
|
305
|
+
const awardedReferrers = rankedReferrers.map((referrer) => {
|
|
306
|
+
return buildAwardedReferrerMetrics(referrer, aggregatedMetrics, rules);
|
|
307
|
+
});
|
|
308
|
+
const referrers = new Map(
|
|
309
|
+
awardedReferrers.map((referrer) => {
|
|
310
|
+
return [referrer.referrer, referrer];
|
|
311
|
+
})
|
|
312
|
+
);
|
|
313
|
+
return {
|
|
314
|
+
rules,
|
|
315
|
+
aggregatedMetrics,
|
|
316
|
+
referrers,
|
|
317
|
+
accurateAsOf
|
|
318
|
+
};
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// src/leaderboard-page.ts
|
|
322
|
+
var REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT = 25;
|
|
323
|
+
var REFERRERS_PER_LEADERBOARD_PAGE_MAX = 100;
|
|
324
|
+
var validateReferrerLeaderboardPaginationParams = (params) => {
|
|
325
|
+
if (params.page !== void 0 && !isPositiveInteger(params.page)) {
|
|
326
|
+
throw new Error(
|
|
327
|
+
`Invalid ReferrerLeaderboardPaginationParams: ${params.page}. page must be a positive integer.`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
if (params.itemsPerPage !== void 0 && !isPositiveInteger(params.itemsPerPage)) {
|
|
331
|
+
throw new Error(
|
|
332
|
+
`Invalid ReferrerLeaderboardPaginationParams: ${params.itemsPerPage}. itemsPerPage must be a positive integer.`
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
if (params.itemsPerPage !== void 0 && params.itemsPerPage > REFERRERS_PER_LEADERBOARD_PAGE_MAX) {
|
|
336
|
+
throw new Error(
|
|
337
|
+
`Invalid ReferrerLeaderboardPaginationParams: ${params.itemsPerPage}. itemsPerPage must be less than or equal to ${REFERRERS_PER_LEADERBOARD_PAGE_MAX}.`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
var buildReferrerLeaderboardPaginationParams = (params) => {
|
|
342
|
+
const result = {
|
|
343
|
+
page: params.page ?? 1,
|
|
344
|
+
itemsPerPage: params.itemsPerPage ?? REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT
|
|
345
|
+
};
|
|
346
|
+
validateReferrerLeaderboardPaginationParams(result);
|
|
347
|
+
return result;
|
|
348
|
+
};
|
|
349
|
+
var validateReferrerLeaderboardPaginationContext = (context) => {
|
|
350
|
+
validateReferrerLeaderboardPaginationParams(context);
|
|
351
|
+
if (!isNonNegativeInteger(context.totalRecords)) {
|
|
352
|
+
throw new Error(
|
|
353
|
+
`Invalid ReferrerLeaderboardPaginationContext: total must be a non-negative integer but is ${context.totalRecords}.`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const startIndex = (context.page - 1) * context.itemsPerPage;
|
|
357
|
+
const endIndex = startIndex + context.itemsPerPage;
|
|
358
|
+
if (!context.hasNext && endIndex < context.totalRecords) {
|
|
359
|
+
throw new Error(
|
|
360
|
+
`Invalid ReferrerLeaderboardPaginationContext: if hasNext is false, endIndex (${endIndex}) must be greater than or equal to total (${context.totalRecords}).`
|
|
361
|
+
);
|
|
362
|
+
} else if (context.hasNext && context.page * context.itemsPerPage >= context.totalRecords) {
|
|
363
|
+
throw new Error(
|
|
364
|
+
`Invalid ReferrerLeaderboardPaginationContext: if hasNext is true, endIndex (${endIndex}) must be less than total (${context.totalRecords}).`
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
if (!context.hasPrev && context.page !== 1) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
`Invalid ReferrerLeaderboardPaginationContext: if hasPrev is false, page must be the first page (1) but is ${context.page}.`
|
|
370
|
+
);
|
|
371
|
+
} else if (context.hasPrev && context.page === 1) {
|
|
372
|
+
throw new Error(
|
|
373
|
+
`Invalid ReferrerLeaderboardPaginationContext: if hasPrev is true, page must not be the first page (1) but is ${context.page}.`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
var buildReferrerLeaderboardPaginationContext = (optionalParams, leaderboard) => {
|
|
378
|
+
const materializedParams = buildReferrerLeaderboardPaginationParams(optionalParams);
|
|
379
|
+
const totalRecords = leaderboard.referrers.size;
|
|
380
|
+
const totalPages = Math.max(1, Math.ceil(totalRecords / materializedParams.itemsPerPage));
|
|
381
|
+
if (materializedParams.page > totalPages) {
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Invalid ReferrerLeaderboardPaginationContext: page ${materializedParams.page} exceeds total pages ${totalPages}.`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
if (totalRecords === 0) {
|
|
387
|
+
return {
|
|
388
|
+
...materializedParams,
|
|
389
|
+
totalRecords: 0,
|
|
390
|
+
totalPages: 1,
|
|
391
|
+
hasNext: false,
|
|
392
|
+
hasPrev: false,
|
|
393
|
+
startIndex: void 0,
|
|
394
|
+
endIndex: void 0
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
const startIndex = (materializedParams.page - 1) * materializedParams.itemsPerPage;
|
|
398
|
+
const maxTheoreticalIndexOnPage = startIndex + (materializedParams.itemsPerPage - 1);
|
|
399
|
+
const endIndex = Math.min(maxTheoreticalIndexOnPage, totalRecords - 1);
|
|
400
|
+
const hasNext = maxTheoreticalIndexOnPage < totalRecords;
|
|
401
|
+
const hasPrev = materializedParams.page > 1;
|
|
402
|
+
const result = {
|
|
403
|
+
...materializedParams,
|
|
404
|
+
totalRecords,
|
|
405
|
+
totalPages,
|
|
406
|
+
hasNext,
|
|
407
|
+
hasPrev,
|
|
408
|
+
startIndex,
|
|
409
|
+
endIndex
|
|
410
|
+
};
|
|
411
|
+
validateReferrerLeaderboardPaginationContext(result);
|
|
412
|
+
return result;
|
|
413
|
+
};
|
|
414
|
+
var getReferrerLeaderboardPage = (paginationParams, leaderboard) => {
|
|
415
|
+
const paginationContext = buildReferrerLeaderboardPaginationContext(
|
|
416
|
+
paginationParams,
|
|
417
|
+
leaderboard
|
|
418
|
+
);
|
|
419
|
+
let referrers;
|
|
420
|
+
if (paginationContext.totalRecords > 0 && typeof paginationContext.startIndex !== "undefined" && typeof paginationContext.endIndex !== "undefined") {
|
|
421
|
+
referrers = Array.from(leaderboard.referrers.values()).slice(
|
|
422
|
+
paginationContext.startIndex,
|
|
423
|
+
paginationContext.endIndex + 1
|
|
424
|
+
// For `slice`, this is exclusive of the element at the index 'end'. We need it to be inclusive, hence plus one.
|
|
425
|
+
);
|
|
426
|
+
} else {
|
|
427
|
+
referrers = [];
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
rules: leaderboard.rules,
|
|
431
|
+
referrers,
|
|
432
|
+
aggregatedMetrics: leaderboard.aggregatedMetrics,
|
|
433
|
+
paginationContext,
|
|
434
|
+
accurateAsOf: leaderboard.accurateAsOf
|
|
435
|
+
};
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// src/link.ts
|
|
439
|
+
import { getAddress as getAddress2 } from "viem";
|
|
38
440
|
function buildEnsReferralUrl(address) {
|
|
39
441
|
const ensAppUrl = new URL("https://app.ens.domains");
|
|
40
|
-
ensAppUrl.searchParams.set("referrer",
|
|
442
|
+
ensAppUrl.searchParams.set("referrer", getAddress2(address));
|
|
41
443
|
return ensAppUrl;
|
|
42
444
|
}
|
|
445
|
+
|
|
446
|
+
// src/rules.ts
|
|
447
|
+
var ENS_HOLIDAY_AWARDS_START_DATE = 1764547200;
|
|
448
|
+
var ENS_HOLIDAY_AWARDS_END_DATE = 1767225599;
|
|
449
|
+
var ENS_HOLIDAY_AWARDS_MAX_QUALIFIED_REFERRERS = 10;
|
|
450
|
+
var ENS_HOLIDAY_AWARDS_TOTAL_AWARD_POOL_VALUE = 1e4;
|
|
451
|
+
var validateReferralProgramRules = (rules) => {
|
|
452
|
+
validateUSDQuantity(rules.totalAwardPoolValue);
|
|
453
|
+
validateNonNegativeInteger(rules.maxQualifiedReferrers);
|
|
454
|
+
validateUnixTimestamp(rules.startTime);
|
|
455
|
+
validateUnixTimestamp(rules.endTime);
|
|
456
|
+
if (rules.endTime < rules.startTime) {
|
|
457
|
+
throw new Error(
|
|
458
|
+
`ReferralProgramRules: startTime: ${rules.startTime} is after endTime: ${rules.endTime}.`
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
};
|
|
462
|
+
var buildReferralProgramRules = (totalAwardPoolValue, maxQualifiedReferrers, startTime, endTime, subregistryId) => {
|
|
463
|
+
const result = {
|
|
464
|
+
totalAwardPoolValue,
|
|
465
|
+
maxQualifiedReferrers,
|
|
466
|
+
startTime,
|
|
467
|
+
endTime,
|
|
468
|
+
subregistryId
|
|
469
|
+
};
|
|
470
|
+
validateReferralProgramRules(result);
|
|
471
|
+
return result;
|
|
472
|
+
};
|
|
43
473
|
export {
|
|
44
474
|
ENCODED_REFERRER_BYTE_LENGTH,
|
|
45
475
|
ENCODED_REFERRER_BYTE_OFFSET,
|
|
46
476
|
ENS_HOLIDAY_AWARDS_END_DATE,
|
|
477
|
+
ENS_HOLIDAY_AWARDS_MAX_QUALIFIED_REFERRERS,
|
|
47
478
|
ENS_HOLIDAY_AWARDS_START_DATE,
|
|
479
|
+
ENS_HOLIDAY_AWARDS_TOTAL_AWARD_POOL_VALUE,
|
|
480
|
+
EXPECTED_ENCODED_REFERRER_PADDING,
|
|
481
|
+
REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT,
|
|
482
|
+
REFERRERS_PER_LEADERBOARD_PAGE_MAX,
|
|
483
|
+
SECONDS_PER_YEAR,
|
|
484
|
+
ZERO_ENCODED_REFERRER,
|
|
485
|
+
buildAggregatedReferrerMetrics,
|
|
486
|
+
buildAwardedReferrerMetrics,
|
|
48
487
|
buildEncodedReferrer,
|
|
49
488
|
buildEnsReferralUrl,
|
|
489
|
+
buildRankedReferrerMetrics,
|
|
490
|
+
buildReferralProgramRules,
|
|
491
|
+
buildReferrerLeaderboard,
|
|
492
|
+
buildReferrerLeaderboardPaginationContext,
|
|
493
|
+
buildReferrerLeaderboardPaginationParams,
|
|
494
|
+
buildReferrerMetrics,
|
|
495
|
+
buildScoredReferrerMetrics,
|
|
496
|
+
calcReferrerAwardPoolShare,
|
|
497
|
+
calcReferrerFinalScore,
|
|
498
|
+
calcReferrerFinalScoreBoost,
|
|
499
|
+
calcReferrerFinalScoreMultiplier,
|
|
500
|
+
calcReferrerScore,
|
|
501
|
+
compareReferrerMetrics,
|
|
50
502
|
decodeEncodedReferrer,
|
|
51
|
-
|
|
52
|
-
|
|
503
|
+
getReferrerLeaderboardPage,
|
|
504
|
+
isFiniteNonNegativeNumber,
|
|
505
|
+
isInteger,
|
|
506
|
+
isNonNegativeInteger,
|
|
507
|
+
isPositiveInteger,
|
|
508
|
+
isReferrerQualified,
|
|
509
|
+
isValidDuration,
|
|
510
|
+
isValidReferrerScore,
|
|
511
|
+
isValidUSDQuantity,
|
|
512
|
+
normalizeAddress,
|
|
513
|
+
sortReferrerMetrics,
|
|
514
|
+
validateAggregatedReferrerMetrics,
|
|
515
|
+
validateAwardedReferrerMetrics,
|
|
516
|
+
validateDuration,
|
|
517
|
+
validateLowercaseAddress,
|
|
518
|
+
validateNonNegativeInteger,
|
|
519
|
+
validateRankedReferrerMetrics,
|
|
520
|
+
validateReferralProgramRules,
|
|
521
|
+
validateReferrerLeaderboardPaginationContext,
|
|
522
|
+
validateReferrerMetrics,
|
|
523
|
+
validateReferrerRank,
|
|
524
|
+
validateReferrerScore,
|
|
525
|
+
validateScoredReferrerMetrics,
|
|
526
|
+
validateUSDQuantity,
|
|
527
|
+
validateUnixTimestamp
|
|
53
528
|
};
|
|
54
529
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/referrer.ts"],"sourcesContent":["/**\n * Unix timestamp value\n *\n * Represents the number of seconds that have elapsed\n * since January 1, 1970 (midnight UTC/GMT).\n *\n * Guaranteed to be an integer. May be zero or negative to represent a time at or\n * before Jan 1, 1970.\n */\nexport type UnixTimestamp = number;\n\n/**\n * Start date for the ENS Holiday Awards referral program.\n * 2025-12-01T00:00:00Z (December 1, 2025 at 00:00:00 UTC)\n */\nexport const ENS_HOLIDAY_AWARDS_START_DATE: UnixTimestamp = 1764547200;\n\n/**\n * End date for the ENS Holiday Awards referral program.\n * 2025-12-31T23:59:59Z (December 31, 2025 at 23:59:59 UTC)\n */\nexport const ENS_HOLIDAY_AWARDS_END_DATE: UnixTimestamp = 1767225599;\n","import { type Address, getAddress, type Hex, pad, size, slice, zeroAddress } from \"viem\";\n\n/**\n * Encoded Referrer\n *\n * Represents a \"raw\" ENS referrer value.\n *\n * Guaranteed to be a hex string representation of a 32-byte value.\n * For ENS Holiday Awards a correctly encoded referrer is\n * a left-padded lowercase EVM address.\n */\nexport type EncodedReferrer = Hex;\n\n/**\n * Encoded Referrer byte offset for ENS Holiday Awards.\n *\n * The count of left-padded bytes in an {@link EncodedReferrer} value for ENS Holiday Awards.\n */\nexport const ENCODED_REFERRER_BYTE_OFFSET = 12;\n\n/**\n * Encoded Referrer byte length\n *\n * The count of bytes the {@link EncodedReferrer} value consists of.\n */\nexport const ENCODED_REFERRER_BYTE_LENGTH = 32;\n\n/**\n * Encoded Referrer Padding for ENS Holiday Awards\n *\n * The initial bytes of correctly encoded referrer value for ENS Holiday Awards.\n */\nexport const encodedReferrerPadding = pad(\"0x\", {\n size: ENCODED_REFERRER_BYTE_OFFSET,\n dir: \"left\",\n});\n\n/**\n * Zero Encoded Referrer\n *\n * Guaranteed to be a hex string representation of a 32-byte zero value.\n */\nexport const zeroEncodedReferrer = pad(\"0x\", {\n size: ENCODED_REFERRER_BYTE_LENGTH,\n dir: \"left\",\n});\n\n/**\n * Build an {@link EncodedReferrer} value for the given {@link Address}\n * according to the subjective referrer encoding used for ENS Holiday Awards.\n */\nexport function buildEncodedReferrer(address: Address): EncodedReferrer {\n const lowercaseAddress = address.toLowerCase() as Address;\n\n return pad(lowercaseAddress, { size: ENCODED_REFERRER_BYTE_LENGTH, dir: \"left\" });\n}\n\n/**\n * Decode an {@link EncodedReferrer} value into a checksummed {@link Address}\n * according to the subjective referrer encoding used for ENS Holiday Awards.\n *\n * @param encodedReferrer - The \"raw\" {@link EncodedReferrer} value to decode.\n * @returns The decoded referrer checksummed address.\n * @throws when encodedReferrer value is not represented by\n * {@link ENCODED_REFERRER_BYTE_LENGTH} bytes.\n * @throws when decodedReferrer is not a valid EVM address.\n */\nexport function decodeEncodedReferrer(encodedReferrer: EncodedReferrer): Address {\n // Invariant: encoded referrer must be of expected size\n if (size(encodedReferrer) !== ENCODED_REFERRER_BYTE_LENGTH) {\n throw new Error(\n `Encoded referrer value must be represented by ${ENCODED_REFERRER_BYTE_LENGTH} bytes.`,\n );\n }\n\n const padding = slice(encodedReferrer, 0, ENCODED_REFERRER_BYTE_OFFSET);\n\n // return zero address if the padding of encoded referrer is not correct\n // for ENS Holiday Awards\n if (padding !== encodedReferrerPadding) {\n return zeroAddress;\n }\n\n const decodedReferrer = slice(encodedReferrer, ENCODED_REFERRER_BYTE_OFFSET);\n\n try {\n // return checksummed address\n return getAddress(decodedReferrer);\n } catch {\n throw new Error(`Decoded referrer value must be a valid EVM address.`);\n }\n}\n\n/**\n * Build a URL to the official ENS manager app\n * where the given {@link Address} is set as the referrer.\n */\nexport function buildEnsReferralUrl(address: Address): URL {\n const ensAppUrl = new URL(\"https://app.ens.domains\");\n\n ensAppUrl.searchParams.set(\"referrer\", getAddress(address));\n\n return ensAppUrl;\n}\n"],"mappings":";AAeO,IAAM,gCAA+C;AAMrD,IAAM,8BAA6C;;;ACrB1D,SAAuB,YAAsB,KAAK,MAAM,OAAO,mBAAmB;AAkB3E,IAAM,+BAA+B;AAOrC,IAAM,+BAA+B;AAOrC,IAAM,yBAAyB,IAAI,MAAM;AAAA,EAC9C,MAAM;AAAA,EACN,KAAK;AACP,CAAC;AAOM,IAAM,sBAAsB,IAAI,MAAM;AAAA,EAC3C,MAAM;AAAA,EACN,KAAK;AACP,CAAC;AAMM,SAAS,qBAAqB,SAAmC;AACtE,QAAM,mBAAmB,QAAQ,YAAY;AAE7C,SAAO,IAAI,kBAAkB,EAAE,MAAM,8BAA8B,KAAK,OAAO,CAAC;AAClF;AAYO,SAAS,sBAAsB,iBAA2C;AAE/E,MAAI,KAAK,eAAe,MAAM,8BAA8B;AAC1D,UAAM,IAAI;AAAA,MACR,iDAAiD,4BAA4B;AAAA,IAC/E;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,iBAAiB,GAAG,4BAA4B;AAItE,MAAI,YAAY,wBAAwB;AACtC,WAAO;AAAA,EACT;AAEA,QAAM,kBAAkB,MAAM,iBAAiB,4BAA4B;AAE3E,MAAI;AAEF,WAAO,WAAW,eAAe;AAAA,EACnC,QAAQ;AACN,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AACF;AAMO,SAAS,oBAAoB,SAAuB;AACzD,QAAM,YAAY,IAAI,IAAI,yBAAyB;AAEnD,YAAU,aAAa,IAAI,YAAY,WAAW,OAAO,CAAC;AAE1D,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/address.ts","../src/number.ts","../src/time.ts","../src/score.ts","../src/aggregations.ts","../src/currency.ts","../src/encoding.ts","../src/rank.ts","../src/referrer-metrics.ts","../src/leaderboard.ts","../src/leaderboard-page.ts","../src/link.ts","../src/rules.ts"],"sourcesContent":["import { type Address, isAddress } from \"viem\";\n\nexport const validateLowercaseAddress = (address: Address): void => {\n if (!isAddress(address, { strict: false })) {\n throw new Error(`Invalid address: ${address}. Address must be a valid EVM address.`);\n }\n\n if (address !== address.toLowerCase()) {\n throw new Error(`Invalid address: ${address}. Address must be in lowercase format.`);\n }\n};\n\nexport const normalizeAddress = (address: Address): Address => {\n return address.toLowerCase() as Address;\n};\n","export const isInteger = (value: number): boolean => {\n return Number.isInteger(value);\n};\n\nexport const isNonNegativeInteger = (value: number): boolean => {\n return value >= 0 && Number.isInteger(value);\n};\n\nexport const isPositiveInteger = (value: number): boolean => {\n return value >= 1 && Number.isInteger(value);\n};\n\nexport const validateNonNegativeInteger = (value: number): void => {\n if (!isNonNegativeInteger(value)) {\n throw new Error(`Invalid non-negative integer: ${value}.`);\n }\n};\n\nexport const isFiniteNonNegativeNumber = (value: number): boolean => {\n return value >= 0 && Number.isFinite(value);\n};\n","import { isInteger, isNonNegativeInteger } from \"./number\";\n\n/**\n * Unix timestamp value\n *\n * Represents the number of seconds that have elapsed\n * since January 1, 1970 (midnight UTC/GMT). May be zero or negative to represent a time at or\n * before Jan 1, 1970.\n *\n * @invariant Guaranteed to be an integer.\n */\nexport type UnixTimestamp = number;\n\nexport const validateUnixTimestamp = (timestamp: UnixTimestamp): void => {\n if (!isInteger(timestamp)) {\n throw new Error(`Invalid Unix timestamp: ${timestamp}. Unix timestamp must be an integer.`);\n }\n};\n\n/**\n * Duration\n *\n * Represents a duration in seconds.\n *\n * Guaranteed to be a non-negative integer.\n */\nexport type Duration = number;\n\n/**\n * The number of seconds in a year.\n *\n * (60 seconds per minute * 60 minutes per hour *\n * 24 hours per day * 365.2425 days on average per year).\n */\nexport const SECONDS_PER_YEAR: Duration = 31556952;\n\nexport function isValidDuration(duration: Duration): boolean {\n return isNonNegativeInteger(duration);\n}\n\nexport function validateDuration(duration: Duration): void {\n if (!isValidDuration(duration)) {\n throw new Error(`Invalid duration: ${duration}. Duration must be a non-negative integer.`);\n }\n}\n","import { type Duration, SECONDS_PER_YEAR } from \"./time\";\n\n/**\n * The score of a referrer.\n *\n * @invariant Guaranteed to be a finite non-negative number (>= 0)\n */\nexport type ReferrerScore = number;\n\nexport const isValidReferrerScore = (score: ReferrerScore): boolean => {\n return score >= 0 && Number.isFinite(score);\n};\n\nexport const validateReferrerScore = (score: ReferrerScore): void => {\n if (!isValidReferrerScore(score)) {\n throw new Error(\n `Invalid referrer score: ${score}. Referrer score must be a finite non-negative number.`,\n );\n }\n};\n\n/**\n * Calculate the score of a referrer based on the total incremental duration\n * (in seconds) of registrations and renewals for direct subnames of .eth\n * referrered by the referrer within the ENS Holiday Awards period.\n *\n * @param totalIncrementalDuration - The total incremental duration (in seconds)\n * of referrals made by a referrer within the {@link ReferralProgramRules}.\n * @returns The score of the referrer.\n */\nexport const calcReferrerScore = (totalIncrementalDuration: Duration): ReferrerScore => {\n return totalIncrementalDuration / SECONDS_PER_YEAR;\n};\n","import { validateNonNegativeInteger } from \"./number\";\nimport type { RankedReferrerMetrics } from \"./referrer-metrics\";\nimport type { ReferralProgramRules } from \"./rules\";\nimport { type ReferrerScore, validateReferrerScore } from \"./score\";\nimport { type Duration, validateDuration } from \"./time\";\n\n/**\n * Represents aggregated metrics for a list of `RankedReferrerMetrics`.\n */\nexport interface AggregatedReferrerMetrics {\n /**\n * @invariant The sum of `totalReferrals` across all `RankedReferrerMetrics` in the list.\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n */\n grandTotalReferrals: number;\n\n /**\n * @invariant The sum of `totalIncrementalDuration` across all `RankedReferrerMetrics` in the list.\n */\n grandTotalIncrementalDuration: Duration;\n\n /**\n * @invariant The sum of `finalScore` across all `RankedReferrerMetrics` where `isQualified` is `true`.\n */\n grandTotalQualifiedReferrersFinalScore: ReferrerScore;\n\n /**\n * @invariant Identifies the minimum final score required to become a qualified referrer.\n * @invariant If `rules.maxQualifiedReferrers` is 0, then `minFinalScoreToQualify` is guaranteed to\n * be `Number.MAX_SAFE_INTEGER`.\n * @invariant If `rules.maxQualifiedReferrers` is greater than 0, and there are no current referrers\n * matching the `rules`, then `minFinalScoreToQualify` is guaranteed to be `0`.\n */\n minFinalScoreToQualify: ReferrerScore;\n}\n\nexport const validateAggregatedReferrerMetrics = (metrics: AggregatedReferrerMetrics): void => {\n validateNonNegativeInteger(metrics.grandTotalReferrals);\n validateDuration(metrics.grandTotalIncrementalDuration);\n validateReferrerScore(metrics.grandTotalQualifiedReferrersFinalScore);\n validateReferrerScore(metrics.minFinalScoreToQualify);\n};\n\nexport const buildAggregatedReferrerMetrics = (\n referrers: RankedReferrerMetrics[],\n rules: ReferralProgramRules,\n): AggregatedReferrerMetrics => {\n let grandTotalReferrals = 0;\n let grandTotalIncrementalDuration = 0;\n let grandTotalQualifiedReferrersFinalScore = 0;\n let minFinalScoreToQualify = Number.MAX_SAFE_INTEGER;\n\n for (const referrer of referrers) {\n grandTotalReferrals += referrer.totalReferrals;\n grandTotalIncrementalDuration += referrer.totalIncrementalDuration;\n if (referrer.isQualified) {\n grandTotalQualifiedReferrersFinalScore += referrer.finalScore;\n if (referrer.finalScore < minFinalScoreToQualify) {\n minFinalScoreToQualify = referrer.finalScore;\n }\n }\n }\n\n if (minFinalScoreToQualify === Number.MAX_SAFE_INTEGER) {\n if (rules.maxQualifiedReferrers === 0) {\n // ... because it's impossible to qualify based on the rules\n // therefore keep minFinalScoreToQualify as Number.MAX_SAFE_INTEGER\n } else {\n // ... because there are no referrers at all on the leaderboard\n if (referrers.length !== 0) {\n // invariant sanity check\n throw new Error(\n \"AggregatedReferrerMetrics: There are referrers on the leaderboard, and the rules allow for qualified referrers, but no qualified referrers.\",\n );\n }\n\n minFinalScoreToQualify = 0;\n }\n }\n\n const result = {\n grandTotalReferrals,\n grandTotalIncrementalDuration,\n grandTotalQualifiedReferrersFinalScore,\n minFinalScoreToQualify,\n };\n\n validateAggregatedReferrerMetrics(result);\n\n return result;\n};\n","import { isFiniteNonNegativeNumber } from \"./number\";\n\n/**\n * Represents a quantity of USD.\n *\n * @invariant Guaranteed to be a finite non-negative number (>= 0)\n */\nexport type USDQuantity = number;\n\nexport function isValidUSDQuantity(value: USDQuantity): boolean {\n return isFiniteNonNegativeNumber(value);\n}\n\nexport function validateUSDQuantity(value: USDQuantity): void {\n if (!isValidUSDQuantity(value)) {\n throw new Error(`Invalid USD quantity: ${value}.`);\n }\n}\n","import { type Address, getAddress, type Hex, pad, size, slice, zeroAddress } from \"viem\";\n\n/**\n * Encoded Referrer\n *\n * Represents a \"raw\" ENS referrer value.\n *\n * Guaranteed to be a hex string representation of a 32-byte value.\n * For ENS Holiday Awards a correctly encoded referrer is\n * a left-padded lowercase EVM address.\n */\nexport type EncodedReferrer = Hex;\n\n/**\n * Encoded Referrer byte offset for ENS Holiday Awards.\n *\n * The count of left-padded bytes in an {@link EncodedReferrer} value for ENS Holiday Awards.\n */\nexport const ENCODED_REFERRER_BYTE_OFFSET = 12;\n\n/**\n * Encoded Referrer byte length\n *\n * The count of bytes the {@link EncodedReferrer} value consists of.\n */\nexport const ENCODED_REFERRER_BYTE_LENGTH = 32;\n\n/**\n * Encoded Referrer Padding for ENS Holiday Awards\n *\n * The initial bytes of correctly encoded referrer value for ENS Holiday Awards.\n */\nexport const EXPECTED_ENCODED_REFERRER_PADDING: Hex = pad(\"0x\", {\n size: ENCODED_REFERRER_BYTE_OFFSET,\n dir: \"left\",\n});\n\n/**\n * Zero Encoded Referrer\n *\n * Guaranteed to be a hex string representation of a 32-byte zero value.\n */\nexport const ZERO_ENCODED_REFERRER: EncodedReferrer = pad(\"0x\", {\n size: ENCODED_REFERRER_BYTE_LENGTH,\n dir: \"left\",\n});\n\n/**\n * Build an {@link EncodedReferrer} value for the given {@link Address}\n * according to the subjective referrer encoding used for ENS Holiday Awards.\n */\nexport function buildEncodedReferrer(address: Address): EncodedReferrer {\n const lowercaseAddress = address.toLowerCase() as Address;\n\n return pad(lowercaseAddress, { size: ENCODED_REFERRER_BYTE_LENGTH, dir: \"left\" });\n}\n\n/**\n * Decode an {@link EncodedReferrer} value into a checksummed {@link Address}\n * according to the subjective referrer encoding used for ENS Holiday Awards.\n *\n * @param encodedReferrer - The \"raw\" {@link EncodedReferrer} value to decode.\n * @returns The decoded referrer checksummed address.\n * @throws when encodedReferrer value is not represented by\n * {@link ENCODED_REFERRER_BYTE_LENGTH} bytes.\n * @throws when decodedReferrer is not a valid EVM address.\n */\nexport function decodeEncodedReferrer(encodedReferrer: EncodedReferrer): Address {\n // Invariant: encoded referrer must be of expected size\n if (size(encodedReferrer) !== ENCODED_REFERRER_BYTE_LENGTH) {\n throw new Error(\n `Encoded referrer value must be represented by ${ENCODED_REFERRER_BYTE_LENGTH} bytes.`,\n );\n }\n\n const padding = slice(encodedReferrer, 0, ENCODED_REFERRER_BYTE_OFFSET);\n\n // return zero address if the padding of encoded referrer is not correct\n // for ENS Holiday Awards\n if (padding !== EXPECTED_ENCODED_REFERRER_PADDING) {\n return zeroAddress;\n }\n\n const decodedReferrer = slice(encodedReferrer, ENCODED_REFERRER_BYTE_OFFSET);\n\n try {\n // return checksummed address\n return getAddress(decodedReferrer);\n } catch {\n throw new Error(`Decoded referrer value must be a valid EVM address.`);\n }\n}\n","import type { Address } from \"viem\";\n\nimport { isPositiveInteger } from \"./number\";\nimport type { ReferralProgramRules } from \"./rules\";\nimport { calcReferrerScore, type ReferrerScore } from \"./score\";\nimport type { Duration } from \"./time\";\n\n/**\n * The rank of a referrer relative to all other referrers, where 1 is the\n * top-ranked referrer.\n *\n * @invariant Guaranteed to be a positive integer (> 0)\n */\nexport type ReferrerRank = number;\n\nexport const validateReferrerRank = (rank: ReferrerRank): void => {\n if (!isPositiveInteger(rank)) {\n throw new Error(`Invalid ReferrerRank: ${rank}. ReferrerRank must be a positive integer.`);\n }\n};\n\n/**\n * Determine if a referrer with the given `rank` is qualified to receive a non-zero `awardPoolShare` according to the given `rules`.\n *\n * @param rank - The rank of the referrer relative to all other referrers on a {@link ReferrerLeaderboard}.\n * @param rules - The rules of the referral program that generated the `rank`.\n */\nexport function isReferrerQualified(rank: ReferrerRank, rules: ReferralProgramRules): boolean {\n return rank <= rules.maxQualifiedReferrers;\n}\n\n/**\n * Calculate the final score boost of a referrer based on their rank.\n *\n * @param rank - The rank of the referrer relative to all other referrers, where 1 is the\n * top-ranked referrer.\n * @returns The final score boost of the referrer as a number between 0 and 1 (inclusive).\n */\nexport function calcReferrerFinalScoreBoost(\n rank: ReferrerRank,\n rules: ReferralProgramRules,\n): number {\n if (!isReferrerQualified(rank, rules)) return 0;\n\n return 1 - (rank - 1) / (rules.maxQualifiedReferrers - 1);\n}\n\n/**\n * Calculate the final score multiplier of a referrer based on their rank.\n *\n * @param rank - The rank of the referrer relative to all other referrers, where 1 is the\n * top-ranked referrer.\n * @returns The final score multiplier of the referrer as a number between 1 and 2 (inclusive).\n */\nexport function calcReferrerFinalScoreMultiplier(\n rank: ReferrerRank,\n rules: ReferralProgramRules,\n): number {\n return 1 + calcReferrerFinalScoreBoost(rank, rules);\n}\n\n/**\n * Calculate the final score of a referrer based on their score and final score boost.\n *\n * @param rank - The rank of the referrer relative to all other referrers.\n * @param totalIncrementalDuration - The total incremental duration (in seconds)\n * of referrals made by the referrer within the `rules`.\n * @param rules - The rules of the referral program that generated the `rank`.\n * @returns The final score of the referrer.\n */\nexport function calcReferrerFinalScore(\n rank: ReferrerRank,\n totalIncrementalDuration: Duration,\n rules: ReferralProgramRules,\n): ReferrerScore {\n return (\n calcReferrerScore(totalIncrementalDuration) * calcReferrerFinalScoreMultiplier(rank, rules)\n );\n}\n\nexport interface ReferrerMetricsForComparison {\n /**\n * The total incremental duration (in seconds) of all referrals made by the referrer within\n * the {@link ReferralProgramRules}.\n */\n totalIncrementalDuration: Duration;\n\n /**\n * The fully lowercase Ethereum address of the referrer.\n *\n * @invariant Guaranteed to be a valid EVM address in lowercase format.\n */\n referrer: Address;\n}\n\nexport const compareReferrerMetrics = (\n a: ReferrerMetricsForComparison,\n b: ReferrerMetricsForComparison,\n): number => {\n // Primary sort: totalIncrementalDuration (descending)\n if (a.totalIncrementalDuration !== b.totalIncrementalDuration) {\n return b.totalIncrementalDuration - a.totalIncrementalDuration;\n }\n\n // Secondary sort: referrer address using lexicographic comparison of ASCII hex strings (descending)\n if (b.referrer > a.referrer) return 1;\n if (b.referrer < a.referrer) return -1;\n return 0;\n};\n","import type { Address } from \"viem\";\n\nimport { normalizeAddress, validateLowercaseAddress } from \"./address\";\nimport type { AggregatedReferrerMetrics } from \"./aggregations\";\nimport type { USDQuantity } from \"./currency\";\nimport { validateNonNegativeInteger } from \"./number\";\nimport {\n calcReferrerFinalScore,\n calcReferrerFinalScoreBoost,\n compareReferrerMetrics,\n isReferrerQualified,\n type ReferrerRank,\n validateReferrerRank,\n} from \"./rank\";\nimport type { ReferralProgramRules } from \"./rules\";\nimport { calcReferrerScore, type ReferrerScore, validateReferrerScore } from \"./score\";\nimport type { Duration } from \"./time\";\nimport { validateDuration } from \"./time\";\n\n/**\n * Represents metrics for a single referrer independent of other referrers.\n */\nexport interface ReferrerMetrics {\n /**\n * The fully lowercase Ethereum address of the referrer.\n *\n * @invariant Guaranteed to be a valid EVM address in lowercase format\n */\n referrer: Address;\n\n /**\n * The total number of referrals made by the referrer within the {@link ReferralProgramRules}.\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n */\n totalReferrals: number;\n\n /**\n * The total incremental duration (in seconds) of all referrals made by the referrer within\n * the {@link ReferralProgramRules}.\n */\n totalIncrementalDuration: Duration;\n}\n\nexport const buildReferrerMetrics = (\n referrer: Address,\n totalReferrals: number,\n totalIncrementalDuration: Duration,\n): ReferrerMetrics => {\n const result = {\n referrer: normalizeAddress(referrer),\n totalReferrals,\n totalIncrementalDuration,\n } satisfies ReferrerMetrics;\n\n validateReferrerMetrics(result);\n return result;\n};\n\nexport const validateReferrerMetrics = (metrics: ReferrerMetrics): void => {\n validateLowercaseAddress(metrics.referrer);\n validateNonNegativeInteger(metrics.totalReferrals);\n validateDuration(metrics.totalIncrementalDuration);\n};\n\nexport const sortReferrerMetrics = (referrers: ReferrerMetrics[]): ReferrerMetrics[] => {\n return [...referrers].sort(compareReferrerMetrics);\n};\n\n/**\n * Represents metrics for a single referrer independent of other referrers,\n * including a calculation of the referrer's score.\n */\nexport interface ScoredReferrerMetrics extends ReferrerMetrics {\n /**\n * The referrer's score.\n *\n * @invariant Guaranteed to be `calcReferrerScore(totalIncrementalDuration)`\n */\n score: ReferrerScore;\n}\n\nexport const buildScoredReferrerMetrics = (referrer: ReferrerMetrics): ScoredReferrerMetrics => {\n const result = {\n ...referrer,\n score: calcReferrerScore(referrer.totalIncrementalDuration),\n } satisfies ScoredReferrerMetrics;\n\n validateScoredReferrerMetrics(result);\n return result;\n};\n\nexport const validateScoredReferrerMetrics = (metrics: ScoredReferrerMetrics): void => {\n validateReferrerMetrics(metrics);\n validateReferrerScore(metrics.score);\n\n const expectedScore = calcReferrerScore(metrics.totalIncrementalDuration);\n if (metrics.score !== expectedScore) {\n throw new Error(`Referrer: Invalid score: ${metrics.score}, expected: ${expectedScore}.`);\n }\n};\n\n/**\n * Extends {@link ScoredReferrerMetrics} to include additional metrics\n * relative to all other referrers on a {@link ReferrerLeaderboard} and {@link ReferralProgramRules}.\n */\nexport interface RankedReferrerMetrics extends ScoredReferrerMetrics {\n /**\n * The referrer's rank on the {@link ReferrerLeaderboard} relative to all other referrers.\n */\n rank: ReferrerRank;\n\n /**\n * Identifies if the referrer meets the qualifications of the {@link ReferralProgramRules} to receive a non-zero `awardPoolShare`.\n *\n * @invariant true if and only if `rank` is less than or equal to {@link ReferralProgramRules.maxQualifiedReferrers}\n */\n isQualified: boolean;\n\n /**\n * The referrer's final score boost.\n *\n * @invariant Guaranteed to be a number between 0 and 1 (inclusive)\n * @invariant Calculated as: `1-((rank-1)/({@link ReferralProgramRules.maxQualifiedReferrers}-1))` if `isQualified` is `true`, else `0`\n */\n finalScoreBoost: number;\n\n /**\n * The referrer's final score.\n *\n * @invariant Calculated as: `score * (1 + finalScoreBoost)`\n */\n finalScore: ReferrerScore;\n}\n\nexport const validateRankedReferrerMetrics = (\n metrics: RankedReferrerMetrics,\n rules: ReferralProgramRules,\n): void => {\n validateScoredReferrerMetrics(metrics);\n validateReferrerRank(metrics.rank);\n\n if (metrics.finalScoreBoost < 0 || metrics.finalScoreBoost > 1) {\n throw new Error(\n `Invalid RankedReferrerMetrics: Invalid finalScoreBoost: ${metrics.finalScoreBoost}. finalScoreBoost must be between 0 and 1 (inclusive).`,\n );\n }\n\n validateReferrerScore(metrics.finalScore);\n\n const expectedIsQualified = isReferrerQualified(metrics.rank, rules);\n if (metrics.isQualified !== expectedIsQualified) {\n throw new Error(\n `RankedReferrerMetrics: Invalid isQualified: ${metrics.isQualified}, expected: ${expectedIsQualified}.`,\n );\n }\n\n const expectedFinalScoreBoost = calcReferrerFinalScoreBoost(metrics.rank, rules);\n if (metrics.finalScoreBoost !== expectedFinalScoreBoost) {\n throw new Error(\n `RankedReferrerMetrics: Invalid finalScoreBoost: ${metrics.finalScoreBoost}, expected: ${expectedFinalScoreBoost}.`,\n );\n }\n\n const expectedFinalScore = calcReferrerFinalScore(\n metrics.rank,\n metrics.totalIncrementalDuration,\n rules,\n );\n if (metrics.finalScore !== expectedFinalScore) {\n throw new Error(\n `RankedReferrerMetrics: Invalid finalScore: ${metrics.finalScore}, expected: ${expectedFinalScore}.`,\n );\n }\n};\n\nexport const buildRankedReferrerMetrics = (\n referrer: ScoredReferrerMetrics,\n rank: ReferrerRank,\n rules: ReferralProgramRules,\n): RankedReferrerMetrics => {\n const result = {\n ...referrer,\n rank,\n isQualified: isReferrerQualified(rank, rules),\n finalScoreBoost: calcReferrerFinalScoreBoost(rank, rules),\n finalScore: calcReferrerFinalScore(rank, referrer.totalIncrementalDuration, rules),\n } satisfies RankedReferrerMetrics;\n validateRankedReferrerMetrics(result, rules);\n return result;\n};\n\n/**\n * Calculate the share of the award pool for a referrer.\n * @param referrer - The referrer to calculate the award pool share for.\n * @param aggregatedMetrics - Aggregated metrics for all referrers.\n * @param rules - The rules of the referral program.\n * @returns The referrer's share of the award pool as a number between 0 and 1 (inclusive).\n */\nexport const calcReferrerAwardPoolShare = (\n referrer: RankedReferrerMetrics,\n aggregatedMetrics: AggregatedReferrerMetrics,\n rules: ReferralProgramRules,\n): number => {\n if (!isReferrerQualified(referrer.rank, rules)) return 0;\n if (aggregatedMetrics.grandTotalQualifiedReferrersFinalScore === 0) return 0;\n\n return (\n calcReferrerFinalScore(referrer.rank, referrer.totalIncrementalDuration, rules) /\n aggregatedMetrics.grandTotalQualifiedReferrersFinalScore\n );\n};\n\n/**\n * Extends {@link RankedReferrerMetrics} to include additional metrics\n * relative to {@link AggregatedRankedReferrerMetrics}.\n */\nexport interface AwardedReferrerMetrics extends RankedReferrerMetrics {\n /**\n * The referrer's share of the award pool.\n *\n * @invariant Guaranteed to be a number between 0 and 1 (inclusive)\n * @invariant Calculated as: `finalScore / {@link AggregatedRankedReferrerMetrics.grandTotalQualifiedReferrersFinalScore}` if `isQualified` is `true`, else `0`\n */\n awardPoolShare: number;\n\n /**\n * The approximate {@link USDQuantity} of the referrer's share of the {@link ReferralProgramRules.totalAwardPoolValue}.\n *\n * @invariant Guaranteed to be a number between 0 and {@link ReferralProgramRules.totalAwardPoolValue} (inclusive)\n * @invariant Calculated as: `awardPoolShare` * {@link ReferralProgramRules.totalAwardPoolValue}\n */\n awardPoolApproxValue: USDQuantity;\n}\n\nexport const validateAwardedReferrerMetrics = (\n referrer: AwardedReferrerMetrics,\n rules: ReferralProgramRules,\n): void => {\n validateRankedReferrerMetrics(referrer, rules);\n if (referrer.awardPoolShare < 0 || referrer.awardPoolShare > 1) {\n throw new Error(\n `Invalid AwardedReferrerMetrics: ${referrer.awardPoolShare}. awardPoolShare must be between 0 and 1 (inclusive).`,\n );\n }\n\n if (\n referrer.awardPoolApproxValue < 0 ||\n referrer.awardPoolApproxValue > rules.totalAwardPoolValue\n ) {\n throw new Error(\n `Invalid AwardedReferrerMetrics: ${referrer.awardPoolApproxValue}. awardPoolApproxValue must be between 0 and ${rules.totalAwardPoolValue} (inclusive).`,\n );\n }\n};\n\nexport const buildAwardedReferrerMetrics = (\n referrer: RankedReferrerMetrics,\n aggregatedMetrics: AggregatedReferrerMetrics,\n rules: ReferralProgramRules,\n): AwardedReferrerMetrics => {\n const awardPoolShare = calcReferrerAwardPoolShare(referrer, aggregatedMetrics, rules);\n\n const result = {\n ...referrer,\n awardPoolShare,\n awardPoolApproxValue: awardPoolShare * rules.totalAwardPoolValue,\n };\n validateAwardedReferrerMetrics(result, rules);\n return result;\n};\n","import type { Address } from \"viem\";\n\nimport { type AggregatedReferrerMetrics, buildAggregatedReferrerMetrics } from \"./aggregations\";\nimport {\n type AwardedReferrerMetrics,\n buildAwardedReferrerMetrics,\n buildRankedReferrerMetrics,\n buildScoredReferrerMetrics,\n type ReferrerMetrics,\n sortReferrerMetrics,\n} from \"./referrer-metrics\";\nimport type { ReferralProgramRules } from \"./rules\";\nimport type { UnixTimestamp } from \"./time\";\n\n/**\n * Represents a leaderboard for any number of referrers.\n */\nexport interface ReferrerLeaderboard {\n /**\n * The rules of the referral program that generated the {@link ReferrerLeaderboard}.\n */\n rules: ReferralProgramRules;\n\n /**\n * The {@link AggregatedReferrerMetrics} for all `RankedReferrerMetrics` values in `leaderboard`.\n */\n aggregatedMetrics: AggregatedReferrerMetrics;\n\n /**\n * Ordered map containing `AwardedReferrerMetrics` for all referrers with 1 or more\n * `totalReferrals` within the `rules` as of `updatedAt`.\n *\n * @invariant Map entries are ordered by `rank` (ascending).\n * @invariant Map is empty if there are no referrers with 1 or more `totalReferrals`\n * within the `rules` as of `updatedAt`.\n * @invariant If a fully-lowercase `Address` is not a key in this map then that `Address` had\n * 0 `totalReferrals`, `totalIncrementalDuration`, and `score` within the\n * `rules` as of `updatedAt`.\n * @invariant Each value in this map is guaranteed to have a non-zero\n * `totalReferrals`, `totalIncrementalDuration`, and `score`.\n */\n referrers: Map<Address, AwardedReferrerMetrics>;\n\n /**\n * The {@link UnixTimestamp} of when the data used to build the {@link ReferrerLeaderboard} was accurate as of.\n */\n accurateAsOf: UnixTimestamp;\n}\n\nexport const buildReferrerLeaderboard = (\n allReferrers: ReferrerMetrics[],\n rules: ReferralProgramRules,\n accurateAsOf: UnixTimestamp,\n): ReferrerLeaderboard => {\n const uniqueReferrers = allReferrers.map((referrer) => referrer.referrer);\n if (uniqueReferrers.length !== allReferrers.length) {\n throw new Error(\n \"ReferrerLeaderboard: Cannot buildReferrerLeaderboard containing duplicate referrers\",\n );\n }\n\n if (accurateAsOf < rules.startTime && allReferrers.length > 0) {\n throw new Error(\n `ReferrerLeaderboard: accurateAsOf (${accurateAsOf}) is before startTime (${rules.startTime}) which indicates allReferrers should be empty, but allReferrers is not empty.`,\n );\n }\n\n const sortedReferrers = sortReferrerMetrics(allReferrers);\n\n const scoredReferrers = sortedReferrers.map((referrer) => buildScoredReferrerMetrics(referrer));\n\n const rankedReferrers = scoredReferrers.map((referrer, index) => {\n return buildRankedReferrerMetrics(referrer, index + 1, rules);\n });\n\n const aggregatedMetrics = buildAggregatedReferrerMetrics(rankedReferrers, rules);\n\n const awardedReferrers = rankedReferrers.map((referrer) => {\n return buildAwardedReferrerMetrics(referrer, aggregatedMetrics, rules);\n });\n\n // Transform ordered list into an ordered map (preserves sort order)\n const referrers = new Map(\n awardedReferrers.map((referrer) => {\n return [referrer.referrer, referrer];\n }),\n );\n\n return {\n rules,\n aggregatedMetrics,\n referrers,\n accurateAsOf,\n };\n};\n","import type { AggregatedReferrerMetrics } from \"./aggregations\";\nimport type { ReferrerLeaderboard } from \"./leaderboard\";\nimport { isNonNegativeInteger, isPositiveInteger } from \"./number\";\nimport type { AwardedReferrerMetrics } from \"./referrer-metrics\";\nimport type { ReferralProgramRules } from \"./rules\";\nimport type { UnixTimestamp } from \"./time\";\n\n/**\n * The default number of referrers per leaderboard page.\n */\nexport const REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT = 25;\n\n/**\n * The maximum number of referrers per leaderboard page.\n */\n\nexport const REFERRERS_PER_LEADERBOARD_PAGE_MAX = 100;\n\n/**\n * Pagination params for leaderboard queries.\n */\nexport interface ReferrerLeaderboardPaginationParams {\n /**\n * Requested referrer leaderboard page number (1-indexed)\n * @invariant Must be a positive integer (>= 1)\n * @default 1\n */\n page?: number;\n\n /**\n * Maximum number of referrers to return per leaderboard page\n * @invariant Must be a positive integer (>= 1) and less than or equal to {@link REFERRERS_PER_LEADERBOARD_PAGE_MAX}\n * @default {@link REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT}\n */\n itemsPerPage?: number;\n}\n\nconst validateReferrerLeaderboardPaginationParams = (\n params: ReferrerLeaderboardPaginationParams,\n): void => {\n if (params.page !== undefined && !isPositiveInteger(params.page)) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationParams: ${params.page}. page must be a positive integer.`,\n );\n }\n if (params.itemsPerPage !== undefined && !isPositiveInteger(params.itemsPerPage)) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationParams: ${params.itemsPerPage}. itemsPerPage must be a positive integer.`,\n );\n }\n if (\n params.itemsPerPage !== undefined &&\n params.itemsPerPage > REFERRERS_PER_LEADERBOARD_PAGE_MAX\n ) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationParams: ${params.itemsPerPage}. itemsPerPage must be less than or equal to ${REFERRERS_PER_LEADERBOARD_PAGE_MAX}.`,\n );\n }\n};\n\nexport const buildReferrerLeaderboardPaginationParams = (\n params: ReferrerLeaderboardPaginationParams,\n): Required<ReferrerLeaderboardPaginationParams> => {\n const result = {\n page: params.page ?? 1,\n itemsPerPage: params.itemsPerPage ?? REFERRERS_PER_LEADERBOARD_PAGE_DEFAULT,\n } satisfies Required<ReferrerLeaderboardPaginationParams>;\n validateReferrerLeaderboardPaginationParams(result);\n return result;\n};\n\nexport interface ReferrerLeaderboardPaginationContext\n extends Required<ReferrerLeaderboardPaginationParams> {\n /**\n * Total number of referrers across all leaderboard pages\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n */\n totalRecords: number;\n\n /**\n * Total number of pages in the leaderboard\n * @invariant Guaranteed to be a positive integer (>= 1)\n */\n totalPages: number;\n\n /**\n * Indicates if there is a next page available\n * @invariant true if and only if (`page` * `itemsPerPage` < `total`)\n */\n hasNext: boolean;\n\n /**\n * Indicates if there is a previous page available\n * @invariant true if and only if (`page` > 1)\n */\n hasPrev: boolean;\n\n /**\n * The start index of the referrers on the page (0-indexed)\n *\n * `undefined` if and only if `totalRecords` is 0.\n *\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n */\n startIndex?: number;\n\n /**\n * The end index of the referrers on the page (0-indexed)\n *\n * `undefined` if and only if `totalRecords` is 0.\n *\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n * @invariant If `totalRecords` is > 0:\n * - Guaranteed to be greater than or equal to `startIndex`.\n * - Guaranteed to be less than `totalRecords`.\n */\n endIndex?: number;\n}\n\nexport const validateReferrerLeaderboardPaginationContext = (\n context: ReferrerLeaderboardPaginationContext,\n): void => {\n validateReferrerLeaderboardPaginationParams(context);\n if (!isNonNegativeInteger(context.totalRecords)) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: total must be a non-negative integer but is ${context.totalRecords}.`,\n );\n }\n const startIndex = (context.page - 1) * context.itemsPerPage;\n const endIndex = startIndex + context.itemsPerPage;\n\n if (!context.hasNext && endIndex < context.totalRecords) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: if hasNext is false, endIndex (${endIndex}) must be greater than or equal to total (${context.totalRecords}).`,\n );\n } else if (context.hasNext && context.page * context.itemsPerPage >= context.totalRecords) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: if hasNext is true, endIndex (${endIndex}) must be less than total (${context.totalRecords}).`,\n );\n }\n if (!context.hasPrev && context.page !== 1) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: if hasPrev is false, page must be the first page (1) but is ${context.page}.`,\n );\n } else if (context.hasPrev && context.page === 1) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: if hasPrev is true, page must not be the first page (1) but is ${context.page}.`,\n );\n }\n};\n\nexport const buildReferrerLeaderboardPaginationContext = (\n optionalParams: ReferrerLeaderboardPaginationParams,\n leaderboard: ReferrerLeaderboard,\n): ReferrerLeaderboardPaginationContext => {\n const materializedParams = buildReferrerLeaderboardPaginationParams(optionalParams);\n\n const totalRecords = leaderboard.referrers.size;\n\n const totalPages = Math.max(1, Math.ceil(totalRecords / materializedParams.itemsPerPage));\n\n if (materializedParams.page > totalPages) {\n throw new Error(\n `Invalid ReferrerLeaderboardPaginationContext: page ${materializedParams.page} exceeds total pages ${totalPages}.`,\n );\n }\n\n if (totalRecords === 0) {\n return {\n ...materializedParams,\n totalRecords: 0,\n totalPages: 1,\n hasNext: false,\n hasPrev: false,\n startIndex: undefined,\n endIndex: undefined,\n } satisfies ReferrerLeaderboardPaginationContext;\n }\n\n const startIndex = (materializedParams.page - 1) * materializedParams.itemsPerPage;\n const maxTheoreticalIndexOnPage = startIndex + (materializedParams.itemsPerPage - 1);\n const endIndex = Math.min(maxTheoreticalIndexOnPage, totalRecords - 1);\n const hasNext = maxTheoreticalIndexOnPage < totalRecords;\n const hasPrev = materializedParams.page > 1;\n\n const result = {\n ...materializedParams,\n totalRecords,\n totalPages,\n hasNext,\n hasPrev,\n startIndex,\n endIndex,\n } satisfies ReferrerLeaderboardPaginationContext;\n validateReferrerLeaderboardPaginationContext(result);\n return result;\n};\n\n/**\n * A page of referrers from the referrer leaderboard.\n */\nexport interface ReferrerLeaderboardPage {\n /**\n * The {@link ReferralProgramRules} used to generate the {@link ReferrerLeaderboard}\n * that this {@link ReferrerLeaderboardPage} comes from.\n */\n rules: ReferralProgramRules;\n\n /**\n * Ordered list of {@link AwardedReferrerMetrics} for the {@link ReferrerLeaderboardPage}\n * described by `paginationContext` within the related {@link ReferrerLeaderboard}.\n *\n * @invariant Array will be empty if `paginationContext.totalRecords` is 0.\n * @invariant Array entries are ordered by `rank` (descending).\n */\n referrers: AwardedReferrerMetrics[];\n\n /**\n * Aggregated metrics for all referrers on the leaderboard.\n */\n aggregatedMetrics: AggregatedReferrerMetrics;\n\n /**\n * The {@link ReferrerLeaderboardPaginationContext} of this {@link ReferrerLeaderboardPage} relative to the overall\n * {@link ReferrerLeaderboard}.\n */\n paginationContext: ReferrerLeaderboardPaginationContext;\n\n /**\n * The {@link UnixTimestamp} of when the data used to build the {@link ReferrerLeaderboardPage} was accurate as of.\n */\n accurateAsOf: UnixTimestamp;\n}\n\nexport const getReferrerLeaderboardPage = (\n paginationParams: ReferrerLeaderboardPaginationParams,\n leaderboard: ReferrerLeaderboard,\n): ReferrerLeaderboardPage => {\n const paginationContext = buildReferrerLeaderboardPaginationContext(\n paginationParams,\n leaderboard,\n );\n\n let referrers: AwardedReferrerMetrics[];\n\n if (\n paginationContext.totalRecords > 0 &&\n typeof paginationContext.startIndex !== \"undefined\" &&\n typeof paginationContext.endIndex !== \"undefined\"\n ) {\n // extract the referrers from the leaderboard in the range specified by `paginationContext`.\n referrers = Array.from(leaderboard.referrers.values()).slice(\n paginationContext.startIndex,\n paginationContext.endIndex + 1, // For `slice`, this is exclusive of the element at the index 'end'. We need it to be inclusive, hence plus one.\n );\n } else {\n referrers = [];\n }\n\n return {\n rules: leaderboard.rules,\n referrers,\n aggregatedMetrics: leaderboard.aggregatedMetrics,\n paginationContext,\n accurateAsOf: leaderboard.accurateAsOf,\n };\n};\n","import { type Address, getAddress } from \"viem\";\n\n/**\n * Build a URL to the official ENS manager app\n * where the given {@link Address} is set as the referrer.\n */\nexport function buildEnsReferralUrl(address: Address): URL {\n const ensAppUrl = new URL(\"https://app.ens.domains\");\n\n ensAppUrl.searchParams.set(\"referrer\", getAddress(address));\n\n return ensAppUrl;\n}\n","import type { Address } from \"viem\";\n\nimport { type USDQuantity, validateUSDQuantity } from \"./currency\";\nimport { validateNonNegativeInteger } from \"./number\";\nimport { type UnixTimestamp, validateUnixTimestamp } from \"./time\";\n\n/**\n * Start date for the ENS Holiday Awards referral program.\n * 2025-12-01T00:00:00Z (December 1, 2025 at 00:00:00 UTC)\n */\nexport const ENS_HOLIDAY_AWARDS_START_DATE: UnixTimestamp = 1764547200;\n\n/**\n * End date for the ENS Holiday Awards referral program.\n * 2025-12-31T23:59:59Z (December 31, 2025 at 23:59:59 UTC)\n */\nexport const ENS_HOLIDAY_AWARDS_END_DATE: UnixTimestamp = 1767225599;\n\n/**\n * The maximum number of qualified referrers for ENS Holiday Awards.\n */\nexport const ENS_HOLIDAY_AWARDS_MAX_QUALIFIED_REFERRERS = 10;\n\n/**\n * The total value of the award pool in USD.\n */\nexport const ENS_HOLIDAY_AWARDS_TOTAL_AWARD_POOL_VALUE: USDQuantity = 10_000.0;\n\n/**\n * Chain ID\n *\n * Represents a unique identifier for a chain.\n * Guaranteed to be a positive integer.\n **/\nexport type ChainId = number;\n\n/**\n * Represents an account (contract or EOA) at `address` on chain `chainId`.\n *\n * @see https://chainagnostic.org/CAIPs/caip-10\n */\nexport interface AccountId {\n chainId: ChainId;\n address: Address;\n}\n\nexport interface ReferralProgramRules {\n /**\n * The total value of the award pool in USD.\n *\n * NOTE: Awards will actually be distributed in $ENS tokens.\n */\n totalAwardPoolValue: USDQuantity;\n\n /**\n * The maximum number of referrers that will qualify to receive a non-zero `awardPoolShare`.\n *\n * @invariant Guaranteed to be a non-negative integer (>= 0)\n */\n maxQualifiedReferrers: number;\n\n /**\n * The start time of the referral program.\n */\n startTime: UnixTimestamp;\n\n /**\n * The end time of the referral program.\n * @invariant Guaranteed to be greater than or equal to `startTime`\n */\n endTime: UnixTimestamp;\n\n /**\n * The account ID of the subregistry for the referral program.\n */\n subregistryId: AccountId;\n}\n\nexport const validateReferralProgramRules = (rules: ReferralProgramRules): void => {\n validateUSDQuantity(rules.totalAwardPoolValue);\n validateNonNegativeInteger(rules.maxQualifiedReferrers);\n validateUnixTimestamp(rules.startTime);\n validateUnixTimestamp(rules.endTime);\n\n if (rules.endTime < rules.startTime) {\n throw new Error(\n `ReferralProgramRules: startTime: ${rules.startTime} is after endTime: ${rules.endTime}.`,\n );\n }\n};\n\nexport const buildReferralProgramRules = (\n totalAwardPoolValue: USDQuantity,\n maxQualifiedReferrers: number,\n startTime: UnixTimestamp,\n endTime: UnixTimestamp,\n subregistryId: AccountId,\n): ReferralProgramRules => {\n const result = {\n totalAwardPoolValue,\n maxQualifiedReferrers,\n startTime,\n endTime,\n subregistryId,\n } satisfies ReferralProgramRules;\n\n validateReferralProgramRules(result);\n\n return result;\n};\n"],"mappings":"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","names":["getAddress"]}
|