@wkalidev/trivia-quest-sdk 2.0.0 → 3.0.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/sdk/sdk/index.js CHANGED
@@ -1,91 +1,400 @@
1
1
  "use strict";
2
+ // TriviaQuest SDK v2.0.0
3
+ // Blockchain quiz game on Celo & Base
4
+ // https://trivia-quest-eight.vercel.app
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TRIVQ_PER_POINT = exports.REFERRAL_REWARD_TRIVQ = exports.WEEK_BONUS_TRIVQ = exports.DAILY_REWARD_TRIVQ = exports.CELO_RPC = exports.CELO_CHAIN_ID = exports.REFERRAL_ABI = exports.DAILY_CHECKIN_ABI = exports.TRIVIA_QUEST_ABI = exports.TRIVQ_ABI = exports.REFERRAL_ADDRESS = exports.DAILY_CHECKIN_ADDRESS = exports.TRIVIA_QUEST_ADDRESS = exports.TRIVQ_TOKEN_ADDRESS = void 0;
4
- exports.TRIVQ_TOKEN_ADDRESS = "0xe65fc5cacaf9a5aebbc0e151dee08a53f24a05c5";
5
- exports.TRIVIA_QUEST_ADDRESS = "0xffe22d3d1b63866ac9da8ac92fdb9ceddeadb0bb";
6
- exports.DAILY_CHECKIN_ADDRESS = "0x8650e6c477f8ae3933dc6d61d85e65c90cf71828";
7
- exports.REFERRAL_ADDRESS = "0xa0fcd85a25ecb71ca1ea9d63da058c832c27c62e";
8
- exports.TRIVQ_ABI = [
6
+ exports.CATEGORIES = exports.REFERRAL_ABI = exports.CHECKIN_ABI = exports.DuelStatus = exports.TRIVQ_ABI = exports.DUEL_ABI = exports.CONTRACT_ABI = exports.CELO_TESTNET = exports.BASE_MAINNET = exports.CELO_MAINNET = exports.CONTRACT_ADDRESS_TESTNET = exports.CONTRACT_ADDRESS_MAINNET = exports.REFERRAL_ADDRESS_BASE = exports.CHECKIN_ADDRESS_BASE = exports.TRIVQ_TOKEN_ADDRESS_BASE = exports.TRIVIA_QUEST_ADDRESS_BASE = exports.DUEL_ADDRESS_CELO = exports.REFERRAL_ADDRESS_CELO = exports.CHECKIN_ADDRESS_CELO = exports.TRIVQ_TOKEN_ADDRESS_CELO = exports.TRIVIA_QUEST_ADDRESS_CELO = exports.SDK_VERSION = void 0;
7
+ exports.getAddress = getAddress;
8
+ exports.getMultiplier = getMultiplier;
9
+ exports.calculatePoints = calculatePoints;
10
+ exports.getStreakLabel = getStreakLabel;
11
+ exports.getDuelStatusLabel = getDuelStatusLabel;
12
+ exports.formatWager = formatWager;
13
+ exports.getDuelNetPrize = getDuelNetPrize;
14
+ exports.isDuelExpired = isDuelExpired;
15
+ exports.isMiniPay = isMiniPay;
16
+ exports.getMiniPayAccount = getMiniPayAccount;
17
+ exports.formatAddress = formatAddress;
18
+ exports.formatTrivq = formatTrivq;
19
+ exports.formatCelo = formatCelo;
20
+ exports.fetchNetworkStats = fetchNetworkStats;
21
+ exports.getStreakBonus = getStreakBonus;
22
+ exports.getNextStreakMilestone = getNextStreakMilestone;
23
+ exports.formatCountdown = formatCountdown;
24
+ exports.getCategoryById = getCategoryById;
25
+ exports.calculateRewards = calculateRewards;
26
+ exports.SDK_VERSION = "2.0.0";
27
+ // ── Contract Addresses ─────────────────────────────────────
28
+ // Celo Mainnet
29
+ exports.TRIVIA_QUEST_ADDRESS_CELO = "0xffe22d3d1b63866ac9da8ac92fdb9ceddeadb0bb";
30
+ exports.TRIVQ_TOKEN_ADDRESS_CELO = "0xe65fc5cacaf9a5aebbc0e151dee08a53f24a05c5";
31
+ exports.CHECKIN_ADDRESS_CELO = "0x8650e6c477f8ae3933dc6d61d85e65c90cf71828";
32
+ exports.REFERRAL_ADDRESS_CELO = "0xa0fcd85a25ecb71ca1ea9d63da058c832c27c62e";
33
+ exports.DUEL_ADDRESS_CELO = "0xee7be00cd5454b9bea56d864d82076b8b5de5ca1";
34
+ // Base Mainnet
35
+ exports.TRIVIA_QUEST_ADDRESS_BASE = "0x1e2c209412ec30915ccf922654f0593faf61fcfb";
36
+ exports.TRIVQ_TOKEN_ADDRESS_BASE = "0x8ecc1dc70f3bc5be941b61b42707eb7dbddb54c3";
37
+ exports.CHECKIN_ADDRESS_BASE = "0x0f19851d5cd905d110c000a7d26d74a2f21f8ff9";
38
+ exports.REFERRAL_ADDRESS_BASE = "0x4fb5285263354e1e75f044c65166ab22c3840074";
39
+ // Legacy exports (backwards compatibility)
40
+ exports.CONTRACT_ADDRESS_MAINNET = exports.TRIVIA_QUEST_ADDRESS_CELO;
41
+ exports.CONTRACT_ADDRESS_TESTNET = "0x50b20728ba0ad803679b5428f267c89aede9a378";
42
+ // ── Network Config ─────────────────────────────────────────
43
+ exports.CELO_MAINNET = {
44
+ id: 42220,
45
+ name: "Celo Mainnet",
46
+ rpcUrl: "https://forno.celo.org",
47
+ explorerUrl: "https://celoscan.io",
48
+ contracts: {
49
+ game: exports.TRIVIA_QUEST_ADDRESS_CELO,
50
+ token: exports.TRIVQ_TOKEN_ADDRESS_CELO,
51
+ checkin: exports.CHECKIN_ADDRESS_CELO,
52
+ referral: exports.REFERRAL_ADDRESS_CELO,
53
+ duel: exports.DUEL_ADDRESS_CELO,
54
+ },
55
+ };
56
+ exports.BASE_MAINNET = {
57
+ id: 8453,
58
+ name: "Base Mainnet",
59
+ rpcUrl: "https://mainnet.base.org",
60
+ explorerUrl: "https://basescan.org",
61
+ contracts: {
62
+ game: exports.TRIVIA_QUEST_ADDRESS_BASE,
63
+ token: exports.TRIVQ_TOKEN_ADDRESS_BASE,
64
+ checkin: exports.CHECKIN_ADDRESS_BASE,
65
+ referral: exports.REFERRAL_ADDRESS_BASE,
66
+ duel: "",
67
+ },
68
+ };
69
+ exports.CELO_TESTNET = {
70
+ id: 11142220,
71
+ name: "Celo Sepolia",
72
+ rpcUrl: "https://forno.celo-sepolia.celo-testnet.org",
73
+ explorerUrl: "https://sepolia.celoscan.io",
74
+ contracts: {
75
+ game: exports.CONTRACT_ADDRESS_TESTNET,
76
+ token: "",
77
+ checkin: "",
78
+ referral: "",
79
+ duel: "0xd9456518d7acbe6bcab494aa5894ce4cdf7c5ad7",
80
+ },
81
+ };
82
+ // ── Helper: get contract address by chainId ────────────────
83
+ function getAddress(chainId, contract) {
84
+ if (chainId === 42220)
85
+ return exports.CELO_MAINNET.contracts[contract];
86
+ if (chainId === 8453)
87
+ return exports.BASE_MAINNET.contracts[contract];
88
+ return exports.CELO_TESTNET.contracts[contract];
89
+ }
90
+ // ── TriviaQuest ABI ────────────────────────────────────────
91
+ exports.CONTRACT_ABI = [
9
92
  {
10
- name: "balanceOf",
93
+ name: "joinRound",
94
+ type: "function",
95
+ stateMutability: "payable",
96
+ inputs: [],
97
+ outputs: [],
98
+ },
99
+ {
100
+ name: "getCurrentRound",
11
101
  type: "function",
12
102
  stateMutability: "view",
13
- inputs: [{ name: "account", type: "address" }],
14
- outputs: [{ name: "", type: "uint256" }],
103
+ inputs: [],
104
+ outputs: [
105
+ {
106
+ type: "tuple",
107
+ components: [
108
+ { name: "id", type: "uint256" },
109
+ { name: "prizePool", type: "uint256" },
110
+ { name: "startTime", type: "uint256" },
111
+ { name: "endTime", type: "uint256" },
112
+ { name: "topWinners", type: "address[]" },
113
+ { name: "finished", type: "bool" },
114
+ ],
115
+ },
116
+ ],
117
+ },
118
+ {
119
+ name: "entryFee",
120
+ type: "function",
121
+ stateMutability: "view",
122
+ inputs: [],
123
+ outputs: [{ type: "uint256" }],
124
+ },
125
+ {
126
+ name: "getLeaderboard",
127
+ type: "function",
128
+ stateMutability: "view",
129
+ inputs: [],
130
+ outputs: [
131
+ {
132
+ type: "tuple[]",
133
+ components: [
134
+ { name: "player", type: "address" },
135
+ { name: "totalPoints", type: "uint256" },
136
+ { name: "bestScore", type: "uint256" },
137
+ { name: "gamesPlayed", type: "uint256" },
138
+ ],
139
+ },
140
+ ],
141
+ },
142
+ {
143
+ name: "getPlayerStats",
144
+ type: "function",
145
+ stateMutability: "view",
146
+ inputs: [{ name: "player", type: "address" }],
147
+ outputs: [
148
+ {
149
+ type: "tuple",
150
+ components: [
151
+ { name: "score", type: "uint256" },
152
+ { name: "totalWinnings", type: "uint256" },
153
+ { name: "totalPoints", type: "uint256" },
154
+ { name: "gamesPlayed", type: "uint256" },
155
+ { name: "bestScore", type: "uint256" },
156
+ { name: "exists", type: "bool" },
157
+ ],
158
+ },
159
+ ],
160
+ },
161
+ {
162
+ name: "getTotalPlayers",
163
+ type: "function",
164
+ stateMutability: "view",
165
+ inputs: [],
166
+ outputs: [{ type: "uint256" }],
15
167
  },
16
168
  {
17
- name: "mintReward",
169
+ name: "submitScore",
18
170
  type: "function",
19
171
  stateMutability: "nonpayable",
20
172
  inputs: [
21
173
  { name: "player", type: "address" },
22
- { name: "amount", type: "uint256" },
174
+ { name: "score", type: "uint256" },
175
+ { name: "points", type: "uint256" },
23
176
  ],
24
177
  outputs: [],
25
178
  },
179
+ ];
180
+ // ── TriviaDuel ABI ─────────────────────────────────────────
181
+ exports.DUEL_ABI = [
26
182
  {
27
- name: "rewardsRemaining",
183
+ name: "createDuel",
28
184
  type: "function",
29
- stateMutability: "view",
185
+ stateMutability: "payable",
30
186
  inputs: [],
31
187
  outputs: [{ type: "uint256" }],
32
188
  },
33
- ];
34
- exports.TRIVIA_QUEST_ABI = [
35
189
  {
36
- name: "joinRound",
190
+ name: "joinDuel",
37
191
  type: "function",
38
192
  stateMutability: "payable",
39
- inputs: [],
193
+ inputs: [{ name: "duelId", type: "uint256" }],
40
194
  outputs: [],
41
195
  },
42
196
  {
43
- name: "getCurrentRound",
197
+ name: "cancelExpiredDuel",
198
+ type: "function",
199
+ stateMutability: "nonpayable",
200
+ inputs: [{ name: "duelId", type: "uint256" }],
201
+ outputs: [],
202
+ },
203
+ {
204
+ name: "getDuel",
44
205
  type: "function",
45
206
  stateMutability: "view",
46
- inputs: [],
207
+ inputs: [{ name: "duelId", type: "uint256" }],
47
208
  outputs: [
48
- { name: "id", type: "uint256" },
49
- { name: "prizePool", type: "uint256" },
50
- { name: "startTime", type: "uint256" },
51
- { name: "endTime", type: "uint256" },
52
- { name: "topWinners", type: "address[]" },
53
- { name: "finished", type: "bool" },
209
+ {
210
+ type: "tuple",
211
+ components: [
212
+ { name: "id", type: "uint256" },
213
+ { name: "playerA", type: "address" },
214
+ { name: "playerB", type: "address" },
215
+ { name: "wager", type: "uint256" },
216
+ { name: "scoreA", type: "uint256" },
217
+ { name: "scoreB", type: "uint256" },
218
+ { name: "scoreASubmitted", type: "bool" },
219
+ { name: "scoreBSubmitted", type: "bool" },
220
+ { name: "winner", type: "address" },
221
+ { name: "status", type: "uint8" },
222
+ { name: "createdAt", type: "uint256" },
223
+ { name: "expiresAt", type: "uint256" },
224
+ ],
225
+ },
54
226
  ],
55
227
  },
56
228
  {
57
- name: "getLeaderboard",
229
+ name: "getOpenDuels",
58
230
  type: "function",
59
231
  stateMutability: "view",
60
- inputs: [],
232
+ inputs: [{ name: "limit", type: "uint256" }],
61
233
  outputs: [
62
234
  {
63
235
  type: "tuple[]",
64
236
  components: [
65
- { name: "player", type: "address" },
66
- { name: "totalPoints", type: "uint256" },
67
- { name: "bestScore", type: "uint256" },
68
- { name: "gamesPlayed", type: "uint256" },
237
+ { name: "id", type: "uint256" },
238
+ { name: "playerA", type: "address" },
239
+ { name: "playerB", type: "address" },
240
+ { name: "wager", type: "uint256" },
241
+ { name: "scoreA", type: "uint256" },
242
+ { name: "scoreB", type: "uint256" },
243
+ { name: "scoreASubmitted", type: "bool" },
244
+ { name: "scoreBSubmitted", type: "bool" },
245
+ { name: "winner", type: "address" },
246
+ { name: "status", type: "uint8" },
247
+ { name: "createdAt", type: "uint256" },
248
+ { name: "expiresAt", type: "uint256" },
69
249
  ],
70
250
  },
71
251
  ],
72
252
  },
73
253
  {
74
- name: "getTotalPlayers",
254
+ name: "getPlayerDuels",
255
+ type: "function",
256
+ stateMutability: "view",
257
+ inputs: [{ name: "player", type: "address" }],
258
+ outputs: [{ type: "uint256[]" }],
259
+ },
260
+ {
261
+ name: "duelCounter",
75
262
  type: "function",
76
263
  stateMutability: "view",
77
264
  inputs: [],
78
265
  outputs: [{ type: "uint256" }],
79
266
  },
267
+ ];
268
+ // ── TRIVQ Token ABI ────────────────────────────────────────
269
+ exports.TRIVQ_ABI = [
80
270
  {
81
- name: "entryFee",
271
+ name: "balanceOf",
272
+ type: "function",
273
+ stateMutability: "view",
274
+ inputs: [{ name: "account", type: "address" }],
275
+ outputs: [{ type: "uint256" }],
276
+ },
277
+ {
278
+ name: "transfer",
279
+ type: "function",
280
+ stateMutability: "nonpayable",
281
+ inputs: [
282
+ { name: "to", type: "address" },
283
+ { name: "amount", type: "uint256" },
284
+ ],
285
+ outputs: [{ type: "bool" }],
286
+ },
287
+ {
288
+ name: "totalSupply",
82
289
  type: "function",
83
290
  stateMutability: "view",
84
291
  inputs: [],
85
292
  outputs: [{ type: "uint256" }],
86
293
  },
294
+ {
295
+ name: "decimals",
296
+ type: "function",
297
+ stateMutability: "view",
298
+ inputs: [],
299
+ outputs: [{ type: "uint8" }],
300
+ },
87
301
  ];
88
- exports.DAILY_CHECKIN_ABI = [
302
+ var DuelStatus;
303
+ (function (DuelStatus) {
304
+ DuelStatus[DuelStatus["Open"] = 0] = "Open";
305
+ DuelStatus[DuelStatus["Active"] = 1] = "Active";
306
+ DuelStatus[DuelStatus["Finished"] = 2] = "Finished";
307
+ DuelStatus[DuelStatus["Cancelled"] = 3] = "Cancelled";
308
+ })(DuelStatus || (exports.DuelStatus = DuelStatus = {}));
309
+ // ── Score & Streak Utils ───────────────────────────────────
310
+ function getMultiplier(streak) {
311
+ if (streak >= 5)
312
+ return 3;
313
+ if (streak >= 3)
314
+ return 2;
315
+ return 1;
316
+ }
317
+ function calculatePoints(correct, streak) {
318
+ if (!correct)
319
+ return 0;
320
+ return 100 * getMultiplier(streak + 1);
321
+ }
322
+ function getStreakLabel(streak) {
323
+ if (streak >= 5)
324
+ return "🔥🔥🔥 x3 MEGA";
325
+ if (streak >= 3)
326
+ return "🔥🔥 x2 HOT";
327
+ if (streak >= 1)
328
+ return "🔥 Streak";
329
+ return "";
330
+ }
331
+ // ── Duel Utils ─────────────────────────────────────────────
332
+ function getDuelStatusLabel(status) {
333
+ switch (status) {
334
+ case DuelStatus.Open: return "Open";
335
+ case DuelStatus.Active: return "Active";
336
+ case DuelStatus.Finished: return "Finished";
337
+ case DuelStatus.Cancelled: return "Cancelled";
338
+ }
339
+ }
340
+ function formatWager(wager) {
341
+ const n = Number(wager) / 1e18;
342
+ if (n < 0.001)
343
+ return "<0.001 CELO";
344
+ return `${n.toFixed(3)} CELO`;
345
+ }
346
+ function getDuelNetPrize(wager, feeBps = 1000) {
347
+ const total = wager * BigInt(2);
348
+ const fee = (total * BigInt(feeBps)) / BigInt(10000);
349
+ return total - fee;
350
+ }
351
+ function isDuelExpired(expiresAt) {
352
+ return Date.now() / 1000 > Number(expiresAt);
353
+ }
354
+ // ── MiniPay Utils ──────────────────────────────────────────
355
+ function isMiniPay() {
356
+ if (typeof window === "undefined")
357
+ return false;
358
+ return window.navigator.userAgent.includes("MiniPay");
359
+ }
360
+ async function getMiniPayAccount() {
361
+ if (!isMiniPay())
362
+ return null;
363
+ try {
364
+ const ethereum = window.ethereum;
365
+ if (!ethereum)
366
+ return null;
367
+ const accounts = await ethereum.request({ method: "eth_requestAccounts" });
368
+ return accounts[0] ?? null;
369
+ }
370
+ catch {
371
+ return null;
372
+ }
373
+ }
374
+ // ── Format Utils ───────────────────────────────────────────
375
+ function formatAddress(address) {
376
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
377
+ }
378
+ function formatTrivq(raw) {
379
+ const n = Number(raw) / 1e18;
380
+ if (n >= 1000000000)
381
+ return `${(n / 1000000000).toFixed(1)}B`;
382
+ if (n >= 1000000)
383
+ return `${(n / 1000000).toFixed(1)}M`;
384
+ if (n >= 1000)
385
+ return `${(n / 1000).toFixed(1)}K`;
386
+ return n.toFixed(0);
387
+ }
388
+ function formatCelo(wei) {
389
+ const n = Number(wei) / 1e18;
390
+ if (n === 0)
391
+ return "0";
392
+ if (n < 0.001)
393
+ return "<0.001";
394
+ return n.toFixed(3);
395
+ }
396
+ // ── CheckIn ABI ────────────────────────────────────────────
397
+ exports.CHECKIN_ABI = [
89
398
  {
90
399
  name: "checkIn",
91
400
  type: "function",
@@ -107,13 +416,14 @@ exports.DAILY_CHECKIN_ABI = [
107
416
  ],
108
417
  },
109
418
  {
110
- name: "isCheckInAvailable",
419
+ name: "totalCheckIns",
111
420
  type: "function",
112
421
  stateMutability: "view",
113
- inputs: [{ name: "player", type: "address" }],
114
- outputs: [{ type: "bool" }],
422
+ inputs: [],
423
+ outputs: [{ type: "uint256" }],
115
424
  },
116
425
  ];
426
+ // ── Referral ABI ───────────────────────────────────────────
117
427
  exports.REFERRAL_ABI = [
118
428
  {
119
429
  name: "registerReferral",
@@ -123,23 +433,69 @@ exports.REFERRAL_ABI = [
123
433
  outputs: [],
124
434
  },
125
435
  {
126
- name: "getReferralStats",
436
+ name: "getReferrer",
127
437
  type: "function",
128
438
  stateMutability: "view",
129
439
  inputs: [{ name: "user", type: "address" }],
130
- outputs: [
131
- { name: "refs", type: "uint256" },
132
- { name: "earned", type: "uint256" },
133
- { name: "referred", type: "bool" },
134
- { name: "referrer", type: "address" },
135
- ],
440
+ outputs: [{ type: "address" }],
441
+ },
442
+ {
443
+ name: "getReferralCount",
444
+ type: "function",
445
+ stateMutability: "view",
446
+ inputs: [{ name: "referrer", type: "address" }],
447
+ outputs: [{ type: "uint256" }],
136
448
  },
137
449
  ];
138
- // Chain info
139
- exports.CELO_CHAIN_ID = 42220;
140
- exports.CELO_RPC = "https://forno.celo.org";
141
- // Reward rates
142
- exports.DAILY_REWARD_TRIVQ = 100;
143
- exports.WEEK_BONUS_TRIVQ = 2000;
144
- exports.REFERRAL_REWARD_TRIVQ = 500;
145
- exports.TRIVQ_PER_POINT = 100;
450
+ // ── Network Stats Fetcher ──────────────────────────────────
451
+ async function fetchNetworkStats() {
452
+ const res = await fetch("https://trivia-quest-eight.vercel.app/api/stats");
453
+ const data = await res.json();
454
+ return {
455
+ players: data.live_stats?.players ?? 0,
456
+ roundId: data.live_stats?.round_id ?? 0,
457
+ prizePool: data.live_stats?.prize_pool ?? "0",
458
+ totalCheckins: data.live_stats?.total_checkins ?? 0,
459
+ };
460
+ }
461
+ // ── Streak Utils ───────────────────────────────────────────
462
+ function getStreakBonus(streak) {
463
+ return streak > 0 && streak % 7 === 0 ? 2000 : 0;
464
+ }
465
+ function getNextStreakMilestone(streak) {
466
+ return 7 - (streak % 7);
467
+ }
468
+ function formatCountdown(seconds) {
469
+ const h = Math.floor(seconds / 3600);
470
+ const m = Math.floor((seconds % 3600) / 60);
471
+ const s = seconds % 60;
472
+ return `${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
473
+ }
474
+ // ── Category Utils ─────────────────────────────────────────
475
+ exports.CATEGORIES = [
476
+ { id: 1, name: "Africa Explorer", emoji: "🌍", description: "African Geography" },
477
+ { id: 2, name: "Crypto Master", emoji: "⛓", description: "Web3 & Crypto" },
478
+ { id: 3, name: "Culture Keeper", emoji: "📜", description: "History & Culture" },
479
+ { id: 4, name: "Tech Wizard", emoji: "⚡", description: "Science & Tech" },
480
+ { id: 5, name: "Sport Champion", emoji: "🏆", description: "Sports" },
481
+ { id: 6, name: "Trivia Legend", emoji: "✨", description: "General Knowledge" },
482
+ ];
483
+ function getCategoryById(id) {
484
+ return exports.CATEGORIES.find(c => c.id === id) ?? null;
485
+ }
486
+ // ── Reward Calculator ──────────────────────────────────────
487
+ function calculateRewards(params) {
488
+ let trivq = 0;
489
+ let celoWin = BigInt(0);
490
+ if (params.score > 0) {
491
+ trivq += params.score * 100 * getMultiplier(params.streak);
492
+ }
493
+ if (params.isCheckIn) {
494
+ trivq += 100;
495
+ trivq += getStreakBonus(params.streak);
496
+ }
497
+ if (params.isDuelWinner && params.wager) {
498
+ celoWin = getDuelNetPrize(params.wager);
499
+ }
500
+ return { trivq, celoWin };
501
+ }