@passly/passly-sdk 0.1.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/README.md +41 -0
- package/package.json +33 -0
- package/src/passly-sdk.js +495 -0
package/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Passly SDK
|
|
2
|
+
|
|
3
|
+
Official JavaScript SDK for the Passly identity protocol - a social identity layer that helps verify user identities and works as an anti-sybil tool.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @passly/passly-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import PasslySDK from '@passly/passly-sdk';
|
|
15
|
+
|
|
16
|
+
// Initialize the SDK
|
|
17
|
+
const passly = new PasslySDK();
|
|
18
|
+
|
|
19
|
+
// Connect to the contract
|
|
20
|
+
await passly.connect();
|
|
21
|
+
|
|
22
|
+
// Check if a user has a passport
|
|
23
|
+
const hasPassport = await passly.hasPassport('0x1234...');
|
|
24
|
+
|
|
25
|
+
// Get complete passport data
|
|
26
|
+
const passport = await passly.getPassport('0x1234...');
|
|
27
|
+
console.log(passport);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Core Features
|
|
31
|
+
|
|
32
|
+
- ✅ **Identity Verification**: Check if users have verified social accounts
|
|
33
|
+
- ✅ **Anti-Sybil Protection**: Verify unique identity across platforms
|
|
34
|
+
- ✅ **Multiple Platforms**: Twitter, Discord, GitHub, Telegram support
|
|
35
|
+
- ✅ **Verification Strength**: Calculate identity confidence scores
|
|
36
|
+
- ✅ **Account Age**: Track how long identities have been verified
|
|
37
|
+
|
|
38
|
+
## Links
|
|
39
|
+
|
|
40
|
+
- [Documentation](https://passly.xyz/docs)
|
|
41
|
+
- [Website](https://passly.xyz)
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@passly/passly-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Official SDK for Passly identity protocol - social identity verification for Web3",
|
|
6
|
+
"main": "src/passly-sdk.js",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"ethers": "^5.7.2"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"web3",
|
|
12
|
+
"identity",
|
|
13
|
+
"verification",
|
|
14
|
+
"did",
|
|
15
|
+
"social",
|
|
16
|
+
"anti-sybil",
|
|
17
|
+
"blockchain",
|
|
18
|
+
"ethereum",
|
|
19
|
+
"skale",
|
|
20
|
+
"oauth",
|
|
21
|
+
"passport"
|
|
22
|
+
],
|
|
23
|
+
"author": {
|
|
24
|
+
"name": "0xRetroDev",
|
|
25
|
+
"email": "hellO@0xRetro.dev",
|
|
26
|
+
"url": "https://github.com/0xRetroDev"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://passly.xyz",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
// passly-sdk.js
|
|
2
|
+
import { ethers } from 'ethers';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Passly SDK - A simple interface for interacting with the Passly identity protocol
|
|
6
|
+
*
|
|
7
|
+
* This SDK provides an easy way for developers to integrate with Passly,
|
|
8
|
+
* a social identity layer that helps verify user identities and works as an anti-sybil tool.
|
|
9
|
+
*/
|
|
10
|
+
class PasslySDK {
|
|
11
|
+
/**
|
|
12
|
+
* Initialize the Passly SDK
|
|
13
|
+
* @param {Object} config - Configuration options
|
|
14
|
+
* @param {string} [config.contractAddress] - Optional override for the Passly contract address
|
|
15
|
+
* @param {ethers.providers.Provider} [config.provider] - Optional ethers provider
|
|
16
|
+
* @param {ethers.Signer} [config.signer] - Optional ethers signer for write operations
|
|
17
|
+
*/
|
|
18
|
+
constructor(config = {}) {
|
|
19
|
+
// Default configuration will be set when connect() is called
|
|
20
|
+
this.config = config;
|
|
21
|
+
this.contract = null;
|
|
22
|
+
this.isConnected = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Connect to the Passly contract
|
|
27
|
+
* @param {Object} options - Optional connection parameters to override constructor config
|
|
28
|
+
* @returns {Promise<PasslySDK>} - Returns the SDK instance
|
|
29
|
+
*/
|
|
30
|
+
async connect(options = {}) {
|
|
31
|
+
const config = { ...this.config, ...options };
|
|
32
|
+
|
|
33
|
+
// Use default provider if none provided
|
|
34
|
+
if (!config.provider) {
|
|
35
|
+
config.provider = new ethers.providers.JsonRpcProvider('https://testnet.skalenodes.com/v1/aware-fake-trim-testnet');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If contract address isn't provided, use the default deployment
|
|
39
|
+
// This would typically be set to your production deployment
|
|
40
|
+
config.contractAddress = config.contractAddress || '0xB9eaC2E3f9c1171fB9d05eFC8D7af311684B9113';
|
|
41
|
+
|
|
42
|
+
// Load contract ABI
|
|
43
|
+
const abi = [
|
|
44
|
+
"function getPassportByAddress(address user) external view returns (uint256)",
|
|
45
|
+
"function getPassportData(uint256 passportId) external view returns (address owner, uint256 createdAt, uint256 verificationCount, string memory category)",
|
|
46
|
+
"function getVerifiedPlatforms(uint256 passportId) external view returns (string[] memory)",
|
|
47
|
+
"function getVerification(uint256 passportId, string calldata platform) external view returns (string memory identifier, uint256 verifiedAt, bytes32 proofHash, bool active)",
|
|
48
|
+
"function isIdentifierVerified(string calldata platform, string calldata identifier) external view returns (bool isVerified, uint256 passportId)",
|
|
49
|
+
"function getSupportedPlatforms() external view returns (string[] memory)",
|
|
50
|
+
"function getSupportedCategories() external view returns (string[] memory)",
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Initialize the contract
|
|
54
|
+
if (config.signer) {
|
|
55
|
+
// Connect with signer for write operations
|
|
56
|
+
this.contract = new ethers.Contract(config.contractAddress, abi, config.signer);
|
|
57
|
+
} else {
|
|
58
|
+
// Connect with provider for read-only operations
|
|
59
|
+
this.contract = new ethers.Contract(config.contractAddress, abi, config.provider);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.config = config;
|
|
63
|
+
this.isConnected = true;
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Ensure the SDK is connected before performing operations
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
_ensureConnected() {
|
|
72
|
+
if (!this.isConnected) {
|
|
73
|
+
throw new Error('Passly SDK not connected. Call connect() first.');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get a user's passport ID from their wallet address
|
|
79
|
+
* @param {string} address - The wallet address to check
|
|
80
|
+
* @returns {Promise<number|null>} - The passport ID or null if none exists
|
|
81
|
+
*/
|
|
82
|
+
async getPassportId(address) {
|
|
83
|
+
this._ensureConnected();
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const passportId = await this.contract.getPassportByAddress(address);
|
|
87
|
+
return passportId.toNumber();
|
|
88
|
+
} catch (error) {
|
|
89
|
+
// If the error is "User has no passport", return null
|
|
90
|
+
if (error.message.includes('User has no passport')) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a user has a passport
|
|
99
|
+
* @param {string} address - The wallet address to check
|
|
100
|
+
* @returns {Promise<boolean>} - Whether the user has a passport
|
|
101
|
+
*/
|
|
102
|
+
async hasPassport(address) {
|
|
103
|
+
const passportId = await this.getPassportId(address);
|
|
104
|
+
return passportId !== null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get complete passport data for a user
|
|
109
|
+
* @param {string} address - The wallet address
|
|
110
|
+
* @returns {Promise<Object|null>} - The passport data or null if no passport
|
|
111
|
+
*/
|
|
112
|
+
async getPassport(address) {
|
|
113
|
+
this._ensureConnected();
|
|
114
|
+
|
|
115
|
+
let passportId;
|
|
116
|
+
try {
|
|
117
|
+
passportId = await this.getPassportId(address);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (!passportId) return null;
|
|
123
|
+
|
|
124
|
+
const [owner, createdAt, verificationCount, category] = await this.contract.getPassportData(passportId);
|
|
125
|
+
const platforms = await this.contract.getVerifiedPlatforms(passportId);
|
|
126
|
+
|
|
127
|
+
// Gather all verifications
|
|
128
|
+
const verifications = {};
|
|
129
|
+
for (const platform of platforms) {
|
|
130
|
+
const [identifier, verifiedAt, proofHash, active] = await this.contract.getVerification(
|
|
131
|
+
passportId,
|
|
132
|
+
platform
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
verifications[platform] = {
|
|
136
|
+
identifier,
|
|
137
|
+
verifiedAt: new Date(verifiedAt.toNumber() * 1000),
|
|
138
|
+
proofHash: proofHash,
|
|
139
|
+
active
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
id: passportId,
|
|
145
|
+
owner: owner,
|
|
146
|
+
createdAt: new Date(createdAt.toNumber() * 1000),
|
|
147
|
+
verificationCount: verificationCount.toNumber(),
|
|
148
|
+
category,
|
|
149
|
+
platforms,
|
|
150
|
+
verifications
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get all verification data for a passport
|
|
156
|
+
* @param {number} passportId - The passport ID
|
|
157
|
+
* @returns {Promise<Object>} - Object containing verification details
|
|
158
|
+
*/
|
|
159
|
+
async getVerifications(passportId) {
|
|
160
|
+
this._ensureConnected();
|
|
161
|
+
|
|
162
|
+
const platforms = await this.contract.getVerifiedPlatforms(passportId);
|
|
163
|
+
const verifications = {};
|
|
164
|
+
|
|
165
|
+
for (const platform of platforms) {
|
|
166
|
+
const [identifier, verifiedAt, proofHash, active] = await this.contract.getVerification(
|
|
167
|
+
passportId,
|
|
168
|
+
platform
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
verifications[platform] = {
|
|
172
|
+
identifier,
|
|
173
|
+
verifiedAt: new Date(verifiedAt.toNumber() * 1000),
|
|
174
|
+
proofHash: proofHash,
|
|
175
|
+
active
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return verifications;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if a social media account identifier is verified
|
|
184
|
+
* @param {string} platform - The platform (e.g., "twitter")
|
|
185
|
+
* @param {string} identifier - The account identifier (e.g., username)
|
|
186
|
+
* @returns {Promise<{isVerified: boolean, passportId: number|null}>} - Verification status and passport ID if verified
|
|
187
|
+
*/
|
|
188
|
+
async isAccountVerified(platform, identifier) {
|
|
189
|
+
this._ensureConnected();
|
|
190
|
+
|
|
191
|
+
const [isVerified, passportId] = await this.contract.isIdentifierVerified(platform, identifier);
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
isVerified,
|
|
195
|
+
passportId: isVerified ? passportId.toNumber() : null
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get supported platforms
|
|
201
|
+
* @returns {Promise<string[]>} - List of supported platforms
|
|
202
|
+
*/
|
|
203
|
+
async getSupportedPlatforms() {
|
|
204
|
+
this._ensureConnected();
|
|
205
|
+
return await this.contract.getSupportedPlatforms();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Get supported categories
|
|
210
|
+
* @returns {Promise<string[]>} - List of supported categories
|
|
211
|
+
*/
|
|
212
|
+
async getSupportedCategories() {
|
|
213
|
+
this._ensureConnected();
|
|
214
|
+
return await this.contract.getSupportedCategories();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Helper method to get a user's active verifications by wallet address
|
|
219
|
+
* @param {string} address - The wallet address
|
|
220
|
+
* @returns {Promise<Object|null>} - Object of platforms and identifiers, or null if no passport
|
|
221
|
+
*/
|
|
222
|
+
async getUserVerifications(address) {
|
|
223
|
+
const passport = await this.getPassport(address);
|
|
224
|
+
if (!passport) return null;
|
|
225
|
+
|
|
226
|
+
const result = {};
|
|
227
|
+
for (const platform in passport.verifications) {
|
|
228
|
+
const verification = passport.verifications[platform];
|
|
229
|
+
if (verification.active) {
|
|
230
|
+
result[platform] = verification.identifier;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Check if a user has verified a specific platform
|
|
239
|
+
* @param {string} address - The wallet address
|
|
240
|
+
* @param {string} platform - The platform to check
|
|
241
|
+
* @returns {Promise<boolean>} - Whether the user has verified this platform
|
|
242
|
+
*/
|
|
243
|
+
async hasVerifiedPlatform(address, platform) {
|
|
244
|
+
const verifications = await this.getUserVerifications(address);
|
|
245
|
+
return verifications !== null && verifications[platform.toLowerCase()] !== undefined;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get a user's identifier on a specific platform
|
|
250
|
+
* @param {string} address - The wallet address
|
|
251
|
+
* @param {string} platform - The platform to check
|
|
252
|
+
* @returns {Promise<string|null>} - The identifier or null if not verified
|
|
253
|
+
*/
|
|
254
|
+
async getPlatformIdentifier(address, platform) {
|
|
255
|
+
const verifications = await this.getUserVerifications(address);
|
|
256
|
+
if (!verifications) return null;
|
|
257
|
+
return verifications[platform.toLowerCase()] || null;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// CONVENIENCE FUNCTIONS FOR ENHANCED IDENTITY VERIFICATION
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get how long a user has been verified on a specific platform
|
|
264
|
+
* @param {string} address - The wallet address
|
|
265
|
+
* @param {string} platform - The platform to check
|
|
266
|
+
* @returns {Promise<Object|null>} - Object with verification age data or null if not verified
|
|
267
|
+
*/
|
|
268
|
+
async getVerificationAge(address, platform) {
|
|
269
|
+
const passport = await this.getPassport(address);
|
|
270
|
+
if (!passport || !passport.verifications[platform.toLowerCase()]) {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const verification = passport.verifications[platform.toLowerCase()];
|
|
275
|
+
if (!verification.active) return null;
|
|
276
|
+
|
|
277
|
+
const now = new Date();
|
|
278
|
+
const verifiedAt = verification.verifiedAt;
|
|
279
|
+
const ageInMs = now - verifiedAt;
|
|
280
|
+
const ageInDays = Math.floor(ageInMs / (1000 * 60 * 60 * 24));
|
|
281
|
+
const ageInHours = Math.floor(ageInMs / (1000 * 60 * 60));
|
|
282
|
+
const ageInMinutes = Math.floor(ageInMs / (1000 * 60));
|
|
283
|
+
|
|
284
|
+
return {
|
|
285
|
+
platform: platform.toLowerCase(),
|
|
286
|
+
identifier: verification.identifier,
|
|
287
|
+
verifiedAt: verifiedAt,
|
|
288
|
+
ageInMs,
|
|
289
|
+
ageInMinutes,
|
|
290
|
+
ageInHours,
|
|
291
|
+
ageInDays,
|
|
292
|
+
humanReadable: ageInDays > 0
|
|
293
|
+
? `${ageInDays} day${ageInDays !== 1 ? 's' : ''}`
|
|
294
|
+
: ageInHours > 0
|
|
295
|
+
? `${ageInHours} hour${ageInHours !== 1 ? 's' : ''}`
|
|
296
|
+
: `${ageInMinutes} minute${ageInMinutes !== 1 ? 's' : ''}`
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Get the earliest verification for a user (their "account age" in the Passly ecosystem)
|
|
302
|
+
* @param {string} address - The wallet address
|
|
303
|
+
* @returns {Promise<Object|null>} - Object with earliest verification data or null if no verifications
|
|
304
|
+
*/
|
|
305
|
+
async getEarliestVerification(address) {
|
|
306
|
+
const passport = await this.getPassport(address);
|
|
307
|
+
if (!passport || passport.platforms.length === 0) {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
let earliest = null;
|
|
312
|
+
let earliestDate = null;
|
|
313
|
+
|
|
314
|
+
for (const platform of passport.platforms) {
|
|
315
|
+
const verification = passport.verifications[platform];
|
|
316
|
+
if (verification.active && (!earliestDate || verification.verifiedAt < earliestDate)) {
|
|
317
|
+
earliestDate = verification.verifiedAt;
|
|
318
|
+
earliest = {
|
|
319
|
+
platform,
|
|
320
|
+
identifier: verification.identifier,
|
|
321
|
+
verifiedAt: verification.verifiedAt,
|
|
322
|
+
proofHash: verification.proofHash
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (earliest) {
|
|
328
|
+
const now = new Date();
|
|
329
|
+
const ageInMs = now - earliest.verifiedAt;
|
|
330
|
+
const ageInDays = Math.floor(ageInMs / (1000 * 60 * 60 * 24));
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
...earliest,
|
|
334
|
+
ageInDays,
|
|
335
|
+
humanReadable: `${ageInDays} day${ageInDays !== 1 ? 's' : ''} ago`
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Calculate a verification strength score for a user (0-100)
|
|
344
|
+
* @param {string} address - The wallet address
|
|
345
|
+
* @returns {Promise<Object|null>} - Object with verification strength data or null if no passport
|
|
346
|
+
*/
|
|
347
|
+
async getVerificationStrength(address) {
|
|
348
|
+
const passport = await this.getPassport(address);
|
|
349
|
+
if (!passport) return null;
|
|
350
|
+
|
|
351
|
+
const now = new Date();
|
|
352
|
+
let score = 0;
|
|
353
|
+
let breakdown = {
|
|
354
|
+
platformCount: 0,
|
|
355
|
+
ageBonus: 0,
|
|
356
|
+
diversityBonus: 0,
|
|
357
|
+
totalScore: 0
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// Base score: 20 points per verified platform (max 80 points for 4+ platforms)
|
|
361
|
+
const activePlatforms = passport.platforms.filter(platform =>
|
|
362
|
+
passport.verifications[platform].active
|
|
363
|
+
);
|
|
364
|
+
breakdown.platformCount = Math.min(activePlatforms.length * 20, 80);
|
|
365
|
+
score += breakdown.platformCount;
|
|
366
|
+
|
|
367
|
+
// Age bonus: up to 15 points based on earliest verification age
|
|
368
|
+
const earliest = await this.getEarliestVerification(address);
|
|
369
|
+
if (earliest) {
|
|
370
|
+
const ageInDays = earliest.ageInDays;
|
|
371
|
+
// Give more points for older verifications (max 15 points at 365+ days)
|
|
372
|
+
breakdown.ageBonus = Math.min(Math.floor(ageInDays / 24.33), 15); // ~15 points at 1 year
|
|
373
|
+
score += breakdown.ageBonus;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// Diversity bonus: 5 points if they have both social and development platforms
|
|
377
|
+
const socialPlatforms = ['twitter', 'discord', 'telegram', 'instagram'];
|
|
378
|
+
const devPlatforms = ['github', 'gitlab'];
|
|
379
|
+
|
|
380
|
+
const hasSocial = activePlatforms.some(platform => socialPlatforms.includes(platform));
|
|
381
|
+
const hasDev = activePlatforms.some(platform => devPlatforms.includes(platform));
|
|
382
|
+
|
|
383
|
+
if (hasSocial && hasDev) {
|
|
384
|
+
breakdown.diversityBonus = 5;
|
|
385
|
+
score += 5;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
breakdown.totalScore = Math.min(score, 100);
|
|
389
|
+
|
|
390
|
+
return {
|
|
391
|
+
score: breakdown.totalScore,
|
|
392
|
+
grade: breakdown.totalScore >= 80 ? 'A' :
|
|
393
|
+
breakdown.totalScore >= 60 ? 'B' :
|
|
394
|
+
breakdown.totalScore >= 40 ? 'C' :
|
|
395
|
+
breakdown.totalScore >= 20 ? 'D' : 'F',
|
|
396
|
+
breakdown,
|
|
397
|
+
activePlatforms: activePlatforms.length,
|
|
398
|
+
accountAge: earliest ? earliest.ageInDays : 0
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Get users by category (requires querying multiple passport IDs)
|
|
404
|
+
* Note: This is a simplified version that checks known passport IDs
|
|
405
|
+
* In a real implementation, you might want to use The Graph or similar indexing
|
|
406
|
+
* @param {string} category - The category to search for
|
|
407
|
+
* @param {number} limit - Maximum number of results
|
|
408
|
+
* @param {number} startId - Starting passport ID to search from
|
|
409
|
+
* @returns {Promise<Array>} - Array of passport data matching the category
|
|
410
|
+
*/
|
|
411
|
+
async getUsersByCategory(category, limit = 10, startId = 1) {
|
|
412
|
+
this._ensureConnected();
|
|
413
|
+
|
|
414
|
+
const results = [];
|
|
415
|
+
const normalizedCategory = category.toLowerCase();
|
|
416
|
+
let currentId = startId;
|
|
417
|
+
let found = 0;
|
|
418
|
+
let attempts = 0;
|
|
419
|
+
const maxAttempts = limit * 10; // Prevent infinite loops
|
|
420
|
+
|
|
421
|
+
while (found < limit && attempts < maxAttempts) {
|
|
422
|
+
try {
|
|
423
|
+
const [owner, createdAt, verificationCount, passportCategory] =
|
|
424
|
+
await this.contract.getPassportData(currentId);
|
|
425
|
+
|
|
426
|
+
if (passportCategory.toLowerCase() === normalizedCategory) {
|
|
427
|
+
const platforms = await this.contract.getVerifiedPlatforms(currentId);
|
|
428
|
+
|
|
429
|
+
results.push({
|
|
430
|
+
id: currentId,
|
|
431
|
+
owner,
|
|
432
|
+
createdAt: new Date(createdAt.toNumber() * 1000),
|
|
433
|
+
verificationCount: verificationCount.toNumber(),
|
|
434
|
+
category: passportCategory,
|
|
435
|
+
platforms
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
found++;
|
|
439
|
+
}
|
|
440
|
+
} catch (error) {
|
|
441
|
+
// Passport doesn't exist, continue to next ID
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
currentId++;
|
|
445
|
+
attempts++;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return results;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// PROOF HASH UTILITIES
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Get the proof hash for a specific platform verification
|
|
455
|
+
* @param {string} address - The wallet address
|
|
456
|
+
* @param {string} platform - The platform to get proof hash for
|
|
457
|
+
* @returns {Promise<string|null>} - The proof hash or null if not verified
|
|
458
|
+
*/
|
|
459
|
+
async getProofHash(address, platform) {
|
|
460
|
+
const passport = await this.getPassport(address);
|
|
461
|
+
if (!passport || !passport.verifications[platform.toLowerCase()]) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
const verification = passport.verifications[platform.toLowerCase()];
|
|
466
|
+
if (!verification.active) return null;
|
|
467
|
+
|
|
468
|
+
return verification.proofHash;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Get all proof hashes for all verified platforms
|
|
473
|
+
* @param {string} address - The wallet address
|
|
474
|
+
* @returns {Promise<Object|null>} - Object mapping platforms to proof hashes or null if no passport
|
|
475
|
+
*/
|
|
476
|
+
async getAllProofHashes(address) {
|
|
477
|
+
const passport = await this.getPassport(address);
|
|
478
|
+
if (!passport) return null;
|
|
479
|
+
|
|
480
|
+
const proofHashes = {};
|
|
481
|
+
|
|
482
|
+
for (const platform of passport.platforms) {
|
|
483
|
+
const verification = passport.verifications[platform];
|
|
484
|
+
if (verification.active) {
|
|
485
|
+
proofHashes[platform] = verification.proofHash;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return proofHashes;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
export default PasslySDK;
|