@provable-games/metagame-sdk 0.1.0 → 0.1.2
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/extensions-BskkhEHk.d.cts +101 -0
- package/dist/extensions-BskkhEHk.d.ts +101 -0
- package/dist/index.cjs +1071 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +489 -0
- package/dist/index.d.ts +489 -0
- package/dist/index.js +1015 -0
- package/dist/index.js.map +1 -0
- package/dist/prizeAggregation-CHwIJzXr.d.cts +325 -0
- package/dist/prizeAggregation-CHwIJzXr.d.ts +325 -0
- package/dist/react.cjs +918 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +300 -0
- package/dist/react.d.ts +300 -0
- package/dist/react.js +906 -0
- package/dist/react.js.map +1 -0
- package/dist/rpc.cjs +161 -0
- package/dist/rpc.cjs.map +1 -0
- package/dist/rpc.d.cts +48 -0
- package/dist/rpc.d.ts +48 -0
- package/dist/rpc.js +146 -0
- package/dist/rpc.js.map +1 -0
- package/package.json +10 -10
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1071 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var starknet = require('starknet');
|
|
4
|
+
|
|
5
|
+
// src/types/extensions.ts
|
|
6
|
+
var QualifyingMode = /* @__PURE__ */ ((QualifyingMode2) => {
|
|
7
|
+
QualifyingMode2[QualifyingMode2["AtLeastOne"] = 0] = "AtLeastOne";
|
|
8
|
+
QualifyingMode2[QualifyingMode2["CumulativePerTournament"] = 1] = "CumulativePerTournament";
|
|
9
|
+
QualifyingMode2[QualifyingMode2["All"] = 2] = "All";
|
|
10
|
+
QualifyingMode2[QualifyingMode2["CumulativePerEntry"] = 3] = "CumulativePerEntry";
|
|
11
|
+
QualifyingMode2[QualifyingMode2["AllParticipateAnyWin"] = 4] = "AllParticipateAnyWin";
|
|
12
|
+
QualifyingMode2[QualifyingMode2["AllWithCumulative"] = 5] = "AllWithCumulative";
|
|
13
|
+
return QualifyingMode2;
|
|
14
|
+
})(QualifyingMode || {});
|
|
15
|
+
|
|
16
|
+
// src/utils/address.ts
|
|
17
|
+
function indexAddress(address) {
|
|
18
|
+
return address.replace(/^0x0+/, "0x");
|
|
19
|
+
}
|
|
20
|
+
function padAddress(address) {
|
|
21
|
+
if (address && address !== "") {
|
|
22
|
+
const length = address.length;
|
|
23
|
+
const neededLength = 66 - length;
|
|
24
|
+
let zeros = "";
|
|
25
|
+
for (let i = 0; i < neededLength; i++) {
|
|
26
|
+
zeros += "0";
|
|
27
|
+
}
|
|
28
|
+
return address.substring(0, 2) + zeros + address.substring(2);
|
|
29
|
+
}
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
function displayAddress(address) {
|
|
33
|
+
if (address === void 0) return "unknown";
|
|
34
|
+
return address.substring(0, 6) + "..." + address.substring(address.length - 4);
|
|
35
|
+
}
|
|
36
|
+
function bigintToHex(v) {
|
|
37
|
+
if (!v) return "0x0";
|
|
38
|
+
return `0x${BigInt(v).toString(16)}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// src/utils/formatting.ts
|
|
42
|
+
function formatNumber(num) {
|
|
43
|
+
if (Math.abs(num) >= 1e6) {
|
|
44
|
+
return parseFloat((num / 1e6).toFixed(2)) + "m";
|
|
45
|
+
} else if (Math.abs(num) >= 1e3) {
|
|
46
|
+
return parseFloat((num / 1e3).toFixed(2)) + "k";
|
|
47
|
+
} else if (Math.abs(num) >= 1) {
|
|
48
|
+
return num.toFixed(0);
|
|
49
|
+
} else if (Math.abs(num) >= 0.1) {
|
|
50
|
+
return num.toFixed(1);
|
|
51
|
+
} else if (Math.abs(num) >= 0.01) {
|
|
52
|
+
return num.toFixed(2);
|
|
53
|
+
} else if (Math.abs(num) >= 1e-3) {
|
|
54
|
+
return num.toFixed(3);
|
|
55
|
+
} else if (num === 0) {
|
|
56
|
+
return "0";
|
|
57
|
+
} else {
|
|
58
|
+
return num.toFixed(4);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function formatPrizeAmount(num) {
|
|
62
|
+
if (Math.abs(num) >= 1e6) {
|
|
63
|
+
return parseFloat((num / 1e6).toFixed(2)) + "m";
|
|
64
|
+
} else if (Math.abs(num) >= 1e3) {
|
|
65
|
+
return parseFloat((num / 1e3).toFixed(2)) + "k";
|
|
66
|
+
} else if (Math.abs(num) >= 100) {
|
|
67
|
+
return num.toFixed(1);
|
|
68
|
+
} else if (Math.abs(num) >= 10) {
|
|
69
|
+
return num.toFixed(2);
|
|
70
|
+
} else if (Math.abs(num) >= 1) {
|
|
71
|
+
return num.toFixed(2);
|
|
72
|
+
} else if (Math.abs(num) >= 0.1) {
|
|
73
|
+
return num.toFixed(2);
|
|
74
|
+
} else if (Math.abs(num) >= 0.01) {
|
|
75
|
+
return num.toFixed(3);
|
|
76
|
+
} else if (Math.abs(num) >= 1e-3) {
|
|
77
|
+
return num.toFixed(4);
|
|
78
|
+
} else if (num === 0) {
|
|
79
|
+
return "0";
|
|
80
|
+
} else {
|
|
81
|
+
return num.toFixed(5);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function formatUsdValue(value) {
|
|
85
|
+
if (value === 0) return "0.00";
|
|
86
|
+
if (value < 0.01 && value > 0) return "<0.01";
|
|
87
|
+
return value.toFixed(2);
|
|
88
|
+
}
|
|
89
|
+
function formatScore(num) {
|
|
90
|
+
if (Math.abs(num) >= 1e6) {
|
|
91
|
+
return parseFloat((num / 1e6).toFixed(2)) + "m";
|
|
92
|
+
} else if (Math.abs(num) >= 1e3) {
|
|
93
|
+
return parseFloat((num / 1e3).toFixed(2)) + "k";
|
|
94
|
+
} else if (Math.abs(num) >= 10) {
|
|
95
|
+
return num.toFixed(0);
|
|
96
|
+
} else if (Math.abs(num) > 0) {
|
|
97
|
+
return num.toFixed(0);
|
|
98
|
+
} else {
|
|
99
|
+
return "0";
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function formatTime(seconds) {
|
|
103
|
+
const minutes = Math.floor(seconds / 60);
|
|
104
|
+
const hours = Math.floor(minutes / 60);
|
|
105
|
+
const days = Math.floor(hours / 24);
|
|
106
|
+
if (days > 0) {
|
|
107
|
+
return `${days} Day${days > 1 ? "s" : ""}`;
|
|
108
|
+
} else if (hours > 0) {
|
|
109
|
+
return `${hours} Hour${hours > 1 ? "s" : ""}`;
|
|
110
|
+
} else if (minutes > 0) {
|
|
111
|
+
return `${minutes} Min${minutes > 1 ? "s" : ""}`;
|
|
112
|
+
} else {
|
|
113
|
+
return `${seconds.toFixed(0)} Sec${seconds > 1 ? "s" : ""}`;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function getOrdinalSuffix(position) {
|
|
117
|
+
const formatPosition = isNaN(position) ? 0 : position;
|
|
118
|
+
if (formatPosition % 10 === 1 && formatPosition !== 11) return "st";
|
|
119
|
+
if (formatPosition % 10 === 2 && formatPosition !== 12) return "nd";
|
|
120
|
+
if (position % 10 === 3 && position !== 13) return "rd";
|
|
121
|
+
return "th";
|
|
122
|
+
}
|
|
123
|
+
function calculatePayouts(totalPlaces, weightingFactor) {
|
|
124
|
+
const weights = [];
|
|
125
|
+
for (let i = 1; i <= totalPlaces; i++) {
|
|
126
|
+
weights.push(1 / Math.pow(i, weightingFactor));
|
|
127
|
+
}
|
|
128
|
+
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
129
|
+
const payouts = weights.map(
|
|
130
|
+
(weight) => Math.floor(weight / totalWeight * 100)
|
|
131
|
+
);
|
|
132
|
+
const totalPayout = payouts.reduce((sum, payout) => sum + payout, 0);
|
|
133
|
+
let remaining = 100 - totalPayout;
|
|
134
|
+
let index = 0;
|
|
135
|
+
while (remaining > 0) {
|
|
136
|
+
payouts[index] += 1;
|
|
137
|
+
remaining -= 1;
|
|
138
|
+
index = (index + 1) % totalPlaces;
|
|
139
|
+
}
|
|
140
|
+
return payouts;
|
|
141
|
+
}
|
|
142
|
+
function calculateDistribution(positions, weight, creatorFee, gameFee, refundShare, distributionType = "exponential") {
|
|
143
|
+
if (positions <= 0) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const safeCreatorFee = creatorFee ?? 0;
|
|
147
|
+
const safeGameFee = gameFee ?? 0;
|
|
148
|
+
const safeRefundShare = refundShare ?? 0;
|
|
149
|
+
const prizePoolPercentage = 100 - safeCreatorFee - safeGameFee - safeRefundShare;
|
|
150
|
+
if (prizePoolPercentage <= 0) {
|
|
151
|
+
return Array(positions).fill(0);
|
|
152
|
+
}
|
|
153
|
+
let rawDistributions = [];
|
|
154
|
+
if (distributionType === "uniform") {
|
|
155
|
+
rawDistributions = Array(positions).fill(1);
|
|
156
|
+
} else if (distributionType === "linear") {
|
|
157
|
+
for (let i = 0; i < positions; i++) {
|
|
158
|
+
const positionValue = positions - i;
|
|
159
|
+
const share = 1 + (positionValue - 1) * (weight / 10);
|
|
160
|
+
rawDistributions.push(share);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
for (let i = 0; i < positions; i++) {
|
|
164
|
+
const share = Math.pow(1 - i / positions, weight);
|
|
165
|
+
rawDistributions.push(share);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const total = rawDistributions.reduce((a, b) => a + b, 0);
|
|
169
|
+
if (total === 0) {
|
|
170
|
+
return Array(positions).fill(0);
|
|
171
|
+
}
|
|
172
|
+
const basisPointShares = rawDistributions.map((d) => {
|
|
173
|
+
const ratio = d / total;
|
|
174
|
+
const basisPoints = ratio * 1e4;
|
|
175
|
+
return Math.floor(basisPoints);
|
|
176
|
+
});
|
|
177
|
+
const totalBasisPoints = basisPointShares.reduce((a, b) => a + b, 0);
|
|
178
|
+
const remainingBasisPoints = 1e4 - totalBasisPoints;
|
|
179
|
+
if (remainingBasisPoints !== 0 && positions > 0) {
|
|
180
|
+
basisPointShares[0] = basisPointShares[0] + remainingBasisPoints;
|
|
181
|
+
}
|
|
182
|
+
return basisPointShares.map((bp) => bp / 100);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// src/utils/prizes.ts
|
|
186
|
+
function groupPrizesByToken(prizes) {
|
|
187
|
+
return prizes.reduce(
|
|
188
|
+
(acc, prize) => {
|
|
189
|
+
const key = prize.tokenAddress;
|
|
190
|
+
if (!acc[key]) {
|
|
191
|
+
acc[key] = {
|
|
192
|
+
type: prize.tokenType,
|
|
193
|
+
address: prize.tokenAddress,
|
|
194
|
+
totalAmount: "0"
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
acc[key].totalAmount = String(
|
|
198
|
+
BigInt(acc[key].totalAmount) + BigInt(prize.amount)
|
|
199
|
+
);
|
|
200
|
+
return acc;
|
|
201
|
+
},
|
|
202
|
+
{}
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
function calculatePrizeValue(amount, decimals, price) {
|
|
206
|
+
return Number(amount) / 10 ** decimals * price;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// src/utils/status.ts
|
|
210
|
+
function computeStatus(timestamps, now) {
|
|
211
|
+
const currentTimestamp = now ?? Math.floor(Date.now() / 1e3);
|
|
212
|
+
if (timestamps.unlocked === false) {
|
|
213
|
+
return {
|
|
214
|
+
label: "Locked",
|
|
215
|
+
variant: "locked",
|
|
216
|
+
isActive: false,
|
|
217
|
+
countdown: null
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (timestamps.completed) {
|
|
221
|
+
return {
|
|
222
|
+
label: "Completed",
|
|
223
|
+
variant: "completed",
|
|
224
|
+
isActive: false,
|
|
225
|
+
countdown: null
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const { registrationStart, registrationEnd, start, end, submissionEnd } = timestamps;
|
|
229
|
+
if (registrationStart && registrationEnd) {
|
|
230
|
+
if (currentTimestamp < registrationStart) {
|
|
231
|
+
return {
|
|
232
|
+
label: "Upcoming",
|
|
233
|
+
variant: "upcoming",
|
|
234
|
+
isActive: false,
|
|
235
|
+
countdown: {
|
|
236
|
+
targetTimestamp: registrationStart,
|
|
237
|
+
label: "Registration"
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (currentTimestamp >= registrationStart && currentTimestamp < registrationEnd) {
|
|
242
|
+
return {
|
|
243
|
+
label: "Registration",
|
|
244
|
+
variant: "registration",
|
|
245
|
+
isActive: true,
|
|
246
|
+
countdown: { targetTimestamp: registrationEnd, label: "Closes" }
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (currentTimestamp < start) {
|
|
251
|
+
return {
|
|
252
|
+
label: "Upcoming",
|
|
253
|
+
variant: "upcoming",
|
|
254
|
+
isActive: false,
|
|
255
|
+
countdown: { targetTimestamp: start, label: "Starts" }
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
if (currentTimestamp >= start && currentTimestamp < end) {
|
|
259
|
+
return {
|
|
260
|
+
label: "Live",
|
|
261
|
+
variant: "active",
|
|
262
|
+
isActive: true,
|
|
263
|
+
countdown: { targetTimestamp: end, label: "Ends" }
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (submissionEnd && currentTimestamp >= end && currentTimestamp < submissionEnd) {
|
|
267
|
+
return {
|
|
268
|
+
label: "Submission",
|
|
269
|
+
variant: "submission",
|
|
270
|
+
isActive: true,
|
|
271
|
+
countdown: { targetTimestamp: submissionEnd, label: "Submit" }
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
label: "Ended",
|
|
276
|
+
variant: "ended",
|
|
277
|
+
isActive: false,
|
|
278
|
+
countdown: null
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// src/utils/time.ts
|
|
283
|
+
function isBefore(date1, date2) {
|
|
284
|
+
return date1.getTime() < date2.getTime();
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/utils/entryFee.ts
|
|
288
|
+
function calculateEntryFeeBreakdown(feePerEntry, entryCount, shares) {
|
|
289
|
+
const totalCollected = feePerEntry * BigInt(entryCount);
|
|
290
|
+
if (totalCollected === 0n) {
|
|
291
|
+
return {
|
|
292
|
+
totalCollected: 0n,
|
|
293
|
+
creatorAmount: 0n,
|
|
294
|
+
gameCreatorAmount: 0n,
|
|
295
|
+
refundAmount: 0n,
|
|
296
|
+
prizePoolAmount: 0n
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
const creatorAmount = shares.creatorShare > 0 ? totalCollected * BigInt(shares.creatorShare) / 10000n : 0n;
|
|
300
|
+
const gameCreatorAmount = shares.gameCreatorShare > 0 ? totalCollected * BigInt(shares.gameCreatorShare) / 10000n : 0n;
|
|
301
|
+
const refundAmount = shares.refundShare > 0 ? totalCollected * BigInt(shares.refundShare) / 10000n : 0n;
|
|
302
|
+
const prizePoolBps = 1e4 - shares.creatorShare - shares.gameCreatorShare - shares.refundShare;
|
|
303
|
+
const prizePoolAmount = prizePoolBps > 0 ? totalCollected * BigInt(prizePoolBps) / 10000n : 0n;
|
|
304
|
+
return {
|
|
305
|
+
totalCollected,
|
|
306
|
+
creatorAmount,
|
|
307
|
+
gameCreatorAmount,
|
|
308
|
+
refundAmount,
|
|
309
|
+
prizePoolAmount
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function distributePool(prizePoolAmount, percentages) {
|
|
313
|
+
return percentages.map((pct, index) => ({
|
|
314
|
+
position: index + 1,
|
|
315
|
+
amount: prizePoolAmount * BigInt(Math.floor(pct * 100)) / 10000n
|
|
316
|
+
})).filter((entry) => entry.amount > 0n);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// src/utils/prizeAggregation.ts
|
|
320
|
+
function aggregatePrizesByPosition(prizes) {
|
|
321
|
+
const positionMap = /* @__PURE__ */ new Map();
|
|
322
|
+
for (const prize of prizes) {
|
|
323
|
+
if (!positionMap.has(prize.position)) {
|
|
324
|
+
positionMap.set(prize.position, {
|
|
325
|
+
erc20Map: /* @__PURE__ */ new Map(),
|
|
326
|
+
erc721Map: /* @__PURE__ */ new Map()
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const group = positionMap.get(prize.position);
|
|
330
|
+
if (prize.tokenType === "erc20") {
|
|
331
|
+
const current = group.erc20Map.get(prize.tokenAddress) ?? 0n;
|
|
332
|
+
group.erc20Map.set(prize.tokenAddress, current + BigInt(prize.amount));
|
|
333
|
+
} else {
|
|
334
|
+
const ids = group.erc721Map.get(prize.tokenAddress) ?? [];
|
|
335
|
+
ids.push(prize.id);
|
|
336
|
+
group.erc721Map.set(prize.tokenAddress, ids);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return Array.from(positionMap.entries()).sort(([a], [b]) => a - b).map(([position, { erc20Map, erc721Map }]) => ({
|
|
340
|
+
position,
|
|
341
|
+
erc20: Array.from(erc20Map.entries()).map(([tokenAddress, totalAmount]) => ({
|
|
342
|
+
tokenAddress,
|
|
343
|
+
totalAmount
|
|
344
|
+
})),
|
|
345
|
+
erc721: Array.from(erc721Map.entries()).map(([tokenAddress, tokenIds]) => ({
|
|
346
|
+
tokenAddress,
|
|
347
|
+
tokenIds
|
|
348
|
+
}))
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
351
|
+
function aggregatePrizesBySponsor(prizes) {
|
|
352
|
+
const sponsorMap = /* @__PURE__ */ new Map();
|
|
353
|
+
for (const prize of prizes) {
|
|
354
|
+
if (!prize.sponsorAddress || prize.sponsorAddress === "0x0") continue;
|
|
355
|
+
if (!sponsorMap.has(prize.sponsorAddress)) {
|
|
356
|
+
sponsorMap.set(prize.sponsorAddress, {
|
|
357
|
+
erc20Map: /* @__PURE__ */ new Map(),
|
|
358
|
+
nftMap: /* @__PURE__ */ new Map(),
|
|
359
|
+
nftCount: 0,
|
|
360
|
+
totalCount: 0
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
const sponsor = sponsorMap.get(prize.sponsorAddress);
|
|
364
|
+
sponsor.totalCount++;
|
|
365
|
+
if (prize.tokenType === "erc20") {
|
|
366
|
+
const current = sponsor.erc20Map.get(prize.tokenAddress) ?? {
|
|
367
|
+
amount: 0n,
|
|
368
|
+
count: 0
|
|
369
|
+
};
|
|
370
|
+
sponsor.erc20Map.set(prize.tokenAddress, {
|
|
371
|
+
amount: current.amount + BigInt(prize.amount),
|
|
372
|
+
count: current.count + 1
|
|
373
|
+
});
|
|
374
|
+
} else {
|
|
375
|
+
sponsor.nftCount++;
|
|
376
|
+
const ids = sponsor.nftMap.get(prize.tokenAddress) ?? [];
|
|
377
|
+
ids.push(prize.amount);
|
|
378
|
+
sponsor.nftMap.set(prize.tokenAddress, ids);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return Array.from(sponsorMap.entries()).map(([sponsorAddress, { erc20Map, nftMap, nftCount, totalCount }]) => ({
|
|
382
|
+
sponsorAddress,
|
|
383
|
+
tokens: Array.from(erc20Map.entries()).map(
|
|
384
|
+
([tokenAddress, { amount, count }]) => ({
|
|
385
|
+
tokenAddress,
|
|
386
|
+
totalAmount: amount,
|
|
387
|
+
prizeCount: count
|
|
388
|
+
})
|
|
389
|
+
),
|
|
390
|
+
nftCollections: Array.from(nftMap.entries()).map(
|
|
391
|
+
([tokenAddress, tokenIds]) => ({
|
|
392
|
+
tokenAddress,
|
|
393
|
+
tokenIds
|
|
394
|
+
})
|
|
395
|
+
),
|
|
396
|
+
nftCount,
|
|
397
|
+
totalPrizeCount: totalCount
|
|
398
|
+
})).sort((a, b) => b.totalPrizeCount - a.totalPrizeCount);
|
|
399
|
+
}
|
|
400
|
+
function filterClaimablePrizes(prizes, claimedIds) {
|
|
401
|
+
return prizes.filter((prize) => !claimedIds.has(prize.id));
|
|
402
|
+
}
|
|
403
|
+
function filterZeroPrizes(prizes) {
|
|
404
|
+
return prizes.filter((prize) => {
|
|
405
|
+
if (prize.tokenType === "erc20") {
|
|
406
|
+
return BigInt(prize.amount) > 0n;
|
|
407
|
+
}
|
|
408
|
+
return true;
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// src/utils/nftInput.ts
|
|
413
|
+
function parseNFTBulkInput(input) {
|
|
414
|
+
const trimmed = input.trim();
|
|
415
|
+
if (!trimmed) return { entries: [], errors: [] };
|
|
416
|
+
if (trimmed.startsWith("[")) {
|
|
417
|
+
return parseJsonFormat(trimmed);
|
|
418
|
+
}
|
|
419
|
+
return parseKeyValueFormat(trimmed);
|
|
420
|
+
}
|
|
421
|
+
function parseJsonFormat(input) {
|
|
422
|
+
const errors = [];
|
|
423
|
+
try {
|
|
424
|
+
const parsed = JSON.parse(input);
|
|
425
|
+
if (!Array.isArray(parsed)) {
|
|
426
|
+
return { entries: [], errors: ["Expected a JSON array"] };
|
|
427
|
+
}
|
|
428
|
+
const entries = [];
|
|
429
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
430
|
+
const item = parsed[i];
|
|
431
|
+
const tokenId = item.tokenId ?? item.token_id ?? item.id;
|
|
432
|
+
const position = item.position ?? item.pos;
|
|
433
|
+
if (tokenId == null || position == null) {
|
|
434
|
+
errors.push(
|
|
435
|
+
`Item ${i + 1}: missing tokenId or position`
|
|
436
|
+
);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const posNum = Number(position);
|
|
440
|
+
if (!Number.isFinite(posNum) || posNum < 1) {
|
|
441
|
+
errors.push(`Item ${i + 1}: position must be >= 1`);
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
entries.push({ tokenId: String(tokenId), position: posNum });
|
|
445
|
+
}
|
|
446
|
+
return { entries, errors };
|
|
447
|
+
} catch {
|
|
448
|
+
return { entries: [], errors: ["Invalid JSON format"] };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function parseKeyValueFormat(input) {
|
|
452
|
+
const errors = [];
|
|
453
|
+
const entries = [];
|
|
454
|
+
const lines = input.split(/[\n,]/).map((s) => s.trim()).filter((s) => s.length > 0);
|
|
455
|
+
for (let i = 0; i < lines.length; i++) {
|
|
456
|
+
const line = lines[i];
|
|
457
|
+
const parts = line.split(":").map((s) => s.trim());
|
|
458
|
+
if (parts.length !== 2) {
|
|
459
|
+
errors.push(`Line ${i + 1}: expected "tokenId:position" format`);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
const [tokenIdStr, posStr] = parts;
|
|
463
|
+
const position = Number(posStr);
|
|
464
|
+
if (!tokenIdStr) {
|
|
465
|
+
errors.push(`Line ${i + 1}: missing tokenId`);
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (!Number.isFinite(position) || position < 1) {
|
|
469
|
+
errors.push(`Line ${i + 1}: position must be >= 1`);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
entries.push({ tokenId: tokenIdStr, position });
|
|
473
|
+
}
|
|
474
|
+
return { entries, errors };
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// src/utils/extensions.ts
|
|
478
|
+
var EXTENSION_ADDRESSES = {
|
|
479
|
+
SN_SEPOLIA: {
|
|
480
|
+
tournamentValidator: "0x04c9ba290bbebd47f87141801623748884af610230cdaf69232007b4135be3f1",
|
|
481
|
+
erc20BalanceValidator: "0x05437210fd8231893da2317971983ae1f810d8d500344ad09e5b258b5820a908",
|
|
482
|
+
opusTrovesValidator: "0x0317f96eeff41d1badffd9bda126d36c1806523d8c91a5b440f9bdf2aa2b9fe7",
|
|
483
|
+
snapshotValidator: "0x02628045f0688663ecacbaefdbbc46c2e315b2da39271419e8a3443fe4120f61",
|
|
484
|
+
zkPassportValidator: "0x025b65137c5297e03491830635f4c5d76eb17e5b2db3bf49dbee34bba94ad3e6",
|
|
485
|
+
governanceValidator: "0x0585e6b1aa8daac7711004a92a8d0d3b5a81eaac8c9a07db24fc34b1a4f2322c"
|
|
486
|
+
},
|
|
487
|
+
SN_MAIN: {
|
|
488
|
+
tournamentValidator: "0x0771b57c0709fc4407ff8b63d573f302b96fb03638364032fad734e3c310b9e0",
|
|
489
|
+
erc20BalanceValidator: "0x051fc2681f65ee18e99dab3cc2ca2eca1b4532c735e752f575ace91ed30f17b7",
|
|
490
|
+
opusTrovesValidator: "0x0604bc0d54727f439786c65d7dc6f7d46de1aba7e129ef9caf65fef111a1644e",
|
|
491
|
+
snapshotValidator: "0x03e6820e9e1cfb5c22465a86f469c651355f05397e29fc94de8e832d5f3d8ede",
|
|
492
|
+
zkPassportValidator: "0x01a25f04c8a39da7bb37adcc53d595fde9e60702611d15b1c498df0657001479",
|
|
493
|
+
governanceValidator: ""
|
|
494
|
+
// placeholder until deployed
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
var OPUS_SUPPORTED_ASSETS = {
|
|
498
|
+
SN_SEPOLIA: [],
|
|
499
|
+
SN_MAIN: [
|
|
500
|
+
"0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
|
|
501
|
+
// ETH
|
|
502
|
+
"0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
|
|
503
|
+
// STRK
|
|
504
|
+
"0x0124aeb495b947201f5fac96fd1138e326ad86195b98df6dec9009158a533b49",
|
|
505
|
+
// LORDS
|
|
506
|
+
"0x075afe6402ad5a5c20dd25e10ec3b3986acaa647b77e4ae24b0cbc9a54a27a87",
|
|
507
|
+
// EKUBO
|
|
508
|
+
"0x03fe2b97c1fd336e750087d68b9b867997fd64a2661ff3ca5a7c771641e8e7ac"
|
|
509
|
+
// WBTC
|
|
510
|
+
]
|
|
511
|
+
};
|
|
512
|
+
function getExtensionAddresses(chainId) {
|
|
513
|
+
return EXTENSION_ADDRESSES[chainId] ?? {};
|
|
514
|
+
}
|
|
515
|
+
function getOpusSupportedAssets(chainId) {
|
|
516
|
+
return OPUS_SUPPORTED_ASSETS[chainId] ?? [];
|
|
517
|
+
}
|
|
518
|
+
function getWhitelistedExtensionAddresses(chainId) {
|
|
519
|
+
const addresses = EXTENSION_ADDRESSES[chainId] ?? {};
|
|
520
|
+
const set = /* @__PURE__ */ new Set();
|
|
521
|
+
for (const addr of Object.values(addresses)) {
|
|
522
|
+
if (addr) {
|
|
523
|
+
set.add(normalizeAddr(addr));
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return set;
|
|
527
|
+
}
|
|
528
|
+
function isWhitelistedExtension(extensionAddress, chainId) {
|
|
529
|
+
const whitelist = getWhitelistedExtensionAddresses(chainId);
|
|
530
|
+
return whitelist.has(normalizeAddr(extensionAddress));
|
|
531
|
+
}
|
|
532
|
+
function normalizeAddr(address) {
|
|
533
|
+
if (!address) return "";
|
|
534
|
+
const hex = address.toLowerCase().replace(/^0x0*/, "");
|
|
535
|
+
return hex;
|
|
536
|
+
}
|
|
537
|
+
function identifyExtensionType(extensionAddress, chainId) {
|
|
538
|
+
const addresses = getExtensionAddresses(chainId);
|
|
539
|
+
const normalized = normalizeAddr(extensionAddress);
|
|
540
|
+
if (addresses.tournamentValidator && normalizeAddr(addresses.tournamentValidator) === normalized) {
|
|
541
|
+
return "tournament";
|
|
542
|
+
}
|
|
543
|
+
if (addresses.erc20BalanceValidator && normalizeAddr(addresses.erc20BalanceValidator) === normalized) {
|
|
544
|
+
return "erc20Balance";
|
|
545
|
+
}
|
|
546
|
+
if (addresses.opusTrovesValidator && normalizeAddr(addresses.opusTrovesValidator) === normalized) {
|
|
547
|
+
return "opusTroves";
|
|
548
|
+
}
|
|
549
|
+
if (addresses.snapshotValidator && normalizeAddr(addresses.snapshotValidator) === normalized) {
|
|
550
|
+
return "snapshot";
|
|
551
|
+
}
|
|
552
|
+
if (addresses.zkPassportValidator && normalizeAddr(addresses.zkPassportValidator) === normalized) {
|
|
553
|
+
return "zkPassport";
|
|
554
|
+
}
|
|
555
|
+
if (addresses.governanceValidator && normalizeAddr(addresses.governanceValidator) === normalized) {
|
|
556
|
+
return "governance";
|
|
557
|
+
}
|
|
558
|
+
return "unknown";
|
|
559
|
+
}
|
|
560
|
+
function parseTournamentValidatorConfig(config) {
|
|
561
|
+
if (!config || config.length < 3) return null;
|
|
562
|
+
return {
|
|
563
|
+
requirementType: String(config[0]) === "1" ? "won" : "participated",
|
|
564
|
+
qualifyingMode: Number(config[1]),
|
|
565
|
+
topPositions: Number(config[2]),
|
|
566
|
+
tournamentIds: config.slice(3).map((id) => String(BigInt(id)))
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function parseERC20BalanceValidatorConfig(config) {
|
|
570
|
+
if (!config || config.length < 8) return null;
|
|
571
|
+
const tokenAddress = String(config[0]);
|
|
572
|
+
const minThreshold = BigInt(config[2]) << 128n | BigInt(config[1]);
|
|
573
|
+
const maxThreshold = BigInt(config[4]) << 128n | BigInt(config[3]);
|
|
574
|
+
const valuePerEntry = BigInt(config[6]) << 128n | BigInt(config[5]);
|
|
575
|
+
const maxEntries = Number(config[7]);
|
|
576
|
+
return {
|
|
577
|
+
tokenAddress,
|
|
578
|
+
minThreshold,
|
|
579
|
+
maxThreshold,
|
|
580
|
+
valuePerEntry,
|
|
581
|
+
maxEntries
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function parseOpusTrovesValidatorConfig(config) {
|
|
585
|
+
if (!config || config.length < 4) return null;
|
|
586
|
+
const assetCount = Number(config[0]);
|
|
587
|
+
const assetAddresses = config.slice(1, assetCount + 1).map(String);
|
|
588
|
+
const threshold = BigInt(config[assetCount + 1] ?? "0");
|
|
589
|
+
const valuePerEntry = BigInt(config[assetCount + 2] ?? "0");
|
|
590
|
+
const maxEntries = Number(config[assetCount + 3] ?? "0");
|
|
591
|
+
return {
|
|
592
|
+
assetCount,
|
|
593
|
+
assetAddresses,
|
|
594
|
+
threshold,
|
|
595
|
+
valuePerEntry,
|
|
596
|
+
maxEntries
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function parseSnapshotValidatorConfig(config) {
|
|
600
|
+
if (!config || config.length === 0) return null;
|
|
601
|
+
return {
|
|
602
|
+
snapshotId: String(Number(BigInt(config[0] ?? "0")))
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function parseZkPassportValidatorConfig(config) {
|
|
606
|
+
if (!config || config.length < 6) return null;
|
|
607
|
+
return {
|
|
608
|
+
verifierAddress: String(config[0]),
|
|
609
|
+
serviceScope: BigInt(config[1]),
|
|
610
|
+
subscope: BigInt(config[2]),
|
|
611
|
+
paramCommitment: BigInt(config[3]),
|
|
612
|
+
maxProofAge: Number(config[4]),
|
|
613
|
+
nullifierType: Number(config[5])
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function parseGovernanceValidatorConfig(config) {
|
|
617
|
+
if (!config || config.length < 11) return null;
|
|
618
|
+
return {
|
|
619
|
+
governorAddress: String(config[0]),
|
|
620
|
+
governanceTokenAddress: String(config[1]),
|
|
621
|
+
balanceThreshold: BigInt(config[3]) << 128n | BigInt(config[2]),
|
|
622
|
+
proposalId: BigInt(config[5]) << 128n | BigInt(config[4]),
|
|
623
|
+
checkVoted: String(config[6]) === "1" || BigInt(config[6]) === 1n,
|
|
624
|
+
votesThreshold: BigInt(config[8]) << 128n | BigInt(config[7]),
|
|
625
|
+
votesPerEntry: BigInt(config[10]) << 128n | BigInt(config[9])
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function parseExtensionConfig(extensionAddress, config, chainId) {
|
|
629
|
+
const extensionType = identifyExtensionType(extensionAddress, chainId);
|
|
630
|
+
switch (extensionType) {
|
|
631
|
+
case "tournament": {
|
|
632
|
+
const parsed = parseTournamentValidatorConfig(config);
|
|
633
|
+
return parsed ? { type: "tournament", config: parsed } : null;
|
|
634
|
+
}
|
|
635
|
+
case "erc20Balance": {
|
|
636
|
+
const parsed = parseERC20BalanceValidatorConfig(config);
|
|
637
|
+
return parsed ? { type: "erc20Balance", config: parsed } : null;
|
|
638
|
+
}
|
|
639
|
+
case "opusTroves": {
|
|
640
|
+
const parsed = parseOpusTrovesValidatorConfig(config);
|
|
641
|
+
return parsed ? { type: "opusTroves", config: parsed } : null;
|
|
642
|
+
}
|
|
643
|
+
case "snapshot": {
|
|
644
|
+
const parsed = parseSnapshotValidatorConfig(config);
|
|
645
|
+
return parsed ? { type: "snapshot", config: parsed } : null;
|
|
646
|
+
}
|
|
647
|
+
case "zkPassport": {
|
|
648
|
+
const parsed = parseZkPassportValidatorConfig(config);
|
|
649
|
+
return parsed ? { type: "zkPassport", config: parsed } : null;
|
|
650
|
+
}
|
|
651
|
+
case "governance": {
|
|
652
|
+
const parsed = parseGovernanceValidatorConfig(config);
|
|
653
|
+
return parsed ? { type: "governance", config: parsed } : null;
|
|
654
|
+
}
|
|
655
|
+
default:
|
|
656
|
+
return { type: "unknown", config: null };
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function getQualifyingModeInfo(mode) {
|
|
660
|
+
switch (mode) {
|
|
661
|
+
case 0:
|
|
662
|
+
return {
|
|
663
|
+
label: "At Least One",
|
|
664
|
+
description: "Qualify in at least one tournament"
|
|
665
|
+
};
|
|
666
|
+
case 1:
|
|
667
|
+
return {
|
|
668
|
+
label: "Cumulative per Tournament",
|
|
669
|
+
description: "Track entry limits separately for each tournament"
|
|
670
|
+
};
|
|
671
|
+
case 2:
|
|
672
|
+
return {
|
|
673
|
+
label: "All",
|
|
674
|
+
description: "Must qualify in all tournaments"
|
|
675
|
+
};
|
|
676
|
+
case 3:
|
|
677
|
+
return {
|
|
678
|
+
label: "Cumulative per Entry",
|
|
679
|
+
description: "Track entries per qualifying token ID"
|
|
680
|
+
};
|
|
681
|
+
case 4:
|
|
682
|
+
return {
|
|
683
|
+
label: "All Participate, Any Win",
|
|
684
|
+
description: "Must participate in all tournaments, but only need to win in any one"
|
|
685
|
+
};
|
|
686
|
+
case 5:
|
|
687
|
+
return {
|
|
688
|
+
label: "All With Cumulative",
|
|
689
|
+
description: "Must participate in all tournaments, entries multiply by tournament count"
|
|
690
|
+
};
|
|
691
|
+
default:
|
|
692
|
+
return { label: "Unknown", description: "" };
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
function formatTokenAmount(value, decimals) {
|
|
696
|
+
if (value === 0n) return "0";
|
|
697
|
+
const divisor = 10n ** BigInt(decimals);
|
|
698
|
+
const integerPart = value / divisor;
|
|
699
|
+
const remainder = value % divisor;
|
|
700
|
+
if (remainder === 0n) return integerPart.toString();
|
|
701
|
+
const decimalStr = remainder.toString().padStart(decimals, "0");
|
|
702
|
+
const trimmed = decimalStr.replace(/0+$/, "");
|
|
703
|
+
return trimmed ? `${integerPart}.${trimmed}` : integerPart.toString();
|
|
704
|
+
}
|
|
705
|
+
function formatCashToUSD(value) {
|
|
706
|
+
return formatTokenAmount(value, 18);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/utils/qualification.ts
|
|
710
|
+
function evaluateTokenQualification(input) {
|
|
711
|
+
if (input.ownedTokenIds.length === 0) {
|
|
712
|
+
return {
|
|
713
|
+
meetsRequirements: false,
|
|
714
|
+
bestProof: null,
|
|
715
|
+
qualifications: [],
|
|
716
|
+
totalEntriesLeft: 0
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
const qualifications = [];
|
|
720
|
+
for (const tokenId of input.ownedTokenIds) {
|
|
721
|
+
const usedCount = input.usedEntryCounts[tokenId] ?? 0;
|
|
722
|
+
const remaining = input.entryLimit > 0 ? input.entryLimit - usedCount : Infinity;
|
|
723
|
+
if (remaining > 0) {
|
|
724
|
+
qualifications.push({
|
|
725
|
+
id: `token-${tokenId}`,
|
|
726
|
+
entriesLeft: remaining,
|
|
727
|
+
proof: { type: "token", tokenId },
|
|
728
|
+
label: `Token #${tokenId}`,
|
|
729
|
+
metadata: { tokenId }
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
const totalEntriesLeft = qualifications.reduce(
|
|
734
|
+
(sum, q) => sum + (q.entriesLeft === Infinity ? 1 : q.entriesLeft),
|
|
735
|
+
0
|
|
736
|
+
);
|
|
737
|
+
const best = qualifications.length > 0 ? qualifications.reduce(
|
|
738
|
+
(a, b) => b.entriesLeft > a.entriesLeft ? b : a
|
|
739
|
+
) : null;
|
|
740
|
+
return {
|
|
741
|
+
meetsRequirements: qualifications.length > 0,
|
|
742
|
+
bestProof: best?.proof ?? null,
|
|
743
|
+
qualifications,
|
|
744
|
+
totalEntriesLeft
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function evaluateExtensionQualification(input) {
|
|
748
|
+
const qualifications = input.checkedQualifications.filter((q) => q.entriesLeft > 0).map((q) => ({
|
|
749
|
+
id: q.id,
|
|
750
|
+
entriesLeft: q.entriesLeft,
|
|
751
|
+
proof: {
|
|
752
|
+
type: "extension",
|
|
753
|
+
extensionProof: q.proof
|
|
754
|
+
},
|
|
755
|
+
label: q.label,
|
|
756
|
+
metadata: q.metadata
|
|
757
|
+
}));
|
|
758
|
+
const totalEntriesLeft = qualifications.reduce(
|
|
759
|
+
(sum, q) => sum + q.entriesLeft,
|
|
760
|
+
0
|
|
761
|
+
);
|
|
762
|
+
const best = qualifications.length > 0 ? qualifications.reduce(
|
|
763
|
+
(a, b) => b.entriesLeft > a.entriesLeft ? b : a
|
|
764
|
+
) : null;
|
|
765
|
+
return {
|
|
766
|
+
meetsRequirements: qualifications.length > 0,
|
|
767
|
+
bestProof: best?.proof ?? null,
|
|
768
|
+
qualifications,
|
|
769
|
+
totalEntriesLeft
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
function evaluateQualification(variant, input) {
|
|
773
|
+
if (variant === "none" || input.type === "none") {
|
|
774
|
+
return {
|
|
775
|
+
meetsRequirements: true,
|
|
776
|
+
bestProof: null,
|
|
777
|
+
qualifications: [
|
|
778
|
+
{
|
|
779
|
+
id: "no-requirement",
|
|
780
|
+
entriesLeft: Infinity,
|
|
781
|
+
proof: { type: "none" }
|
|
782
|
+
}
|
|
783
|
+
],
|
|
784
|
+
totalEntriesLeft: Infinity
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
switch (input.type) {
|
|
788
|
+
case "token":
|
|
789
|
+
return evaluateTokenQualification(input.data);
|
|
790
|
+
case "extension":
|
|
791
|
+
return evaluateExtensionQualification(input.data);
|
|
792
|
+
default:
|
|
793
|
+
return {
|
|
794
|
+
meetsRequirements: false,
|
|
795
|
+
bestProof: null,
|
|
796
|
+
qualifications: [],
|
|
797
|
+
totalEntriesLeft: 0
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
function buildQualificationProof(proof) {
|
|
802
|
+
if (!proof) {
|
|
803
|
+
return new starknet.CairoOption(starknet.CairoOptionVariant.None);
|
|
804
|
+
}
|
|
805
|
+
switch (proof.type) {
|
|
806
|
+
case "token":
|
|
807
|
+
return buildNFTProof(proof.tokenId ?? "0");
|
|
808
|
+
case "extension":
|
|
809
|
+
return buildExtensionProof(proof.extensionProof ?? []);
|
|
810
|
+
case "none":
|
|
811
|
+
return new starknet.CairoOption(starknet.CairoOptionVariant.None);
|
|
812
|
+
default:
|
|
813
|
+
return new starknet.CairoOption(starknet.CairoOptionVariant.None);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
function buildNFTProof(tokenId) {
|
|
817
|
+
return new starknet.CairoOption(
|
|
818
|
+
starknet.CairoOptionVariant.Some,
|
|
819
|
+
new starknet.CairoCustomEnum({
|
|
820
|
+
NFT: {
|
|
821
|
+
token_id: {
|
|
822
|
+
low: tokenId,
|
|
823
|
+
high: "0"
|
|
824
|
+
}
|
|
825
|
+
},
|
|
826
|
+
Extension: void 0
|
|
827
|
+
})
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
function buildTournamentExtensionProof(tournamentId, tokenId, position) {
|
|
831
|
+
return buildExtensionProof([
|
|
832
|
+
tournamentId.toString(),
|
|
833
|
+
tokenId.toString(),
|
|
834
|
+
position.toString()
|
|
835
|
+
]);
|
|
836
|
+
}
|
|
837
|
+
function buildExtensionProof(proofData) {
|
|
838
|
+
return new starknet.CairoOption(
|
|
839
|
+
starknet.CairoOptionVariant.Some,
|
|
840
|
+
new starknet.CairoCustomEnum({
|
|
841
|
+
NFT: void 0,
|
|
842
|
+
Extension: proofData
|
|
843
|
+
})
|
|
844
|
+
);
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// src/utils/tournamentValidator.ts
|
|
848
|
+
function buildParticipationMap(registrations, requiredTournamentIds) {
|
|
849
|
+
const requiredSet = new Set(requiredTournamentIds);
|
|
850
|
+
const map = {};
|
|
851
|
+
for (const reg of registrations) {
|
|
852
|
+
if (!requiredSet.has(reg.tournamentId)) continue;
|
|
853
|
+
if (!map[reg.tournamentId]) map[reg.tournamentId] = [];
|
|
854
|
+
map[reg.tournamentId].push(reg.gameTokenId);
|
|
855
|
+
}
|
|
856
|
+
return map;
|
|
857
|
+
}
|
|
858
|
+
function buildWinMap(leaderboards, ownedGameTokenIds, topPositions = 0) {
|
|
859
|
+
const ownedSet = new Set(ownedGameTokenIds);
|
|
860
|
+
const map = {};
|
|
861
|
+
for (const lb of leaderboards) {
|
|
862
|
+
if (!lb.isFinalized) continue;
|
|
863
|
+
const limit = topPositions > 0 ? topPositions : lb.tokenIds.length;
|
|
864
|
+
for (let i = 0; i < Math.min(limit, lb.tokenIds.length); i++) {
|
|
865
|
+
if (ownedSet.has(lb.tokenIds[i])) {
|
|
866
|
+
if (!map[lb.tournamentId]) map[lb.tournamentId] = [];
|
|
867
|
+
map[lb.tournamentId].push({
|
|
868
|
+
tokenId: lb.tokenIds[i],
|
|
869
|
+
position: i + 1
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
return map;
|
|
875
|
+
}
|
|
876
|
+
function resolveTournamentQualifications(config, participationMap, winMap, tournamentNames) {
|
|
877
|
+
const inputs = [];
|
|
878
|
+
if (config.requirementType === "won") {
|
|
879
|
+
for (const tournamentId of config.tournamentIds) {
|
|
880
|
+
const wonEntries = winMap[tournamentId];
|
|
881
|
+
if (!wonEntries) continue;
|
|
882
|
+
for (const entry of wonEntries) {
|
|
883
|
+
inputs.push({
|
|
884
|
+
tournamentId,
|
|
885
|
+
tokenId: entry.tokenId,
|
|
886
|
+
position: entry.position,
|
|
887
|
+
tournamentName: tournamentNames?.[tournamentId]
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
} else {
|
|
892
|
+
for (const tournamentId of config.tournamentIds) {
|
|
893
|
+
const tokenIds = participationMap[tournamentId];
|
|
894
|
+
if (!tokenIds) continue;
|
|
895
|
+
for (const tokenId of tokenIds) {
|
|
896
|
+
inputs.push({
|
|
897
|
+
tournamentId,
|
|
898
|
+
tokenId,
|
|
899
|
+
position: 1,
|
|
900
|
+
// Participation = position 1
|
|
901
|
+
tournamentName: tournamentNames?.[tournamentId]
|
|
902
|
+
});
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return inputs;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
// src/utils/opusTroves.ts
|
|
910
|
+
function calculateOpusTrovesEntries(debt, config) {
|
|
911
|
+
let allowed = 0;
|
|
912
|
+
if (config.valuePerEntry > 0n) {
|
|
913
|
+
if (debt > config.threshold) {
|
|
914
|
+
allowed = Number((debt - config.threshold) / config.valuePerEntry);
|
|
915
|
+
}
|
|
916
|
+
} else {
|
|
917
|
+
if (debt >= config.threshold && config.maxEntries > 0) {
|
|
918
|
+
allowed = config.maxEntries;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
if (config.maxEntries > 0) {
|
|
922
|
+
allowed = Math.min(allowed, config.maxEntries);
|
|
923
|
+
}
|
|
924
|
+
return allowed;
|
|
925
|
+
}
|
|
926
|
+
function findBannableEntries(registeredTokenIds, entriesAllowed) {
|
|
927
|
+
const bannable = /* @__PURE__ */ new Set();
|
|
928
|
+
const bannableCount = Math.max(0, registeredTokenIds.length - entriesAllowed);
|
|
929
|
+
if (bannableCount > 0) {
|
|
930
|
+
const sorted = [...registeredTokenIds].sort((a, b) => {
|
|
931
|
+
return Number(BigInt(a)) - Number(BigInt(b));
|
|
932
|
+
});
|
|
933
|
+
for (let i = 0; i < bannableCount; i++) {
|
|
934
|
+
bannable.add(sorted[i]);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return bannable;
|
|
938
|
+
}
|
|
939
|
+
function findAllBannableEntries(players, config) {
|
|
940
|
+
const allBannable = /* @__PURE__ */ new Set();
|
|
941
|
+
for (const [, { debt, registeredTokenIds }] of players) {
|
|
942
|
+
const allowed = calculateOpusTrovesEntries(debt, config);
|
|
943
|
+
const bannable = findBannableEntries(registeredTokenIds, allowed);
|
|
944
|
+
for (const id of bannable) {
|
|
945
|
+
allBannable.add(id);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return allBannable;
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// src/utils/prizePool.ts
|
|
952
|
+
function buildEntryFeePrizes(entryFee, entryCount) {
|
|
953
|
+
if (!entryFee.amount || entryCount === 0 || entryFee.distributionCount <= 0) {
|
|
954
|
+
return [];
|
|
955
|
+
}
|
|
956
|
+
const shares = {
|
|
957
|
+
creatorShare: entryFee.tournamentCreatorShare,
|
|
958
|
+
gameCreatorShare: entryFee.gameCreatorShare,
|
|
959
|
+
refundShare: entryFee.refundShare
|
|
960
|
+
};
|
|
961
|
+
const breakdown = calculateEntryFeeBreakdown(
|
|
962
|
+
BigInt(entryFee.amount),
|
|
963
|
+
entryCount,
|
|
964
|
+
shares
|
|
965
|
+
);
|
|
966
|
+
if (breakdown.prizePoolAmount <= 0n) return [];
|
|
967
|
+
const dist = entryFee.distribution;
|
|
968
|
+
let distributionType = "uniform";
|
|
969
|
+
let weight = 10;
|
|
970
|
+
if (dist?.type === "linear" || dist?.Linear !== void 0) {
|
|
971
|
+
distributionType = "linear";
|
|
972
|
+
weight = Number(dist.weight ?? dist.Linear ?? 100) / 10;
|
|
973
|
+
} else if (dist?.type === "exponential" || dist?.Exponential !== void 0) {
|
|
974
|
+
distributionType = "exponential";
|
|
975
|
+
weight = Number(dist.weight ?? dist.Exponential ?? 100) / 10;
|
|
976
|
+
}
|
|
977
|
+
const percentages = calculateDistribution(
|
|
978
|
+
entryFee.distributionCount,
|
|
979
|
+
weight,
|
|
980
|
+
0,
|
|
981
|
+
0,
|
|
982
|
+
0,
|
|
983
|
+
distributionType
|
|
984
|
+
);
|
|
985
|
+
const positions = distributePool(breakdown.prizePoolAmount, percentages);
|
|
986
|
+
return positions.map((pos) => ({
|
|
987
|
+
id: `entry_fee_${pos.position}`,
|
|
988
|
+
position: pos.position,
|
|
989
|
+
tokenAddress: entryFee.tokenAddress,
|
|
990
|
+
tokenType: "erc20",
|
|
991
|
+
amount: pos.amount.toString(),
|
|
992
|
+
sponsorAddress: ""
|
|
993
|
+
}));
|
|
994
|
+
}
|
|
995
|
+
function calculateTotalPrizeValueUSD(prizes, prices, decimals, normalizeAddress = (addr) => addr) {
|
|
996
|
+
let total = 0;
|
|
997
|
+
for (const prize of prizes) {
|
|
998
|
+
if (prize.tokenType !== "erc20") continue;
|
|
999
|
+
const addr = normalizeAddress(prize.tokenAddress);
|
|
1000
|
+
const price = prices[addr];
|
|
1001
|
+
if (price === void 0) continue;
|
|
1002
|
+
const dec = decimals[addr] ?? 18;
|
|
1003
|
+
total += calculatePrizeValue(prize.amount, dec, price);
|
|
1004
|
+
}
|
|
1005
|
+
return total;
|
|
1006
|
+
}
|
|
1007
|
+
function calculatePaidPlaces(sponsoredPrizes, entryFeeDistributionCount) {
|
|
1008
|
+
const maxSponsoredPosition = sponsoredPrizes.reduce(
|
|
1009
|
+
(max, p) => Math.max(max, p.position),
|
|
1010
|
+
0
|
|
1011
|
+
);
|
|
1012
|
+
return Math.max(maxSponsoredPosition, entryFeeDistributionCount);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
exports.QualifyingMode = QualifyingMode;
|
|
1016
|
+
exports.aggregatePrizesByPosition = aggregatePrizesByPosition;
|
|
1017
|
+
exports.aggregatePrizesBySponsor = aggregatePrizesBySponsor;
|
|
1018
|
+
exports.bigintToHex = bigintToHex;
|
|
1019
|
+
exports.buildEntryFeePrizes = buildEntryFeePrizes;
|
|
1020
|
+
exports.buildExtensionProof = buildExtensionProof;
|
|
1021
|
+
exports.buildNFTProof = buildNFTProof;
|
|
1022
|
+
exports.buildParticipationMap = buildParticipationMap;
|
|
1023
|
+
exports.buildQualificationProof = buildQualificationProof;
|
|
1024
|
+
exports.buildTournamentExtensionProof = buildTournamentExtensionProof;
|
|
1025
|
+
exports.buildWinMap = buildWinMap;
|
|
1026
|
+
exports.calculateDistribution = calculateDistribution;
|
|
1027
|
+
exports.calculateEntryFeeBreakdown = calculateEntryFeeBreakdown;
|
|
1028
|
+
exports.calculateOpusTrovesEntries = calculateOpusTrovesEntries;
|
|
1029
|
+
exports.calculatePaidPlaces = calculatePaidPlaces;
|
|
1030
|
+
exports.calculatePayouts = calculatePayouts;
|
|
1031
|
+
exports.calculatePrizeValue = calculatePrizeValue;
|
|
1032
|
+
exports.calculateTotalPrizeValueUSD = calculateTotalPrizeValueUSD;
|
|
1033
|
+
exports.computeStatus = computeStatus;
|
|
1034
|
+
exports.displayAddress = displayAddress;
|
|
1035
|
+
exports.distributePool = distributePool;
|
|
1036
|
+
exports.evaluateExtensionQualification = evaluateExtensionQualification;
|
|
1037
|
+
exports.evaluateQualification = evaluateQualification;
|
|
1038
|
+
exports.evaluateTokenQualification = evaluateTokenQualification;
|
|
1039
|
+
exports.filterClaimablePrizes = filterClaimablePrizes;
|
|
1040
|
+
exports.filterZeroPrizes = filterZeroPrizes;
|
|
1041
|
+
exports.findAllBannableEntries = findAllBannableEntries;
|
|
1042
|
+
exports.findBannableEntries = findBannableEntries;
|
|
1043
|
+
exports.formatCashToUSD = formatCashToUSD;
|
|
1044
|
+
exports.formatNumber = formatNumber;
|
|
1045
|
+
exports.formatPrizeAmount = formatPrizeAmount;
|
|
1046
|
+
exports.formatScore = formatScore;
|
|
1047
|
+
exports.formatTime = formatTime;
|
|
1048
|
+
exports.formatTokenAmount = formatTokenAmount;
|
|
1049
|
+
exports.formatUsdValue = formatUsdValue;
|
|
1050
|
+
exports.getExtensionAddresses = getExtensionAddresses;
|
|
1051
|
+
exports.getOpusSupportedAssets = getOpusSupportedAssets;
|
|
1052
|
+
exports.getOrdinalSuffix = getOrdinalSuffix;
|
|
1053
|
+
exports.getQualifyingModeInfo = getQualifyingModeInfo;
|
|
1054
|
+
exports.getWhitelistedExtensionAddresses = getWhitelistedExtensionAddresses;
|
|
1055
|
+
exports.groupPrizesByToken = groupPrizesByToken;
|
|
1056
|
+
exports.identifyExtensionType = identifyExtensionType;
|
|
1057
|
+
exports.indexAddress = indexAddress;
|
|
1058
|
+
exports.isBefore = isBefore;
|
|
1059
|
+
exports.isWhitelistedExtension = isWhitelistedExtension;
|
|
1060
|
+
exports.padAddress = padAddress;
|
|
1061
|
+
exports.parseERC20BalanceValidatorConfig = parseERC20BalanceValidatorConfig;
|
|
1062
|
+
exports.parseExtensionConfig = parseExtensionConfig;
|
|
1063
|
+
exports.parseGovernanceValidatorConfig = parseGovernanceValidatorConfig;
|
|
1064
|
+
exports.parseNFTBulkInput = parseNFTBulkInput;
|
|
1065
|
+
exports.parseOpusTrovesValidatorConfig = parseOpusTrovesValidatorConfig;
|
|
1066
|
+
exports.parseSnapshotValidatorConfig = parseSnapshotValidatorConfig;
|
|
1067
|
+
exports.parseTournamentValidatorConfig = parseTournamentValidatorConfig;
|
|
1068
|
+
exports.parseZkPassportValidatorConfig = parseZkPassportValidatorConfig;
|
|
1069
|
+
exports.resolveTournamentQualifications = resolveTournamentQualifications;
|
|
1070
|
+
//# sourceMappingURL=index.cjs.map
|
|
1071
|
+
//# sourceMappingURL=index.cjs.map
|