Notarize AT Protocol records on Ethereum using EAS (experiment)
at main 1.5 kB view raw
1import * as crypto from 'crypto'; 2 3export function parseRecordURI(uri: string): { did: string; collection: string; rkey: string } { 4 const match = uri.match(/^at:\/\/(did:[^\/]+)\/([^\/]+)\/(.+)$/); 5 if (!match) { 6 throw new Error('Invalid AT Protocol URI format. Expected: at://did:plc:xxx/collection/rkey'); 7 } 8 9 return { 10 did: match[1]!, 11 collection: match[2]!, 12 rkey: match[3]! 13 }; 14} 15 16// Recursively sort object keys for deterministic hashing 17function sortObject(obj: any): any { 18 if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) { 19 return obj; 20 } 21 22 const sorted: any = {}; 23 Object.keys(obj) 24 .sort() 25 .forEach(key => { 26 sorted[key] = sortObject(obj[key]); 27 }); 28 29 return sorted; 30} 31 32export function hashContent(content: any): string { 33 // Sort keys for deterministic hashing 34 const sortedContent = sortObject(content); 35 const jsonString = JSON.stringify(sortedContent); 36 return '0x' + crypto.createHash('sha256').update(jsonString).digest('hex'); 37} 38 39export function getExplorerURL(attestationUID: string, network: string = 'sepolia'): string { 40 const explorers: Record<string, string> = { 41 'sepolia': 'https://sepolia.easscan.org', 42 'base-sepolia': 'https://base-sepolia.easscan.org', 43 'base': 'https://base.easscan.org', 44 'optimism': 'https://optimism.easscan.org', 45 'arbitrum': 'https://arbitrum.easscan.org', 46 }; 47 48 const baseURL = explorers[network] || explorers['sepolia']; 49 return `${baseURL}/attestation/view/${attestationUID}`; 50}