UNPKG

@mustafair/reputation-sdk

Version:

Official SDK for integrating CARV ID authentication and MustaFair reputation systems into decentralized applications

368 lines (364 loc) 13.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); class Fair3ReputationSDK { constructor(baseUrl = 'https://musta-fair.vercel.app', timeout = 10000, authConfig) { this.walletConnection = null; this.authConfig = null; this.baseUrl = baseUrl.replace(/\/$/, ''); this.timeout = timeout; this.authConfig = { contractAddress: authConfig?.contractAddress || Fair3ReputationSDK.DEFAULT_CONTRACTS[97], chainId: authConfig?.chainId || 97, requiredChains: authConfig?.requiredChains || [97, 31337], signMessageTemplate: authConfig?.signMessageTemplate || 'Sign this message to authenticate with CARV ID: {timestamp}', ...authConfig }; } async initialize(walletConnection) { if (walletConnection) { this.walletConnection = walletConnection; } if (this.walletConnection && !this.isValidAddress(this.walletConnection.address)) { throw new Error('Invalid wallet address in connection'); } } setWalletConnection(connection) { this.walletConnection = connection; } getWalletConnection() { return this.walletConnection; } isWalletReady() { return !!(this.walletConnection?.isConnected && this.walletConnection?.address && this.authConfig?.requiredChains?.includes(this.walletConnection.chainId)); } async authenticateWithCarvId(signMessage, options) { if (!this.walletConnection?.isConnected) { return { success: false, error: 'Wallet not connected' }; } if (!this.isWalletReady()) { return { success: false, error: 'Wallet not on supported network' }; } try { const hasIdentity = await this.hasCarvId(this.walletConnection.address); if (!hasIdentity) { return { success: false, error: 'No CARV ID found for this wallet. Please mint a CARV ID first.' }; } const timestamp = Date.now(); const message = this.authConfig.signMessageTemplate.replace('{timestamp}', timestamp.toString()); const signature = await signMessage(message); const profile = await this.getCarvIdProfile(this.walletConnection.address); const authData = { address: this.walletConnection.address, tokenId: profile.tokenId || '', signature, message, metadata: options?.fetchMetadata ? profile.metadata : undefined, web2Achievements: options?.fetchWeb2Achievements ? profile.web2Achievements : undefined, identityHash: this.generateIdentityHash({ address: this.walletConnection.address, tokenId: profile.tokenId || '', timestamp }) }; return { success: true, user: authData }; } catch (error) { return { success: false, error: error.message || 'Authentication failed' }; } } generateIdentityHash(data) { const str = JSON.stringify(data); let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; } return Math.abs(hash).toString(16); } async prepareCarvIdMint(recipient) { if (!this.authConfig) { throw new Error('Authentication config not set'); } const targetRecipient = recipient || this.walletConnection?.address; if (!targetRecipient) { throw new Error('No recipient address provided'); } return { to: this.authConfig.contractAddress, data: `0x40c10f19${targetRecipient.slice(2).padStart(64, '0')}${'1'.padStart(64, '0')}`, value: '0', chainId: this.authConfig.chainId }; } static getSupportedChains() { return Fair3ReputationSDK.DEFAULT_CHAINS; } static getDefaultContracts() { return Fair3ReputationSDK.DEFAULT_CONTRACTS; } isValidAddress(address) { return /^0x[a-fA-F0-9]{40}$/.test(address); } async makeRequest(url) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); const response = await fetch(url, { signal: controller.signal, headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, }); clearTimeout(timeoutId); const data = await response.json(); if (!response.ok) { throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`); } return data; } catch (error) { if (error.name === 'AbortError') { throw new Error(`Request timeout after ${this.timeout}ms`); } throw error; } } async getProfile(address) { if (!this.isValidAddress(address)) { throw new Error('Invalid Ethereum address format'); } const response = await this.makeRequest(`${this.baseUrl}/api/public/reputation/${address}`); if (!response.success) { throw new Error(response.error || 'Failed to fetch profile data'); } return response.data; } async getCarvIdProfile(address) { if (!this.isValidAddress(address)) { throw new Error('Invalid Ethereum address format'); } const response = await this.makeRequest(`${this.baseUrl}/api/public/carv-id/${address}`); if (!response.success) { throw new Error(response.error || 'Failed to fetch CARV ID profile'); } return response.data; } async getReputation(address) { const profile = await this.getProfile(address); return profile.reputation; } async getLeaderboard(filters = {}) { const params = new URLSearchParams(); if (filters.page) params.append('page', filters.page.toString()); if (filters.limit) params.append('limit', filters.limit.toString()); if (filters.tier) params.append('tier', filters.tier); if (filters.sortBy) params.append('sortBy', filters.sortBy); if (filters.sortOrder) params.append('sortOrder', filters.sortOrder); const url = `${this.baseUrl}/api/public/leaderboard${params.toString() ? '?' + params.toString() : ''}`; const response = await this.makeRequest(url); if (!response.success) { throw new Error(response.error || 'Failed to fetch leaderboard'); } return response.data; } async getStats() { const response = await this.makeRequest(`${this.baseUrl}/api/public/stats`); if (!response.success) { throw new Error(response.error || 'Failed to fetch platform statistics'); } return response.data; } async getTopPerformers(tier, limit = 10) { const leaderboard = await this.getLeaderboard({ tier, limit, sortBy: 'score', sortOrder: 'desc' }); return leaderboard.reputations; } async hasReputation(address) { try { const reputation = await this.getReputation(address); return reputation !== null && reputation.isActive; } catch (error) { return false; } } async hasCarvId(address) { try { const profile = await this.getCarvIdProfile(address); return profile.hasIdentity; } catch (error) { return false; } } async getTierInfo(address) { const reputation = await this.getReputation(address); if (!reputation) { return null; } return { tier: reputation.tier, score: reputation.contributionScore, tierLevel: reputation.tierLevel }; } async searchByTier(tier, page = 1, limit = 20) { const leaderboard = await this.getLeaderboard({ tier, page, limit }); return leaderboard.reputations; } async getDocs() { const response = await this.makeRequest(`${this.baseUrl}/api/public/docs`); return response; } static create(baseUrl, timeout, authConfig) { return new Fair3ReputationSDK(baseUrl, timeout, authConfig); } static validateAddresses(addresses) { const valid = []; const invalid = []; addresses.forEach(addr => { if (/^0x[a-fA-F0-9]{40}$/.test(addr)) { valid.push(addr); } else { invalid.push(addr); } }); return { valid, invalid }; } static getTierValue(tier) { const tierValues = { 'Bronze': 0, 'Silver': 1, 'Gold': 2, 'Platinum': 3 }; return tierValues[tier] || 0; } static formatScore(score) { if (score >= 1000000) return `${(score / 1000000).toFixed(1)}M`; if (score >= 1000) return `${(score / 1000).toFixed(1)}K`; return score.toString(); } async checkReputationRequirements(address, requirements) { const reasons = []; try { const profile = await this.getProfile(address); if (requirements.requireCarvId && !profile.carvId) { reasons.push('CARV ID required but not found'); } if (!profile.reputation) { reasons.push('No reputation NFT found'); return { meets: false, reasons }; } if (requirements.minScore && profile.reputation.contributionScore < requirements.minScore) { reasons.push(`Score ${profile.reputation.contributionScore} below minimum ${requirements.minScore}`); } if (requirements.minTier) { const currentTierValue = Fair3ReputationSDK.getTierValue(profile.reputation.tier); const requiredTierValue = Fair3ReputationSDK.getTierValue(requirements.minTier); if (currentTierValue < requiredTierValue) { reasons.push(`Tier ${profile.reputation.tier} below minimum ${requirements.minTier}`); } } return { meets: reasons.length === 0, reasons }; } catch (error) { reasons.push(`Error checking requirements: ${error}`); return { meets: false, reasons }; } } async getBatchProfiles(addresses, options) { const maxConcurrent = options?.maxConcurrent || 5; const results = []; for (let i = 0; i < addresses.length; i += maxConcurrent) { const batch = addresses.slice(i, i + maxConcurrent); const batchPromises = batch.map(async (address) => { try { const profile = await this.getProfile(address); return { address, profile }; } catch (error) { if (options?.includeErrors) { return { address, error: error.message }; } return { address }; } }); const batchResults = await Promise.all(batchPromises); results.push(...batchResults); } return results; } } Fair3ReputationSDK.DEFAULT_CONTRACTS = { 97: "0x742B6A2A5e29Ad0C20a78B5b6dE55fB2E8B1e8C3", 31337: "0x742B6A2A5e29Ad0C20a78B5b6dE55fB2E8B1e8C3", }; Fair3ReputationSDK.DEFAULT_CHAINS = [ { id: 97, name: 'BSC Testnet', rpcUrls: ['https://data-seed-prebsc-1-s1.binance.org:8545/'], nativeCurrency: { name: 'BNB', symbol: 'tBNB', decimals: 18 } }, { id: 31337, name: 'Localhost', rpcUrls: ['http://127.0.0.1:8545'], nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 } } ]; const createSDKHooks = (sdk) => { return { useProfile: (address) => { return { data: null, loading: false, error: null, refetch: async () => address ? await sdk.getProfile(address) : null }; }, useAuthentication: () => { return { authenticate: sdk.authenticateWithCarvId.bind(sdk), isReady: sdk.isWalletReady(), connection: sdk.getWalletConnection() }; } }; }; const FAIR3_CONSTANTS = { SUPPORTED_CHAINS: Fair3ReputationSDK.DEFAULT_CHAINS, CONTRACT_ADDRESSES: Fair3ReputationSDK.DEFAULT_CONTRACTS, TIER_VALUES: { 'Bronze': 0, 'Silver': 1, 'Gold': 2, 'Platinum': 3 }, API_ENDPOINTS: { REPUTATION: '/api/public/reputation', CARV_ID: '/api/public/carv-id', LEADERBOARD: '/api/public/leaderboard', STATS: '/api/public/stats' } }; const fair3SDK = new Fair3ReputationSDK(); exports.FAIR3_CONSTANTS = FAIR3_CONSTANTS; exports.Fair3ReputationSDK = Fair3ReputationSDK; exports.createSDKHooks = createSDKHooks; exports.default = Fair3ReputationSDK; exports.fair3SDK = fair3SDK; //# sourceMappingURL=index.js.map