story-research-zapwall/lib/lightningAddress.ts
Nicolas Cantu 4735ee71ab feat: Complétion système split et intégrations externes
- Intégration mempool.space pour vérification transactions Bitcoin :
  - Service MempoolSpaceService avec API mempool.space
  - Vérification sorties et montants pour sponsoring
  - Vérification confirmations
  - Attente confirmation avec polling

- Récupération adresses Lightning depuis profils Nostr :
  - Service LightningAddressService
  - Support lud16 et lud06 (NIP-19)
  - Cache avec TTL 1 heure
  - Intégré dans paymentPolling et reviewReward

- Mise à jour événements Nostr pour avis rémunérés :
  - Publication événement avec tags rewarded et reward_amount
  - Parsing tags dans parseReviewFromEvent
  - Vérification doublons

- Tracking sponsoring sur Nostr :
  - Service SponsoringTrackingService
  - Événements avec commissions et confirmations
  - Intégration vérification mempool.space

Toutes les fonctionnalités de split sont maintenant opérationnelles.
Seuls les transferts Lightning réels nécessitent un nœud Lightning.
2025-12-27 21:18:14 +01:00

104 lines
2.6 KiB
TypeScript

import { nostrService } from './nostr'
import type { NostrProfile } from '@/types/nostr'
/**
* Lightning address service
* Retrieves Lightning addresses from Nostr profiles
*
* Supports:
* - lud16: Lightning address format (user@domain.com)
* - lud06: LNURL format
*/
export class LightningAddressService {
private addressCache: Map<string, string | null> = new Map()
private readonly CACHE_TTL = 3600000 // 1 hour
/**
* Get Lightning address from Nostr profile
* Checks lud16 first, then lud06
*/
async getLightningAddress(pubkey: string): Promise<string | null> {
// Check cache first
const cached = this.addressCache.get(pubkey)
if (cached !== undefined) {
return cached
}
try {
const profile = await nostrService.getProfile(pubkey)
if (!profile) {
this.addressCache.set(pubkey, null)
return null
}
const address = this.extractLightningAddress(profile)
this.addressCache.set(pubkey, address)
return address
} catch (error) {
console.error('Error getting Lightning address', {
pubkey,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString(),
})
this.addressCache.set(pubkey, null)
return null
}
}
/**
* Extract Lightning address from profile
* Checks lud16 first, then lud06
*/
private extractLightningAddress(profile: NostrProfile): string | null {
// Check if profile has extended fields (lud16, lud06)
const extendedProfile = profile as NostrProfile & {
lud16?: string
lud06?: string
}
// Prefer lud16 (Lightning address format)
if (extendedProfile.lud16) {
return extendedProfile.lud16
}
// Fallback to lud06 (LNURL format)
if (extendedProfile.lud06) {
return extendedProfile.lud06
}
return null
}
/**
* Clear cache for a specific pubkey
*/
clearCache(pubkey: string): void {
this.addressCache.delete(pubkey)
}
/**
* Clear all cache
*/
clearAllCache(): void {
this.addressCache.clear()
}
/**
* Get Lightning addresses for multiple pubkeys
*/
async getLightningAddresses(pubkeys: string[]): Promise<Map<string, string | null>> {
const addresses = new Map<string, string | null>()
const promises = pubkeys.map(async (pubkey) => {
const address = await this.getLightningAddress(pubkey)
addresses.set(pubkey, address)
})
await Promise.all(promises)
return addresses
}
}
export const lightningAddressService = new LightningAddressService()