@passly/passly-sdk 0.1.5 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +299 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passly/passly-sdk",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "description": "Official SDK for Passly identity protocol - social identity verification for Web3",
6
6
  "main": "src/index.js",
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
@@ -47,10 +49,11 @@ class PasslySDK {
47
49
 
48
50
  // Default contract addresses
49
51
  const addresses = {
50
- passly: config.contractAddress || '0x9a1b12c6E794dE70b1Bd73a74f81eF5A41Fb7Cb3',
51
- platforms: config.platformsAddress || '0x0f1ff42F6f9C2d8D1C054aF7c03cf5e449f0Bc15',
52
- archives: config.archivesAddress || '0xa26A0eAE99A6ccD8f82dC6Cfbd35ccCeebf3BcFf',
53
- rewards: config.rewardsAddress || '0x993d28480D5f9bf457FAb67D58D696e3c5655031'
52
+ passly: config.contractAddress || '0x8EEFC0840Bc24e269A2F77B787E9b3e212c4F316',
53
+ platforms: config.platformsAddress || '0xd2FEEa5775c171D85648198AeB77377fD9AdFe98',
54
+ archives: config.archivesAddress || '0xbC57EA3ff9BDE13087b907Dc02e86f08C57574E7',
55
+ rewards: config.rewardsAddress || '0xd2FEEa5775c171D85648198AeB77377fD9AdFe98',
56
+ leaderboard: config.leaderboardAddress || '0x7f9c4841346d0ef7970daF02aE3663f8AC5bE540'
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
  }