@passly/passly-sdk 0.1.4 → 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 +761 -230
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
|
-
// passly-sdk.js
|
|
1
|
+
// index.js (or passly-sdk.js)
|
|
2
2
|
import { ethers } from 'ethers';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Passly SDK - A
|
|
5
|
+
* Passly SDK - A comprehensive interface for interacting with the Passly identity protocol
|
|
6
6
|
*
|
|
7
7
|
* This SDK provides an easy way for developers to integrate with Passly,
|
|
8
8
|
* a social identity layer that helps verify user identities and works as an anti-sybil tool.
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Identity verification and passport management
|
|
12
|
+
* - Points and rewards system integration
|
|
13
|
+
* - Historical verification data access
|
|
14
|
+
* - Platform configuration queries
|
|
15
|
+
* - Referral system integration
|
|
16
|
+
* - Leaderboard and ranking system
|
|
9
17
|
*/
|
|
10
18
|
class PasslySDK {
|
|
11
19
|
/**
|
|
12
20
|
* Initialize the Passly SDK
|
|
13
21
|
* @param {Object} config - Configuration options
|
|
14
|
-
* @param {string} [config.contractAddress] -
|
|
22
|
+
* @param {string} [config.contractAddress] - Override for the Passly contract address
|
|
23
|
+
* @param {string} [config.platformsAddress] - Override for the Platforms contract address
|
|
24
|
+
* @param {string} [config.archivesAddress] - Override for the Archives contract address
|
|
25
|
+
* @param {string} [config.rewardsAddress] - Override for the Rewards contract address
|
|
26
|
+
* @param {string} [config.leaderboardAddress] - Override for the Leaderboard contract address
|
|
15
27
|
* @param {ethers.providers.Provider} [config.provider] - Optional ethers provider
|
|
16
28
|
* @param {ethers.Signer} [config.signer] - Optional ethers signer for write operations
|
|
29
|
+
* @param {string} [config.network] - Network name for default addresses
|
|
17
30
|
*/
|
|
18
31
|
constructor(config = {}) {
|
|
19
|
-
// Default configuration will be set when connect() is called
|
|
20
32
|
this.config = config;
|
|
21
|
-
this.
|
|
33
|
+
this.contracts = {};
|
|
22
34
|
this.isConnected = false;
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
26
|
-
* Connect to the Passly
|
|
38
|
+
* Connect to the Passly contracts
|
|
27
39
|
* @param {Object} options - Optional connection parameters to override constructor config
|
|
28
40
|
* @returns {Promise<PasslySDK>} - Returns the SDK instance
|
|
29
41
|
*/
|
|
@@ -35,30 +47,98 @@ class PasslySDK {
|
|
|
35
47
|
config.provider = new ethers.providers.JsonRpcProvider('https://testnet.skalenodes.com/v1/aware-fake-trim-testnet');
|
|
36
48
|
}
|
|
37
49
|
|
|
38
|
-
//
|
|
39
|
-
|
|
50
|
+
// Default contract addresses
|
|
51
|
+
const addresses = {
|
|
52
|
+
passly: config.contractAddress || '0x9a1b12c6E794dE70b1Bd73a74f81eF5A41Fb7Cb3',
|
|
53
|
+
platforms: config.platformsAddress || '0x0f1ff42F6f9C2d8D1C054aF7c03cf5e449f0Bc15',
|
|
54
|
+
archives: config.archivesAddress || '0xa26A0eAE99A6ccD8f82dC6Cfbd35ccCeebf3BcFf',
|
|
55
|
+
rewards: config.rewardsAddress || '0x993d28480D5f9bf457FAb67D58D696e3c5655031',
|
|
56
|
+
leaderboard: config.leaderboardAddress || null // Add when deployed
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Contract ABIs
|
|
60
|
+
const abis = {
|
|
61
|
+
passly: [
|
|
62
|
+
"function getPassportByAddress(address user) external view returns (uint256)",
|
|
63
|
+
"function getPassportData(uint256 passportId) external view returns (address owner, uint256 createdAt, uint256 verificationCount, string memory category, uint256 totalPoints, string memory referralCode, uint256 totalReferrals)",
|
|
64
|
+
"function getVerifiedPlatforms(uint256 passportId) external view returns (string[] memory)",
|
|
65
|
+
"function getVerification(uint256 passportId, string calldata platform) external view returns (string memory identifier, uint256 verifiedAt, bytes32 proofHash, bool active, bool pointsAwarded)",
|
|
66
|
+
"function isIdentifierVerified(string calldata platform, string calldata identifier) external view returns (bool isVerified, uint256 passportId)",
|
|
67
|
+
"function getSupportedCategories() external view returns (string[] memory)",
|
|
68
|
+
"function isCategorySupported(string calldata category) external view returns (bool)",
|
|
69
|
+
"function ownerOf(uint256 tokenId) external view returns (address)"
|
|
70
|
+
],
|
|
71
|
+
|
|
72
|
+
platforms: [
|
|
73
|
+
"function getPlatformConfig(string calldata platform) external view returns (bool isSupported, string memory platformType, string[] memory requiredPlatforms, uint256 pointReward, bool enablePointPunishment, uint256 punishmentPeriodDays)",
|
|
74
|
+
"function getSupportedPlatforms() external view returns (string[] memory)",
|
|
75
|
+
"function getSupportedPlatformTypes() external view returns (string[] memory)",
|
|
76
|
+
"function isPlatformSupported(string calldata platform) external view returns (bool)",
|
|
77
|
+
"function isPlatformTypeSupported(string calldata platformType) external view returns (bool)",
|
|
78
|
+
"function validateDependencies(string[] calldata verifiedPlatforms, string calldata platform) external view returns (bool isValid, string memory missingDependency)",
|
|
79
|
+
"function getCascadeAffectedPlatforms(string[] calldata verifiedPlatforms, string calldata revokedPlatform) external view returns (string[] memory affectedPlatforms)"
|
|
80
|
+
],
|
|
81
|
+
|
|
82
|
+
archives: [
|
|
83
|
+
"function getVerificationHistory(uint256 passportId, string calldata platform) external view returns (tuple(string identifier, uint256 verifiedAt, uint256 revokedAt, bytes32 proofHash, bool wasRevoked, string revokeReason)[] memory)",
|
|
84
|
+
"function getPlatformHistory(uint256 passportId, string calldata platform) external view returns (tuple(uint256 totalVerifications, uint256 totalRevocations, string[] historicalIdentifiers, uint256 firstVerificationAt, uint256 lastRevocationAt))",
|
|
85
|
+
"function getHistoricalIdentifiers(uint256 passportId, string calldata platform) external view returns (string[] memory)",
|
|
86
|
+
"function getIdentifierUsage(string calldata platform, string calldata identifier) external view returns (uint256[] memory)",
|
|
87
|
+
"function getMultiPlatformHistory(uint256 passportId, string[] calldata platforms) external view returns (uint256 totalVerifications, uint256 totalRevocations, tuple(uint256 totalVerifications, uint256 totalRevocations, string[] historicalIdentifiers, uint256 firstVerificationAt, uint256 lastRevocationAt)[] memory platformStats)"
|
|
88
|
+
],
|
|
89
|
+
|
|
90
|
+
rewards: [
|
|
91
|
+
"function getPoints(uint256 passportId) external view returns (uint256)",
|
|
92
|
+
"function getPointBreakdown(uint256 passportId) external view returns (uint256 holding, uint256 platform, uint256 referral, uint256 total)",
|
|
93
|
+
"function getPlatformPoints(uint256 passportId, string calldata platform) external view returns (uint256)",
|
|
94
|
+
"function isPlatformRewarded(uint256 passportId, string calldata platform) external view returns (bool)",
|
|
95
|
+
"function getReferralInfo(uint256 passportId) external view returns (string memory referralCode, address referredBy, uint256 totalReferrals, uint256 referralEarnings)",
|
|
96
|
+
"function validateReferralCode(string calldata referralCode) external view returns (bool isValid, uint256 ownerPassportId)",
|
|
97
|
+
"function getUserReferrals(uint256 passportId) external view returns (string[] memory)",
|
|
98
|
+
"function getPointConfig() external view returns (uint256 dailyHolding, uint256 referral, uint256 referee)"
|
|
99
|
+
],
|
|
40
100
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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)"
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// Initialize contracts
|
|
121
|
+
const contractProvider = config.signer || config.provider;
|
|
122
|
+
|
|
123
|
+
this.contracts.passly = new ethers.Contract(addresses.passly, abis.passly, contractProvider);
|
|
124
|
+
|
|
125
|
+
if (addresses.platforms) {
|
|
126
|
+
this.contracts.platforms = new ethers.Contract(addresses.platforms, abis.platforms, contractProvider);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (addresses.archives) {
|
|
130
|
+
this.contracts.archives = new ethers.Contract(addresses.archives, abis.archives, contractProvider);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (addresses.rewards) {
|
|
134
|
+
this.contracts.rewards = new ethers.Contract(addresses.rewards, abis.rewards, contractProvider);
|
|
135
|
+
}
|
|
51
136
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Connect with signer for write operations
|
|
55
|
-
this.contract = new ethers.Contract(config.contractAddress, abi, config.signer);
|
|
56
|
-
} else {
|
|
57
|
-
// Connect with provider for read-only operations
|
|
58
|
-
this.contract = new ethers.Contract(config.contractAddress, abi, config.provider);
|
|
137
|
+
if (addresses.leaderboard) {
|
|
138
|
+
this.contracts.leaderboard = new ethers.Contract(addresses.leaderboard, abis.leaderboard, contractProvider);
|
|
59
139
|
}
|
|
60
140
|
|
|
61
|
-
this.config = config;
|
|
141
|
+
this.config = { ...config, addresses };
|
|
62
142
|
this.isConnected = true;
|
|
63
143
|
return this;
|
|
64
144
|
}
|
|
@@ -73,6 +153,51 @@ class PasslySDK {
|
|
|
73
153
|
}
|
|
74
154
|
}
|
|
75
155
|
|
|
156
|
+
/**
|
|
157
|
+
* Helper to convert address or passport ID to passport ID
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
async _resolvePassportId(addressOrPassportId) {
|
|
161
|
+
if (typeof addressOrPassportId === 'number' ||
|
|
162
|
+
(typeof addressOrPassportId === 'string' && /^\d+$/.test(addressOrPassportId))) {
|
|
163
|
+
return parseInt(addressOrPassportId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof addressOrPassportId === 'string' && ethers.utils.isAddress(addressOrPassportId)) {
|
|
167
|
+
const passportId = await this.getPassportId(addressOrPassportId);
|
|
168
|
+
if (!passportId) {
|
|
169
|
+
throw new Error('No passport found for address');
|
|
170
|
+
}
|
|
171
|
+
return passportId;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
throw new Error('Invalid address or passport ID');
|
|
175
|
+
}
|
|
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
|
+
|
|
197
|
+
// =============================================================================
|
|
198
|
+
// PASSPORT & IDENTITY FUNCTIONS
|
|
199
|
+
// =============================================================================
|
|
200
|
+
|
|
76
201
|
/**
|
|
77
202
|
* Get a user's passport ID from their wallet address
|
|
78
203
|
* @param {string} address - The wallet address to check
|
|
@@ -82,10 +207,9 @@ class PasslySDK {
|
|
|
82
207
|
this._ensureConnected();
|
|
83
208
|
|
|
84
209
|
try {
|
|
85
|
-
const passportId = await this.
|
|
210
|
+
const passportId = await this.contracts.passly.getPassportByAddress(address);
|
|
86
211
|
return passportId.toNumber();
|
|
87
212
|
} catch (error) {
|
|
88
|
-
// If the error is "User has no passport", return null
|
|
89
213
|
if (error.message.includes('User has no passport')) {
|
|
90
214
|
return null;
|
|
91
215
|
}
|
|
@@ -114,37 +238,46 @@ class PasslySDK {
|
|
|
114
238
|
let passportId;
|
|
115
239
|
try {
|
|
116
240
|
passportId = await this.getPassportId(address);
|
|
241
|
+
if (!passportId) return null;
|
|
117
242
|
} catch (error) {
|
|
118
243
|
return null;
|
|
119
244
|
}
|
|
120
245
|
|
|
121
|
-
|
|
246
|
+
// Get passport data - now correctly handling all 7 return values
|
|
247
|
+
const [owner, createdAt, verificationCount, category, totalPoints, referralCode, totalReferrals] =
|
|
248
|
+
await this.contracts.passly.getPassportData(passportId);
|
|
122
249
|
|
|
123
|
-
const
|
|
124
|
-
const platforms = await this.contract.getVerifiedPlatforms(passportId);
|
|
250
|
+
const platforms = await this.contracts.passly.getVerifiedPlatforms(passportId);
|
|
125
251
|
|
|
126
252
|
// Gather all verifications
|
|
127
253
|
const verifications = {};
|
|
128
254
|
for (const platform of platforms) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
255
|
+
try {
|
|
256
|
+
const [identifier, verifiedAt, proofHash, active, pointsAwarded] =
|
|
257
|
+
await this.contracts.passly.getVerification(passportId, platform);
|
|
258
|
+
|
|
259
|
+
verifications[platform] = {
|
|
260
|
+
identifier,
|
|
261
|
+
verifiedAt: new Date(verifiedAt.toNumber() * 1000),
|
|
262
|
+
proofHash,
|
|
263
|
+
active,
|
|
264
|
+
pointsAwarded
|
|
265
|
+
};
|
|
266
|
+
} catch (error) {
|
|
267
|
+
// Skip platforms that fail to load
|
|
268
|
+
console.warn(`Failed to load verification for platform ${platform}:`, error.message);
|
|
269
|
+
}
|
|
140
270
|
}
|
|
141
271
|
|
|
142
272
|
return {
|
|
143
273
|
id: passportId,
|
|
144
|
-
owner
|
|
274
|
+
owner,
|
|
145
275
|
createdAt: new Date(createdAt.toNumber() * 1000),
|
|
146
276
|
verificationCount: verificationCount.toNumber(),
|
|
147
277
|
category,
|
|
278
|
+
totalPoints: totalPoints.toNumber(),
|
|
279
|
+
referralCode,
|
|
280
|
+
totalReferrals: totalReferrals.toNumber(),
|
|
148
281
|
platforms,
|
|
149
282
|
verifications
|
|
150
283
|
};
|
|
@@ -152,27 +285,31 @@ class PasslySDK {
|
|
|
152
285
|
|
|
153
286
|
/**
|
|
154
287
|
* Get all verification data for a passport
|
|
155
|
-
* @param {number}
|
|
288
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
156
289
|
* @returns {Promise<Object>} - Object containing verification details
|
|
157
290
|
*/
|
|
158
|
-
async getVerifications(
|
|
291
|
+
async getVerifications(addressOrPassportId) {
|
|
159
292
|
this._ensureConnected();
|
|
160
293
|
|
|
161
|
-
const
|
|
294
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
295
|
+
const platforms = await this.contracts.passly.getVerifiedPlatforms(passportId);
|
|
162
296
|
const verifications = {};
|
|
163
297
|
|
|
164
298
|
for (const platform of platforms) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
299
|
+
try {
|
|
300
|
+
const [identifier, verifiedAt, proofHash, active, pointsAwarded] =
|
|
301
|
+
await this.contracts.passly.getVerification(passportId, platform);
|
|
302
|
+
|
|
303
|
+
verifications[platform] = {
|
|
304
|
+
identifier,
|
|
305
|
+
verifiedAt: new Date(verifiedAt.toNumber() * 1000),
|
|
306
|
+
proofHash,
|
|
307
|
+
active,
|
|
308
|
+
pointsAwarded
|
|
309
|
+
};
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.warn(`Failed to load verification for platform ${platform}:`, error.message);
|
|
312
|
+
}
|
|
176
313
|
}
|
|
177
314
|
|
|
178
315
|
return verifications;
|
|
@@ -187,7 +324,7 @@ class PasslySDK {
|
|
|
187
324
|
async isAccountVerified(platform, identifier) {
|
|
188
325
|
this._ensureConnected();
|
|
189
326
|
|
|
190
|
-
const [isVerified, passportId] = await this.
|
|
327
|
+
const [isVerified, passportId] = await this.contracts.passly.isIdentifierVerified(platform, identifier);
|
|
191
328
|
|
|
192
329
|
return {
|
|
193
330
|
isVerified,
|
|
@@ -195,26 +332,516 @@ class PasslySDK {
|
|
|
195
332
|
};
|
|
196
333
|
}
|
|
197
334
|
|
|
335
|
+
// =============================================================================
|
|
336
|
+
// REWARDS & POINTS FUNCTIONS
|
|
337
|
+
// =============================================================================
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Get total points for a passport
|
|
341
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
342
|
+
* @returns {Promise<number|null>} - Total points or null if no passport/rewards contract
|
|
343
|
+
*/
|
|
344
|
+
async getPoints(addressOrPassportId) {
|
|
345
|
+
this._ensureConnected();
|
|
346
|
+
|
|
347
|
+
if (!this.contracts.rewards) {
|
|
348
|
+
// Fallback to passport data
|
|
349
|
+
const passport = await this.getPassport(addressOrPassportId);
|
|
350
|
+
return passport ? passport.totalPoints : null;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
355
|
+
const points = await this.contracts.rewards.getPoints(passportId);
|
|
356
|
+
return points.toNumber();
|
|
357
|
+
} catch (error) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get detailed point breakdown for a passport
|
|
364
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
365
|
+
* @returns {Promise<Object|null>} - Point breakdown or null if no passport/rewards contract
|
|
366
|
+
*/
|
|
367
|
+
async getPointBreakdown(addressOrPassportId) {
|
|
368
|
+
this._ensureConnected();
|
|
369
|
+
|
|
370
|
+
if (!this.contracts.rewards) return null;
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
374
|
+
const [holding, platform, referral, total] = await this.contracts.rewards.getPointBreakdown(passportId);
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
holding: holding.toNumber(),
|
|
378
|
+
platform: platform.toNumber(),
|
|
379
|
+
referral: referral.toNumber(),
|
|
380
|
+
total: total.toNumber()
|
|
381
|
+
};
|
|
382
|
+
} catch (error) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get points earned from a specific platform
|
|
389
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
390
|
+
* @param {string} platform - The platform name
|
|
391
|
+
* @returns {Promise<number|null>} - Platform points or null if no passport/rewards contract
|
|
392
|
+
*/
|
|
393
|
+
async getPlatformPoints(addressOrPassportId, platform) {
|
|
394
|
+
this._ensureConnected();
|
|
395
|
+
|
|
396
|
+
if (!this.contracts.rewards) return null;
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
400
|
+
const points = await this.contracts.rewards.getPlatformPoints(passportId, platform);
|
|
401
|
+
return points.toNumber();
|
|
402
|
+
} catch (error) {
|
|
403
|
+
return null;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// =============================================================================
|
|
408
|
+
// REFERRAL FUNCTIONS
|
|
409
|
+
// =============================================================================
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Get referral information for a passport
|
|
413
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
414
|
+
* @returns {Promise<Object|null>} - Referral info or null if no passport/rewards contract
|
|
415
|
+
*/
|
|
416
|
+
async getReferralInfo(addressOrPassportId) {
|
|
417
|
+
this._ensureConnected();
|
|
418
|
+
|
|
419
|
+
if (!this.contracts.rewards) {
|
|
420
|
+
// Fallback to passport data
|
|
421
|
+
const passport = await this.getPassport(addressOrPassportId);
|
|
422
|
+
if (!passport) return null;
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
referralCode: passport.referralCode,
|
|
426
|
+
referredBy: '0x0000000000000000000000000000000000000000', // Not available in passport data
|
|
427
|
+
totalReferrals: passport.totalReferrals,
|
|
428
|
+
referralEarnings: 0 // Not available in passport data
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
try {
|
|
433
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
434
|
+
const [referralCode, referredBy, totalReferrals, referralEarnings] =
|
|
435
|
+
await this.contracts.rewards.getReferralInfo(passportId);
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
referralCode,
|
|
439
|
+
referredBy,
|
|
440
|
+
totalReferrals: totalReferrals.toNumber(),
|
|
441
|
+
referralEarnings: referralEarnings.toNumber()
|
|
442
|
+
};
|
|
443
|
+
} catch (error) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Validate a referral code
|
|
450
|
+
* @param {string} referralCode - The referral code to validate
|
|
451
|
+
* @returns {Promise<Object|null>} - Validation result or null if no rewards contract
|
|
452
|
+
*/
|
|
453
|
+
async validateReferralCode(referralCode) {
|
|
454
|
+
this._ensureConnected();
|
|
455
|
+
|
|
456
|
+
if (!this.contracts.rewards) return null;
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const [isValid, ownerPassportId] = await this.contracts.rewards.validateReferralCode(referralCode);
|
|
460
|
+
|
|
461
|
+
return {
|
|
462
|
+
isValid,
|
|
463
|
+
ownerPassportId: isValid ? ownerPassportId.toNumber() : null
|
|
464
|
+
};
|
|
465
|
+
} catch (error) {
|
|
466
|
+
return { isValid: false, ownerPassportId: null };
|
|
467
|
+
}
|
|
468
|
+
}
|
|
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
|
+
|
|
708
|
+
// =============================================================================
|
|
709
|
+
// HISTORICAL DATA FUNCTIONS
|
|
710
|
+
// =============================================================================
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Get verification history for a platform
|
|
714
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
715
|
+
* @param {string} platform - The platform name
|
|
716
|
+
* @returns {Promise<Array|null>} - Verification history or null if no passport/archives contract
|
|
717
|
+
*/
|
|
718
|
+
async getVerificationHistory(addressOrPassportId, platform) {
|
|
719
|
+
this._ensureConnected();
|
|
720
|
+
|
|
721
|
+
if (!this.contracts.archives) return null;
|
|
722
|
+
|
|
723
|
+
try {
|
|
724
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
725
|
+
const history = await this.contracts.archives.getVerificationHistory(passportId, platform);
|
|
726
|
+
|
|
727
|
+
return history.map(entry => ({
|
|
728
|
+
identifier: entry.identifier,
|
|
729
|
+
verifiedAt: new Date(entry.verifiedAt.toNumber() * 1000),
|
|
730
|
+
revokedAt: entry.revokedAt.toNumber() > 0 ? new Date(entry.revokedAt.toNumber() * 1000) : null,
|
|
731
|
+
proofHash: entry.proofHash,
|
|
732
|
+
wasRevoked: entry.wasRevoked,
|
|
733
|
+
revokeReason: entry.revokeReason
|
|
734
|
+
}));
|
|
735
|
+
} catch (error) {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Get platform usage statistics
|
|
742
|
+
* @param {string|number} addressOrPassportId - Wallet address or passport ID
|
|
743
|
+
* @param {string} platform - The platform name
|
|
744
|
+
* @returns {Promise<Object|null>} - Platform history or null if no passport/archives contract
|
|
745
|
+
*/
|
|
746
|
+
async getPlatformHistory(addressOrPassportId, platform) {
|
|
747
|
+
this._ensureConnected();
|
|
748
|
+
|
|
749
|
+
if (!this.contracts.archives) return null;
|
|
750
|
+
|
|
751
|
+
try {
|
|
752
|
+
const passportId = await this._resolvePassportId(addressOrPassportId);
|
|
753
|
+
const history = await this.contracts.archives.getPlatformHistory(passportId, platform);
|
|
754
|
+
|
|
755
|
+
return {
|
|
756
|
+
totalVerifications: history.totalVerifications.toNumber(),
|
|
757
|
+
totalRevocations: history.totalRevocations.toNumber(),
|
|
758
|
+
historicalIdentifiers: history.historicalIdentifiers,
|
|
759
|
+
firstVerificationAt: history.firstVerificationAt.toNumber() > 0 ?
|
|
760
|
+
new Date(history.firstVerificationAt.toNumber() * 1000) : null,
|
|
761
|
+
lastRevocationAt: history.lastRevocationAt.toNumber() > 0 ?
|
|
762
|
+
new Date(history.lastRevocationAt.toNumber() * 1000) : null
|
|
763
|
+
};
|
|
764
|
+
} catch (error) {
|
|
765
|
+
return null;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// =============================================================================
|
|
770
|
+
// PLATFORM CONFIGURATION FUNCTIONS
|
|
771
|
+
// =============================================================================
|
|
772
|
+
|
|
773
|
+
/**
|
|
774
|
+
* Get platform configuration
|
|
775
|
+
* @param {string} platform - The platform name
|
|
776
|
+
* @returns {Promise<Object|null>} - Platform config or null if no platforms contract
|
|
777
|
+
*/
|
|
778
|
+
async getPlatformConfig(platform) {
|
|
779
|
+
this._ensureConnected();
|
|
780
|
+
|
|
781
|
+
if (!this.contracts.platforms) return null;
|
|
782
|
+
|
|
783
|
+
try {
|
|
784
|
+
const [isSupported, platformType, requiredPlatforms, pointReward, enablePointPunishment, punishmentPeriodDays] =
|
|
785
|
+
await this.contracts.platforms.getPlatformConfig(platform);
|
|
786
|
+
|
|
787
|
+
return {
|
|
788
|
+
isSupported,
|
|
789
|
+
platformType,
|
|
790
|
+
requiredPlatforms,
|
|
791
|
+
pointReward: pointReward.toNumber(),
|
|
792
|
+
enablePointPunishment,
|
|
793
|
+
punishmentPeriodDays: punishmentPeriodDays.toNumber()
|
|
794
|
+
};
|
|
795
|
+
} catch (error) {
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
198
800
|
/**
|
|
199
801
|
* Get supported platforms
|
|
200
802
|
* @returns {Promise<string[]>} - List of supported platforms
|
|
201
803
|
*/
|
|
202
804
|
async getSupportedPlatforms() {
|
|
203
805
|
this._ensureConnected();
|
|
204
|
-
|
|
806
|
+
|
|
807
|
+
if (this.contracts.platforms) {
|
|
808
|
+
return await this.contracts.platforms.getSupportedPlatforms();
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// If platforms contract not available, return empty array
|
|
812
|
+
return [];
|
|
205
813
|
}
|
|
206
814
|
|
|
207
815
|
/**
|
|
208
|
-
* Get supported categories
|
|
816
|
+
* Get supported categories (from Passly contract)
|
|
209
817
|
* @returns {Promise<string[]>} - List of supported categories
|
|
210
818
|
*/
|
|
211
819
|
async getSupportedCategories() {
|
|
212
820
|
this._ensureConnected();
|
|
213
|
-
return await this.
|
|
821
|
+
return await this.contracts.passly.getSupportedCategories();
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Check if a platform is supported
|
|
826
|
+
* @param {string} platform - The platform name
|
|
827
|
+
* @returns {Promise<boolean>} - Whether the platform is supported
|
|
828
|
+
*/
|
|
829
|
+
async isPlatformSupported(platform) {
|
|
830
|
+
this._ensureConnected();
|
|
831
|
+
|
|
832
|
+
if (this.contracts.platforms) {
|
|
833
|
+
return await this.contracts.platforms.isPlatformSupported(platform);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
return false;
|
|
214
837
|
}
|
|
215
838
|
|
|
839
|
+
// =============================================================================
|
|
840
|
+
// CONVENIENCE & UTILITY FUNCTIONS
|
|
841
|
+
// =============================================================================
|
|
842
|
+
|
|
216
843
|
/**
|
|
217
|
-
*
|
|
844
|
+
* Get user's active verifications by wallet address
|
|
218
845
|
* @param {string} address - The wallet address
|
|
219
846
|
* @returns {Promise<Object|null>} - Object of platforms and identifiers, or null if no passport
|
|
220
847
|
*/
|
|
@@ -245,143 +872,79 @@ class PasslySDK {
|
|
|
245
872
|
}
|
|
246
873
|
|
|
247
874
|
/**
|
|
248
|
-
* Get
|
|
249
|
-
* @param {string} address - The wallet address
|
|
250
|
-
* @param {string} platform - The platform to check
|
|
251
|
-
* @returns {Promise<string|null>} - The identifier or null if not verified
|
|
252
|
-
*/
|
|
253
|
-
async getPlatformIdentifier(address, platform) {
|
|
254
|
-
const verifications = await this.getUserVerifications(address);
|
|
255
|
-
if (!verifications) return null;
|
|
256
|
-
return verifications[platform.toLowerCase()] || null;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// CONVENIENCE FUNCTIONS FOR ENHANCED IDENTITY VERIFICATION
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Get how long a user has been verified on a specific platform
|
|
875
|
+
* Get comprehensive user profile
|
|
263
876
|
* @param {string} address - The wallet address
|
|
264
|
-
* @
|
|
265
|
-
* @returns {Promise<Object|null>} - Object with verification age data or null if not verified
|
|
877
|
+
* @returns {Promise<Object|null>} - Complete user profile or null if no passport
|
|
266
878
|
*/
|
|
267
|
-
async
|
|
879
|
+
async getUserProfile(address) {
|
|
268
880
|
const passport = await this.getPassport(address);
|
|
269
|
-
if (!passport
|
|
270
|
-
return null;
|
|
271
|
-
}
|
|
881
|
+
if (!passport) return null;
|
|
272
882
|
|
|
273
|
-
const
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const ageInMs = now - verifiedAt;
|
|
279
|
-
const ageInDays = Math.floor(ageInMs / (1000 * 60 * 60 * 24));
|
|
280
|
-
const ageInHours = Math.floor(ageInMs / (1000 * 60 * 60));
|
|
281
|
-
const ageInMinutes = Math.floor(ageInMs / (1000 * 60));
|
|
883
|
+
const [pointBreakdown, referralInfo, leaderboardData] = await Promise.all([
|
|
884
|
+
this.getPointBreakdown(address).catch(() => null),
|
|
885
|
+
this.getReferralInfo(address).catch(() => null),
|
|
886
|
+
this.getCompleteLeaderboardData(address).catch(() => null)
|
|
887
|
+
]);
|
|
282
888
|
|
|
283
889
|
return {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
ageInMinutes,
|
|
289
|
-
ageInHours,
|
|
290
|
-
ageInDays,
|
|
291
|
-
humanReadable: ageInDays > 0
|
|
292
|
-
? `${ageInDays} day${ageInDays !== 1 ? 's' : ''}`
|
|
293
|
-
: ageInHours > 0
|
|
294
|
-
? `${ageInHours} hour${ageInHours !== 1 ? 's' : ''}`
|
|
295
|
-
: `${ageInMinutes} minute${ageInMinutes !== 1 ? 's' : ''}`
|
|
890
|
+
...passport,
|
|
891
|
+
points: pointBreakdown,
|
|
892
|
+
referrals: referralInfo,
|
|
893
|
+
leaderboard: leaderboardData
|
|
296
894
|
};
|
|
297
895
|
}
|
|
298
896
|
|
|
299
897
|
/**
|
|
300
|
-
*
|
|
301
|
-
* @param {string} address - The wallet address
|
|
302
|
-
* @returns {Promise<Object|null>} - Object with earliest verification data or null if no verifications
|
|
303
|
-
*/
|
|
304
|
-
async getEarliestVerification(address) {
|
|
305
|
-
const passport = await this.getPassport(address);
|
|
306
|
-
if (!passport || passport.platforms.length === 0) {
|
|
307
|
-
return null;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
let earliest = null;
|
|
311
|
-
let earliestDate = null;
|
|
312
|
-
|
|
313
|
-
for (const platform of passport.platforms) {
|
|
314
|
-
const verification = passport.verifications[platform];
|
|
315
|
-
if (verification.active && (!earliestDate || verification.verifiedAt < earliestDate)) {
|
|
316
|
-
earliestDate = verification.verifiedAt;
|
|
317
|
-
earliest = {
|
|
318
|
-
platform,
|
|
319
|
-
identifier: verification.identifier,
|
|
320
|
-
verifiedAt: verification.verifiedAt,
|
|
321
|
-
proofHash: verification.proofHash
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (earliest) {
|
|
327
|
-
const now = new Date();
|
|
328
|
-
const ageInMs = now - earliest.verifiedAt;
|
|
329
|
-
const ageInDays = Math.floor(ageInMs / (1000 * 60 * 60 * 24));
|
|
330
|
-
|
|
331
|
-
return {
|
|
332
|
-
...earliest,
|
|
333
|
-
ageInDays,
|
|
334
|
-
humanReadable: `${ageInDays} day${ageInDays !== 1 ? 's' : ''} ago`
|
|
335
|
-
};
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
return null;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Calculate a verification strength score for a user (0-100)
|
|
898
|
+
* Calculate verification strength score for a user (0-100)
|
|
343
899
|
* @param {string} address - The wallet address
|
|
344
|
-
* @returns {Promise<Object|null>} -
|
|
900
|
+
* @returns {Promise<Object|null>} - Verification strength data or null if no passport
|
|
345
901
|
*/
|
|
346
902
|
async getVerificationStrength(address) {
|
|
347
903
|
const passport = await this.getPassport(address);
|
|
348
904
|
if (!passport) return null;
|
|
349
905
|
|
|
350
|
-
const now = new Date();
|
|
351
906
|
let score = 0;
|
|
352
907
|
let breakdown = {
|
|
353
908
|
platformCount: 0,
|
|
354
909
|
ageBonus: 0,
|
|
355
910
|
diversityBonus: 0,
|
|
911
|
+
pointsBonus: 0,
|
|
356
912
|
totalScore: 0
|
|
357
913
|
};
|
|
358
914
|
|
|
359
|
-
// Base score:
|
|
915
|
+
// Base score: 15 points per verified platform (max 75 points for 5+ platforms)
|
|
360
916
|
const activePlatforms = passport.platforms.filter(platform =>
|
|
361
917
|
passport.verifications[platform].active
|
|
362
918
|
);
|
|
363
|
-
breakdown.platformCount = Math.min(activePlatforms.length *
|
|
919
|
+
breakdown.platformCount = Math.min(activePlatforms.length * 15, 75);
|
|
364
920
|
score += breakdown.platformCount;
|
|
365
921
|
|
|
366
|
-
// Age bonus: up to
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
breakdown.ageBonus = Math.min(Math.floor(ageInDays / 24.33), 15); // ~15 points at 1 year
|
|
372
|
-
score += breakdown.ageBonus;
|
|
373
|
-
}
|
|
922
|
+
// Age bonus: up to 10 points based on passport age
|
|
923
|
+
const now = new Date();
|
|
924
|
+
const ageInDays = Math.floor((now - passport.createdAt) / (1000 * 60 * 60 * 24));
|
|
925
|
+
breakdown.ageBonus = Math.min(Math.floor(ageInDays / 30), 10);
|
|
926
|
+
score += breakdown.ageBonus;
|
|
374
927
|
|
|
375
|
-
// Diversity bonus:
|
|
928
|
+
// Diversity bonus: 10 points if they have multiple platform types
|
|
376
929
|
const socialPlatforms = ['twitter', 'discord', 'telegram', 'instagram'];
|
|
377
930
|
const devPlatforms = ['github', 'gitlab'];
|
|
931
|
+
const chainPlatforms = ['solana', 'ethereum'];
|
|
378
932
|
|
|
379
|
-
const
|
|
380
|
-
|
|
933
|
+
const types = [
|
|
934
|
+
activePlatforms.some(p => socialPlatforms.includes(p)),
|
|
935
|
+
activePlatforms.some(p => devPlatforms.includes(p)),
|
|
936
|
+
activePlatforms.some(p => chainPlatforms.includes(p))
|
|
937
|
+
].filter(Boolean);
|
|
381
938
|
|
|
382
|
-
if (
|
|
383
|
-
breakdown.diversityBonus =
|
|
384
|
-
score +=
|
|
939
|
+
if (types.length >= 2) {
|
|
940
|
+
breakdown.diversityBonus = 10;
|
|
941
|
+
score += 10;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
// Points bonus: up to 5 points based on total points
|
|
945
|
+
if (passport.totalPoints > 0) {
|
|
946
|
+
breakdown.pointsBonus = Math.min(Math.floor(passport.totalPoints / 100), 5);
|
|
947
|
+
score += breakdown.pointsBonus;
|
|
385
948
|
}
|
|
386
949
|
|
|
387
950
|
breakdown.totalScore = Math.min(score, 100);
|
|
@@ -394,63 +957,54 @@ class PasslySDK {
|
|
|
394
957
|
breakdown.totalScore >= 20 ? 'D' : 'F',
|
|
395
958
|
breakdown,
|
|
396
959
|
activePlatforms: activePlatforms.length,
|
|
397
|
-
accountAge:
|
|
960
|
+
accountAge: ageInDays
|
|
398
961
|
};
|
|
399
|
-
}
|
|
962
|
+
}
|
|
400
963
|
|
|
401
964
|
/**
|
|
402
|
-
* Get
|
|
403
|
-
*
|
|
404
|
-
* In a real implementation, you might want to use The Graph or similar indexing
|
|
405
|
-
* @param {string} category - The category to search for
|
|
406
|
-
* @param {number} limit - Maximum number of results
|
|
407
|
-
* @param {number} startId - Starting passport ID to search from
|
|
408
|
-
* @returns {Promise<Array>} - Array of passport data matching the category
|
|
965
|
+
* Get system configuration
|
|
966
|
+
* @returns {Promise<Object>} - System configuration
|
|
409
967
|
*/
|
|
410
|
-
async
|
|
968
|
+
async getSystemConfig() {
|
|
411
969
|
this._ensureConnected();
|
|
412
970
|
|
|
413
|
-
const
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const maxAttempts = limit * 10; // Prevent infinite loops
|
|
971
|
+
const [supportedPlatforms, supportedCategories, leaderboardCategories] = await Promise.all([
|
|
972
|
+
this.getSupportedPlatforms().catch(() => []),
|
|
973
|
+
this.getSupportedCategories().catch(() => []),
|
|
974
|
+
this.getLeaderboardCategories().catch(() => [])
|
|
975
|
+
]);
|
|
419
976
|
|
|
420
|
-
|
|
977
|
+
let pointConfig = null;
|
|
978
|
+
if (this.contracts.rewards) {
|
|
421
979
|
try {
|
|
422
|
-
const [
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
results.push({
|
|
429
|
-
id: currentId,
|
|
430
|
-
owner,
|
|
431
|
-
createdAt: new Date(createdAt.toNumber() * 1000),
|
|
432
|
-
verificationCount: verificationCount.toNumber(),
|
|
433
|
-
category: passportCategory,
|
|
434
|
-
platforms
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
found++;
|
|
438
|
-
}
|
|
980
|
+
const [dailyHolding, referral, referee] = await this.contracts.rewards.getPointConfig();
|
|
981
|
+
pointConfig = {
|
|
982
|
+
dailyHolding: dailyHolding.toNumber(),
|
|
983
|
+
referral: referral.toNumber(),
|
|
984
|
+
referee: referee.toNumber()
|
|
985
|
+
};
|
|
439
986
|
} catch (error) {
|
|
440
|
-
//
|
|
987
|
+
// Ignore if not available
|
|
441
988
|
}
|
|
442
|
-
|
|
443
|
-
currentId++;
|
|
444
|
-
attempts++;
|
|
445
989
|
}
|
|
446
990
|
|
|
447
|
-
return
|
|
991
|
+
return {
|
|
992
|
+
supportedPlatforms,
|
|
993
|
+
supportedCategories,
|
|
994
|
+
leaderboardCategories,
|
|
995
|
+
pointConfig,
|
|
996
|
+
contracts: {
|
|
997
|
+
passly: this.config.addresses.passly,
|
|
998
|
+
platforms: this.config.addresses.platforms || null,
|
|
999
|
+
archives: this.config.addresses.archives || null,
|
|
1000
|
+
rewards: this.config.addresses.rewards || null,
|
|
1001
|
+
leaderboard: this.config.addresses.leaderboard || null
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
448
1004
|
}
|
|
449
1005
|
|
|
450
|
-
// PROOF HASH UTILITIES
|
|
451
|
-
|
|
452
1006
|
/**
|
|
453
|
-
* Get
|
|
1007
|
+
* Get proof hash for a specific platform verification
|
|
454
1008
|
* @param {string} address - The wallet address
|
|
455
1009
|
* @param {string} platform - The platform to get proof hash for
|
|
456
1010
|
* @returns {Promise<string|null>} - The proof hash or null if not verified
|
|
@@ -466,29 +1020,6 @@ class PasslySDK {
|
|
|
466
1020
|
|
|
467
1021
|
return verification.proofHash;
|
|
468
1022
|
}
|
|
469
|
-
|
|
470
|
-
/**
|
|
471
|
-
* Get all proof hashes for all verified platforms
|
|
472
|
-
* @param {string} address - The wallet address
|
|
473
|
-
* @returns {Promise<Object|null>} - Object mapping platforms to proof hashes or null if no passport
|
|
474
|
-
*/
|
|
475
|
-
async getAllProofHashes(address) {
|
|
476
|
-
const passport = await this.getPassport(address);
|
|
477
|
-
if (!passport) return null;
|
|
478
|
-
|
|
479
|
-
const proofHashes = {};
|
|
480
|
-
|
|
481
|
-
for (const platform of passport.platforms) {
|
|
482
|
-
const verification = passport.verifications[platform];
|
|
483
|
-
if (verification.active) {
|
|
484
|
-
proofHashes[platform] = verification.proofHash;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return proofHashes;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
|
|
492
1023
|
}
|
|
493
1024
|
|
|
494
1025
|
export default PasslySDK;
|