@passly/passly-sdk 0.1.5 → 0.1.6
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/package.json +1 -1
- package/src/index.js +296 -8
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { ethers } from 'ethers';
|
|
|
13
13
|
* - Historical verification data access
|
|
14
14
|
* - Platform configuration queries
|
|
15
15
|
* - Referral system integration
|
|
16
|
+
* - Leaderboard and ranking system
|
|
16
17
|
*/
|
|
17
18
|
class PasslySDK {
|
|
18
19
|
/**
|
|
@@ -22,6 +23,7 @@ class PasslySDK {
|
|
|
22
23
|
* @param {string} [config.platformsAddress] - Override for the Platforms contract address
|
|
23
24
|
* @param {string} [config.archivesAddress] - Override for the Archives contract address
|
|
24
25
|
* @param {string} [config.rewardsAddress] - Override for the Rewards contract address
|
|
26
|
+
* @param {string} [config.leaderboardAddress] - Override for the Leaderboard contract address
|
|
25
27
|
* @param {ethers.providers.Provider} [config.provider] - Optional ethers provider
|
|
26
28
|
* @param {ethers.Signer} [config.signer] - Optional ethers signer for write operations
|
|
27
29
|
* @param {string} [config.network] - Network name for default addresses
|
|
@@ -50,7 +52,8 @@ class PasslySDK {
|
|
|
50
52
|
passly: config.contractAddress || '0x9a1b12c6E794dE70b1Bd73a74f81eF5A41Fb7Cb3',
|
|
51
53
|
platforms: config.platformsAddress || '0x0f1ff42F6f9C2d8D1C054aF7c03cf5e449f0Bc15',
|
|
52
54
|
archives: config.archivesAddress || '0xa26A0eAE99A6ccD8f82dC6Cfbd35ccCeebf3BcFf',
|
|
53
|
-
rewards: config.rewardsAddress || '0x993d28480D5f9bf457FAb67D58D696e3c5655031'
|
|
55
|
+
rewards: config.rewardsAddress || '0x993d28480D5f9bf457FAb67D58D696e3c5655031',
|
|
56
|
+
leaderboard: config.leaderboardAddress || null // Add when deployed
|
|
54
57
|
};
|
|
55
58
|
|
|
56
59
|
// Contract ABIs
|
|
@@ -93,6 +96,24 @@ class PasslySDK {
|
|
|
93
96
|
"function validateReferralCode(string calldata referralCode) external view returns (bool isValid, uint256 ownerPassportId)",
|
|
94
97
|
"function getUserReferrals(uint256 passportId) external view returns (string[] memory)",
|
|
95
98
|
"function getPointConfig() external view returns (uint256 dailyHolding, uint256 referral, uint256 referee)"
|
|
99
|
+
],
|
|
100
|
+
|
|
101
|
+
leaderboard: [
|
|
102
|
+
// Global leaderboard functions
|
|
103
|
+
"function getTopEntries(uint256 count) external view returns (tuple(uint256 passportId, address owner, uint256 totalScore, uint256 holdingPoints, uint256 platformPoints, uint256 referralPoints, uint256 verificationCount, string category, uint256 lastUpdated, uint256 rank, uint256 previousRank)[] memory)",
|
|
104
|
+
"function getPassportEntry(uint256 passportId) external view returns (tuple(uint256 passportId, address owner, uint256 totalScore, uint256 holdingPoints, uint256 platformPoints, uint256 referralPoints, uint256 verificationCount, string category, uint256 lastUpdated, uint256 rank, uint256 previousRank))",
|
|
105
|
+
"function getPassportRank(uint256 passportId) external view returns (uint256)",
|
|
106
|
+
"function getPassportScore(uint256 passportId) external view returns (uint256)",
|
|
107
|
+
"function isPassportInLeaderboard(uint256 passportId) external view returns (bool)",
|
|
108
|
+
|
|
109
|
+
// Category-specific functions
|
|
110
|
+
"function getTopEntriesByCategory(string calldata category, uint256 count) external view returns (tuple(uint256 passportId, address owner, uint256 totalScore, uint256 holdingPoints, uint256 platformPoints, uint256 referralPoints, uint256 verificationCount, string category, uint256 lastUpdated, uint256 rank, uint256 previousRank)[] memory)",
|
|
111
|
+
"function getPassportEntryByCategory(uint256 passportId, string calldata category) external view returns (tuple(uint256 passportId, address owner, uint256 totalScore, uint256 holdingPoints, uint256 platformPoints, uint256 referralPoints, uint256 verificationCount, string category, uint256 lastUpdated, uint256 rank, uint256 previousRank))",
|
|
112
|
+
"function getPassportRankByCategory(uint256 passportId, string calldata category) external view returns (uint256)",
|
|
113
|
+
|
|
114
|
+
// Leaderboard statistics
|
|
115
|
+
"function getLeaderboardStats(string calldata category) external view returns (uint256 totalEntries, bool isActive, uint256 maxEntries)",
|
|
116
|
+
"function getSupportedCategories() external view returns (string[] memory)"
|
|
96
117
|
]
|
|
97
118
|
};
|
|
98
119
|
|
|
@@ -113,6 +134,10 @@ class PasslySDK {
|
|
|
113
134
|
this.contracts.rewards = new ethers.Contract(addresses.rewards, abis.rewards, contractProvider);
|
|
114
135
|
}
|
|
115
136
|
|
|
137
|
+
if (addresses.leaderboard) {
|
|
138
|
+
this.contracts.leaderboard = new ethers.Contract(addresses.leaderboard, abis.leaderboard, contractProvider);
|
|
139
|
+
}
|
|
140
|
+
|
|
116
141
|
this.config = { ...config, addresses };
|
|
117
142
|
this.isConnected = true;
|
|
118
143
|
return this;
|
|
@@ -149,6 +174,26 @@ class PasslySDK {
|
|
|
149
174
|
throw new Error('Invalid address or passport ID');
|
|
150
175
|
}
|
|
151
176
|
|
|
177
|
+
/**
|
|
178
|
+
* Helper to format leaderboard entry from contract response
|
|
179
|
+
* @private
|
|
180
|
+
*/
|
|
181
|
+
_formatLeaderboardEntry(entry) {
|
|
182
|
+
return {
|
|
183
|
+
passportId: entry.passportId.toNumber(),
|
|
184
|
+
owner: entry.owner,
|
|
185
|
+
totalScore: entry.totalScore.toNumber(),
|
|
186
|
+
holdingPoints: entry.holdingPoints.toNumber(),
|
|
187
|
+
platformPoints: entry.platformPoints.toNumber(),
|
|
188
|
+
referralPoints: entry.referralPoints.toNumber(),
|
|
189
|
+
verificationCount: entry.verificationCount.toNumber(),
|
|
190
|
+
category: entry.category,
|
|
191
|
+
lastUpdated: new Date(entry.lastUpdated.toNumber() * 1000),
|
|
192
|
+
rank: entry.rank.toNumber(),
|
|
193
|
+
previousRank: entry.previousRank.toNumber()
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
152
197
|
// =============================================================================
|
|
153
198
|
// PASSPORT & IDENTITY FUNCTIONS
|
|
154
199
|
// =============================================================================
|
|
@@ -422,6 +467,244 @@ class PasslySDK {
|
|
|
422
467
|
}
|
|
423
468
|
}
|
|
424
469
|
|
|
470
|
+
// =============================================================================
|
|
471
|
+
// LEADERBOARD FUNCTIONS
|
|
472
|
+
// =============================================================================
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Get top entries from global leaderboard
|
|
476
|
+
* @param {number} [count=10] - Number of entries to return
|
|
477
|
+
* @returns {Promise<Array|null>} - Top leaderboard entries or null if no leaderboard contract
|
|
478
|
+
*/
|
|
479
|
+
async getTopEntries(count = 10) {
|
|
480
|
+
this._ensureConnected();
|
|
481
|
+
|
|
482
|
+
if (!this.contracts.leaderboard) return null;
|
|
483
|
+
|
|
484
|
+
try {
|
|
485
|
+
const entries = await this.contracts.leaderboard.getTopEntries(count);
|
|
486
|
+
return entries.map(entry => this._formatLeaderboardEntry(entry));
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.warn('Failed to get top entries:', error.message);
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Get leaderboard entry for a specific passport
|
|
495
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
496
|
+
* @returns {Promise<Object|null>} - Leaderboard entry or null if not found/no leaderboard contract
|
|
497
|
+
*/
|
|
498
|
+
async getPassportLeaderboardEntry(addressOrPassportId) {
|
|
499
|
+
this._ensureConnected();
|
|
500
|
+
|
|
501
|
+
if (!this.contracts.leaderboard) return null;
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
505
|
+
const entry = await this.contracts.leaderboard.getPassportEntry(passportId);
|
|
506
|
+
return this._formatLeaderboardEntry(entry);
|
|
507
|
+
} catch (error) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Get global rank for a passport
|
|
514
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
515
|
+
* @returns {Promise<number|null>} - Global rank (1 = first place, 0 = not ranked) or null if no leaderboard contract
|
|
516
|
+
*/
|
|
517
|
+
async getGlobalRank(addressOrPassportId) {
|
|
518
|
+
this._ensureConnected();
|
|
519
|
+
|
|
520
|
+
if (!this.contracts.leaderboard) return null;
|
|
521
|
+
|
|
522
|
+
try {
|
|
523
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
524
|
+
const rank = await this.contracts.leaderboard.getPassportRank(passportId);
|
|
525
|
+
return rank.toNumber();
|
|
526
|
+
} catch (error) {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Get current leaderboard score for a passport
|
|
533
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
534
|
+
* @returns {Promise<number|null>} - Current score or null if not found/no leaderboard contract
|
|
535
|
+
*/
|
|
536
|
+
async getLeaderboardScore(addressOrPassportId) {
|
|
537
|
+
this._ensureConnected();
|
|
538
|
+
|
|
539
|
+
if (!this.contracts.leaderboard) return null;
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
543
|
+
const score = await this.contracts.leaderboard.getPassportScore(passportId);
|
|
544
|
+
return score.toNumber();
|
|
545
|
+
} catch (error) {
|
|
546
|
+
return null;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/**
|
|
551
|
+
* Check if passport is in the leaderboard
|
|
552
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
553
|
+
* @returns {Promise<boolean>} - Whether passport is ranked in leaderboard
|
|
554
|
+
*/
|
|
555
|
+
async isInLeaderboard(addressOrPassportId) {
|
|
556
|
+
this._ensureConnected();
|
|
557
|
+
|
|
558
|
+
if (!this.contracts.leaderboard) return false;
|
|
559
|
+
|
|
560
|
+
try {
|
|
561
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
562
|
+
return await this.contracts.leaderboard.isPassportInLeaderboard(passportId);
|
|
563
|
+
} catch (error) {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Get top entries from a specific category leaderboard
|
|
570
|
+
* @param {string} category - Category name (e.g., "creator", "developer") or "global"
|
|
571
|
+
* @param {number} [count=10] - Number of entries to return
|
|
572
|
+
* @returns {Promise<Array|null>} - Top entries for category or null if no leaderboard contract
|
|
573
|
+
*/
|
|
574
|
+
async getTopEntriesByCategory(category, count = 10) {
|
|
575
|
+
this._ensureConnected();
|
|
576
|
+
|
|
577
|
+
if (!this.contracts.leaderboard) return null;
|
|
578
|
+
|
|
579
|
+
try {
|
|
580
|
+
const entries = await this.contracts.leaderboard.getTopEntriesByCategory(category, count);
|
|
581
|
+
return entries.map(entry => this._formatLeaderboardEntry(entry));
|
|
582
|
+
} catch (error) {
|
|
583
|
+
console.warn(`Failed to get top entries for category ${category}:`, error.message);
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Get passport's rank in a specific category
|
|
590
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
591
|
+
* @param {string} category - Category name
|
|
592
|
+
* @returns {Promise<number|null>} - Rank in category (1 = first place, 0 = not ranked) or null if no leaderboard contract
|
|
593
|
+
*/
|
|
594
|
+
async getCategoryRank(addressOrPassportId, category) {
|
|
595
|
+
this._ensureConnected();
|
|
596
|
+
|
|
597
|
+
if (!this.contracts.leaderboard) return null;
|
|
598
|
+
|
|
599
|
+
try {
|
|
600
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
601
|
+
const rank = await this.contracts.leaderboard.getPassportRankByCategory(passportId, category);
|
|
602
|
+
return rank.toNumber();
|
|
603
|
+
} catch (error) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Get passport's entry in a specific category leaderboard
|
|
610
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
611
|
+
* @param {string} category - Category name
|
|
612
|
+
* @returns {Promise<Object|null>} - Category leaderboard entry or null if not found/no leaderboard contract
|
|
613
|
+
*/
|
|
614
|
+
async getCategoryLeaderboardEntry(addressOrPassportId, category) {
|
|
615
|
+
this._ensureConnected();
|
|
616
|
+
|
|
617
|
+
if (!this.contracts.leaderboard) return null;
|
|
618
|
+
|
|
619
|
+
try {
|
|
620
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
621
|
+
const entry = await this.contracts.leaderboard.getPassportEntryByCategory(passportId, category);
|
|
622
|
+
return this._formatLeaderboardEntry(entry);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Get statistics for a leaderboard category
|
|
630
|
+
* @param {string} category - Category name or "global"
|
|
631
|
+
* @returns {Promise<Object|null>} - Leaderboard statistics or null if no leaderboard contract
|
|
632
|
+
*/
|
|
633
|
+
async getLeaderboardStats(category) {
|
|
634
|
+
this._ensureConnected();
|
|
635
|
+
|
|
636
|
+
if (!this.contracts.leaderboard) return null;
|
|
637
|
+
|
|
638
|
+
try {
|
|
639
|
+
const [totalEntries, isActive, maxEntries] = await this.contracts.leaderboard.getLeaderboardStats(category);
|
|
640
|
+
|
|
641
|
+
return {
|
|
642
|
+
category,
|
|
643
|
+
totalEntries: totalEntries.toNumber(),
|
|
644
|
+
isActive,
|
|
645
|
+
maxEntries: maxEntries.toNumber()
|
|
646
|
+
};
|
|
647
|
+
} catch (error) {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Get all supported leaderboard categories
|
|
654
|
+
* @returns {Promise<string[]>} - Array of supported category names
|
|
655
|
+
*/
|
|
656
|
+
async getLeaderboardCategories() {
|
|
657
|
+
this._ensureConnected();
|
|
658
|
+
|
|
659
|
+
if (!this.contracts.leaderboard) return [];
|
|
660
|
+
|
|
661
|
+
try {
|
|
662
|
+
return await this.contracts.leaderboard.getSupportedCategories();
|
|
663
|
+
} catch (error) {
|
|
664
|
+
return [];
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Get comprehensive leaderboard data for a passport
|
|
670
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
671
|
+
* @returns {Promise<Object|null>} - Complete leaderboard information or null if no leaderboard contract
|
|
672
|
+
*/
|
|
673
|
+
async getCompleteLeaderboardData(addressOrPassportId) {
|
|
674
|
+
this._ensureConnected();
|
|
675
|
+
|
|
676
|
+
if (!this.contracts.leaderboard) return null;
|
|
677
|
+
|
|
678
|
+
try {
|
|
679
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
680
|
+
|
|
681
|
+
// Get passport's category from passport data
|
|
682
|
+
const passportData = await this.getPassport(addressOrPassportId);
|
|
683
|
+
if (!passportData) return null;
|
|
684
|
+
|
|
685
|
+
const category = passportData.category;
|
|
686
|
+
|
|
687
|
+
// Gather all leaderboard data in parallel
|
|
688
|
+
const [globalEntry, categoryEntry, isInBoard, categories] = await Promise.all([
|
|
689
|
+
this.getPassportLeaderboardEntry(passportId).catch(() => null),
|
|
690
|
+
this.getCategoryLeaderboardEntry(passportId, category).catch(() => null),
|
|
691
|
+
this.isInLeaderboard(passportId).catch(() => false),
|
|
692
|
+
this.getLeaderboardCategories().catch(() => [])
|
|
693
|
+
]);
|
|
694
|
+
|
|
695
|
+
return {
|
|
696
|
+
passportId,
|
|
697
|
+
category,
|
|
698
|
+
isInLeaderboard: isInBoard,
|
|
699
|
+
global: globalEntry,
|
|
700
|
+
categorySpecific: categoryEntry,
|
|
701
|
+
availableCategories: categories
|
|
702
|
+
};
|
|
703
|
+
} catch (error) {
|
|
704
|
+
return null;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
425
708
|
// =============================================================================
|
|
426
709
|
// HISTORICAL DATA FUNCTIONS
|
|
427
710
|
// =============================================================================
|
|
@@ -530,7 +813,7 @@ class PasslySDK {
|
|
|
530
813
|
}
|
|
531
814
|
|
|
532
815
|
/**
|
|
533
|
-
* Get supported categories
|
|
816
|
+
* Get supported categories (from Passly contract)
|
|
534
817
|
* @returns {Promise<string[]>} - List of supported categories
|
|
535
818
|
*/
|
|
536
819
|
async getSupportedCategories() {
|
|
@@ -597,15 +880,17 @@ class PasslySDK {
|
|
|
597
880
|
const passport = await this.getPassport(address);
|
|
598
881
|
if (!passport) return null;
|
|
599
882
|
|
|
600
|
-
const [pointBreakdown, referralInfo] = await Promise.all([
|
|
883
|
+
const [pointBreakdown, referralInfo, leaderboardData] = await Promise.all([
|
|
601
884
|
this.getPointBreakdown(address).catch(() => null),
|
|
602
|
-
this.getReferralInfo(address).catch(() => null)
|
|
885
|
+
this.getReferralInfo(address).catch(() => null),
|
|
886
|
+
this.getCompleteLeaderboardData(address).catch(() => null)
|
|
603
887
|
]);
|
|
604
888
|
|
|
605
889
|
return {
|
|
606
890
|
...passport,
|
|
607
891
|
points: pointBreakdown,
|
|
608
|
-
referrals: referralInfo
|
|
892
|
+
referrals: referralInfo,
|
|
893
|
+
leaderboard: leaderboardData
|
|
609
894
|
};
|
|
610
895
|
}
|
|
611
896
|
|
|
@@ -683,9 +968,10 @@ class PasslySDK {
|
|
|
683
968
|
async getSystemConfig() {
|
|
684
969
|
this._ensureConnected();
|
|
685
970
|
|
|
686
|
-
const [supportedPlatforms, supportedCategories] = await Promise.all([
|
|
971
|
+
const [supportedPlatforms, supportedCategories, leaderboardCategories] = await Promise.all([
|
|
687
972
|
this.getSupportedPlatforms().catch(() => []),
|
|
688
|
-
this.getSupportedCategories().catch(() => [])
|
|
973
|
+
this.getSupportedCategories().catch(() => []),
|
|
974
|
+
this.getLeaderboardCategories().catch(() => [])
|
|
689
975
|
]);
|
|
690
976
|
|
|
691
977
|
let pointConfig = null;
|
|
@@ -705,12 +991,14 @@ class PasslySDK {
|
|
|
705
991
|
return {
|
|
706
992
|
supportedPlatforms,
|
|
707
993
|
supportedCategories,
|
|
994
|
+
leaderboardCategories,
|
|
708
995
|
pointConfig,
|
|
709
996
|
contracts: {
|
|
710
997
|
passly: this.config.addresses.passly,
|
|
711
998
|
platforms: this.config.addresses.platforms || null,
|
|
712
999
|
archives: this.config.addresses.archives || null,
|
|
713
|
-
rewards: this.config.addresses.rewards || null
|
|
1000
|
+
rewards: this.config.addresses.rewards || null,
|
|
1001
|
+
leaderboard: this.config.addresses.leaderboard || null
|
|
714
1002
|
}
|
|
715
1003
|
};
|
|
716
1004
|
}
|