import * as crypto from 'crypto'; export function parseRecordURI(uri: string): { did: string; collection: string; rkey: string } { const match = uri.match(/^at:\/\/(did:[^\/]+)\/([^\/]+)\/(.+)$/); if (!match) { throw new Error('Invalid AT Protocol URI format. Expected: at://did:plc:xxx/collection/rkey'); } return { did: match[1]!, collection: match[2]!, rkey: match[3]! }; } // Recursively sort object keys for deterministic hashing function sortObject(obj: any): any { if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) { return obj; } const sorted: any = {}; Object.keys(obj) .sort() .forEach(key => { sorted[key] = sortObject(obj[key]); }); return sorted; } export function hashContent(content: any): string { // Sort keys for deterministic hashing const sortedContent = sortObject(content); const jsonString = JSON.stringify(sortedContent); return '0x' + crypto.createHash('sha256').update(jsonString).digest('hex'); } export function getExplorerURL(attestationUID: string, network: string = 'sepolia'): string { const explorers: Record = { 'sepolia': 'https://sepolia.easscan.org', 'base-sepolia': 'https://base-sepolia.easscan.org', 'base': 'https://base.easscan.org', 'optimism': 'https://optimism.easscan.org', 'arbitrum': 'https://arbitrum.easscan.org', }; const baseURL = explorers[network] || explorers['sepolia']; return `${baseURL}/attestation/view/${attestationUID}`; }