UNPKG

@pod-protocol/sdk

Version:

TypeScript SDK for PoD Protocol - AI agent communication on Solana

676 lines (673 loc) 21.9 kB
import { P as PROGRAM_ID, A as getAddressEncoder, H as getProgramDerivedAddress, M as MessageType, O as address } from './types-CllXlTfL.js'; /** * Deterministic hash function for consistent ID generation */ function hashStringDeterministic(input) { let hash = 0; for (let i = 0; i < input.length; i++) { const char = input.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return Math.abs(hash).toString(36); } // Helper function to convert string to bytes function stringToBytes(str) { if (typeof TextEncoder !== 'undefined') { return new TextEncoder().encode(str); } // Fallback for environments without TextEncoder const bytes = new Uint8Array(str.length); for (let i = 0; i < str.length; i++) { bytes[i] = str.charCodeAt(i) & 0xFF; } return bytes; } // Helper function to convert address to bytes function addressToBytes(addr) { // Web3.js v2 Address can be converted to string first const addrString = String(addr); // Base58 decode - for now use a simplified approach // In production, this should use proper base58 decoding const bytes = new Uint8Array(32); // This is a placeholder - in real implementation, we'd need proper base58 decoding // For now, we'll use a hash of the string as a workaround const encoder = new TextEncoder(); const stringBytes = encoder.encode(addrString); // Simple hash function to generate 32 bytes from address string for (let i = 0; i < 32; i++) { bytes[i] = stringBytes[i % stringBytes.length] ^ (i * 7); } return bytes; } // Enhanced PDA derivation using proper Solana patterns async function derivePDA(seeds, programId) { // Use deterministic PDA derivation following Solana patterns return deriveCompatiblePDA(seeds, programId); } // Fallback PDA derivation for compatibility async function deriveCompatiblePDA(seeds, programId) { // More sophisticated derivation that follows Solana patterns // Combine all seeds with program ID let totalLength = 0; seeds.forEach(seed => totalLength += seed.length); const combined = new Uint8Array(totalLength + 32 + 16); // Extra space for PDA marker let offset = 0; seeds.forEach(seed => { combined.set(seed, offset); offset += seed.length; }); // Add program ID bytes const programIdBytes = addressToBytes(programId); combined.set(programIdBytes, offset); offset += 32; // Add PDA marker (following Solana convention) const pdaMarker = new TextEncoder().encode("ProgramDerivedAddress"); combined.set(pdaMarker.slice(0, Math.min(16, pdaMarker.length)), offset); // Try different bump values (Solana-style bump finding) for (let bump = 255; bump >= 0; bump--) { const combinedWithBump = new Uint8Array(combined.length + 1); combinedWithBump.set(combined); combinedWithBump[combined.length] = bump; // Create hash let hashArray; if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle) { const hash = await globalThis.crypto.subtle.digest('SHA-256', combinedWithBump); hashArray = new Uint8Array(hash); } else { hashArray = simpleHash(combinedWithBump); } // Check if this creates a valid PDA (not on the curve) // Simplified check: ensure it's not all zeros and has some variation const isValid = hashArray.some((byte, index) => byte !== hashArray[0]) && hashArray[0] !== 0; if (isValid) { // Convert to base58-style address const chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; let result = ''; for (let i = 0; i < 32; i++) { result += chars[hashArray[i] % chars.length]; } // Ensure consistent length while (result.length < 44) { result += chars[hashArray[(result.length * 7) % 32] % chars.length]; } result = result.substring(0, 44); return [address(result), bump]; } } // Fallback if no valid bump found return [programId, 255]; } /** * Calculate PDA for an agent account */ async function findAgentPDA(walletAddress, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("agent"), addressEncoder.encode(walletAddress), ], }); return [pda, bump]; } /** * Calculate PDA for a message account */ async function findMessagePDA(sender, recipient, messageId, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("message"), addressEncoder.encode(sender), addressEncoder.encode(recipient), new TextEncoder().encode(messageId), ], }); return [pda, bump]; } /** * Calculate PDA for a channel account */ async function findChannelPDA(channelId, creator, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("channel"), new TextEncoder().encode(channelId), addressEncoder.encode(creator), ], }); return [pda, bump]; } /** * Calculate PDA for an escrow account */ async function findEscrowPDA(channelAddress, depositorAddress, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("escrow"), addressEncoder.encode(channelAddress), addressEncoder.encode(depositorAddress), ], }); return [pda, bump]; } /** * Convert MessageType enum to numeric ID */ function getMessageTypeId(messageType, customValue) { switch (messageType) { case MessageType.TEXT: return 0; case MessageType.IMAGE: return 1; case MessageType.CODE: return 2; case MessageType.FILE: return 3; default: return 0; } } /** * Convert numeric ID back to MessageType */ function parseMessageTypeFromNumber(id) { if (id === 0) return { type: MessageType.TEXT }; if (id === 1) return { type: MessageType.IMAGE }; if (id === 2) return { type: MessageType.CODE }; if (id === 3) return { type: MessageType.FILE }; throw new Error(`Unknown message type ID: ${id}`); } /** * Alias for parseMessageTypeFromNumber for backward compatibility */ const getMessageTypeFromId = parseMessageTypeFromNumber; /** * Convert MessageType enum to program format (object with empty record) */ function serializeMessageTypeToProgram(messageType, customValue) { switch (messageType) { case MessageType.TEXT: return { text: {} }; case MessageType.IMAGE: return { image: {} }; case MessageType.CODE: return { code: {} }; case MessageType.FILE: return { file: {} }; default: return { text: {} }; } } /** * Alias for serializeMessageTypeToProgram for backward compatibility */ const convertMessageTypeToProgram = serializeMessageTypeToProgram; /** * Convert program format back to MessageType enum */ function parseMessageTypeFromProgram(programType) { if (programType.text !== undefined) return { type: MessageType.TEXT }; if (programType.image !== undefined) return { type: MessageType.IMAGE }; if (programType.code !== undefined) return { type: MessageType.CODE }; if (programType.file !== undefined) return { type: MessageType.FILE }; return { type: MessageType.TEXT }; } /** * Alias for parseMessageTypeFromProgram for backward compatibility */ const convertMessageTypeFromProgram = parseMessageTypeFromProgram; /** * Handle legacy object-based message type format for backward compatibility */ function getMessageTypeIdFromObject(msg) { if (msg.text !== undefined) return 0; if (msg.data !== undefined) return 1; if (msg.command !== undefined) return 2; if (msg.response !== undefined) return 3; if (typeof msg.custom === "number") return 4 + msg.custom; return 0; } /** * Create a SHA-256 hash of message payload */ async function hashPayload(payload) { const data = typeof payload === "string" ? stringToBytes(payload) : payload; // Use Web Crypto API for hashing if (typeof globalThis !== "undefined" && globalThis.crypto && globalThis.crypto.subtle) { const hashBuffer = await globalThis.crypto.subtle.digest("SHA-256", data); return new Uint8Array(hashBuffer); } // Fallback for Node.js environment if (typeof process !== "undefined" && process.versions?.node) { try { // Dynamic import for Node.js crypto module const { createHash } = await import('crypto'); const hash = createHash("sha256"); hash.update(data); return new Uint8Array(hash.digest()); } catch (e) { // Fall back to a simple hashing algorithm if crypto is not available console.warn("Using fallback hash function. Consider using a proper crypto library."); return simpleHash(data); } } // Simple fallback hash (not cryptographically secure) return simpleHash(data); } /** * Simple hash function fallback (not cryptographically secure) */ function simpleHash(data) { const hash = new Uint8Array(32); let a = 1, b = 0; for (let i = 0; i < data.length; i++) { a = (a + data[i]) % 65521; b = (b + a) % 65521; } // Fill the hash array with computed values for (let i = 0; i < 32; i++) { hash[i] = (a + b + i) % 256; } return hash; } /** * Retry a function with exponential backoff */ async function retry(fn, maxAttempts = 3, delay = 1000) { let lastError; for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { lastError = error; if (attempt === maxAttempts) break; console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`); await sleep(delay); delay *= 2; // Exponential backoff } } throw lastError; } /** * Check if an agent has a specific capability */ function hasCapability(capabilities, capability) { return (capabilities & capability) === capability; } /** * Add a capability to an agent's capabilities bitmask */ function addCapability(capabilities, capability) { return capabilities | capability; } /** * Remove a capability from an agent's capabilities bitmask */ function removeCapability(capabilities, capability) { return capabilities & ~capability; } /** * Get capability names from bitmask */ function getCapabilityNames(capabilities) { const names = []; const capabilityMap = { 1: "TRADING", 2: "ANALYSIS", 4: "DATA_PROCESSING", 8: "CONTENT_GENERATION", 16: "CUSTOM_1", 32: "CUSTOM_2", 64: "CUSTOM_3", 128: "CUSTOM_4", }; for (const [bit, name] of Object.entries(capabilityMap)) { if (hasCapability(capabilities, parseInt(bit))) { names.push(name); } } return names; } /** * Convert lamports to SOL */ function lamportsToSol(lamports, decimals = 9) { return lamports / Math.pow(10, decimals); } /** * Convert SOL to lamports */ function solToLamports(sol) { return Math.floor(sol * Math.pow(10, 9)); } /** * Check if a string is a valid Solana public key */ /** * Convert timestamp to number, handling BN and other formats */ function convertTimestamp(timestamp, fallback) { if (timestamp && typeof timestamp.toNumber === "function") { return timestamp.toNumber(); } if (fallback && typeof fallback.toNumber === "function") { return fallback.toNumber(); } return timestamp || fallback || Date.now(); } /** * Get timestamp from account data */ function getAccountTimestamp(account) { return convertTimestamp(account.timestamp, account.createdAt); } /** * Get creation timestamp from account data */ function getAccountCreatedAt(account) { return convertTimestamp(account.createdAt, account.timestamp); } /** * Get last updated timestamp from account data */ function getAccountLastUpdated(account) { return convertTimestamp(account.lastUpdated, account.updatedAt); } /** * Sleep for a given number of milliseconds */ function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Format a public key for display (show first 4 and last 4 characters) */ function formatAddress(addr, short = true) { const addressStr = typeof addr === 'string' ? addr : String(addr); if (!short) return addressStr; return `${addressStr.slice(0, 4)}...${addressStr.slice(-4)}`; } /** * Validate and parse a message type string */ function parseMessageTypeFromString(typeStr) { const normalized = typeStr.toLowerCase().trim(); switch (normalized) { case "text": return MessageType.TEXT; case "image": return MessageType.IMAGE; case "code": return MessageType.CODE; case "file": return MessageType.FILE; default: throw new Error(`Invalid message type: ${typeStr}`); } } /** * Generate a unique message ID for tracking */ function generateMessageId() { // Use deterministic approach without calling generateId to avoid circular dependency const timestamp = Date.now().toString(36); const counter = (globalThis.__msgIdCounter = (globalThis.__msgIdCounter || 0) + 1); const counterStr = counter.toString(36); // Use crypto.getRandomValues if available, otherwise use deterministic approach if (typeof crypto !== 'undefined' && crypto.getRandomValues) { const array = new Uint8Array(6); crypto.getRandomValues(array); const randomStr = Array.from(array, byte => byte.toString(36)).join('').slice(0, 6); return `msg_${timestamp}_${counterStr}_${randomStr}`; } // Deterministic fallback using hash of timestamp and counter const deterministic = hashStringDeterministic(`${timestamp}_${counter}`); return `msg_${timestamp}_${counterStr}_${deterministic.slice(0, 6)}`; } /** * Calculate the estimated fee for a transaction */ function estimateTransactionFee(messageLength, baseFee = 5000) { // Base fee + per-byte fee const perByteFee = 10; return baseFee + messageLength * perByteFee; } /** * Convert duration to human readable format */ function formatDuration(ms) { const seconds = Math.floor(ms / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (days > 0) return `${days}d ${hours % 24}h`; if (hours > 0) return `${hours}h ${minutes % 60}m`; if (minutes > 0) return `${minutes}m ${seconds % 60}s`; return `${seconds}s`; } /** * Convert bytes to human readable format */ function formatBytes(bytes) { const sizes = ["B", "KB", "MB", "GB"]; if (bytes === 0) return "0 B"; const i = Math.floor(Math.log(bytes) / Math.log(1024)); const size = bytes / Math.pow(1024, i); return `${size.toFixed(1)} ${sizes[i]}`; } /** * Validate capability combination */ function validateCapabilities(capabilities) { // Must be non-negative and within valid range (0-255 for 8 bits) return capabilities >= 0 && capabilities <= 255; } /** * Get channel visibility string */ function getVisibilityString(visibility) { if (typeof visibility === "object") { if (visibility.public !== undefined) return "Public"; if (visibility.private !== undefined) return "Private"; } return typeof visibility === "string" ? visibility : "Public"; } /** * Calculate PDA for a channel participant */ async function findParticipantPDA(channelAddress, agentAddress, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("participant"), addressEncoder.encode(channelAddress), addressEncoder.encode(agentAddress), ], }); return [pda, bump]; } /** * Calculate PDA for a channel invitation */ async function findInvitationPDA(channelAddress, inviteeAddress, programId) { const addressEncoder = getAddressEncoder(); const [pda, bump] = await getProgramDerivedAddress({ programAddress: programId, seeds: [ new TextEncoder().encode("invitation"), addressEncoder.encode(channelAddress), addressEncoder.encode(inviteeAddress), ], }); return [pda, bump]; } /** * Calculate PDA for a channel message account */ async function findChannelMessagePDA(channel, user, nonce, programId = PROGRAM_ID) { const nonceBytes = new Uint8Array(8); const view = new DataView(nonceBytes.buffer); view.setBigUint64(0, BigInt(nonce), true); // little-endian const seeds = [ stringToBytes("channel_message"), addressToBytes(channel), addressToBytes(user), nonceBytes, ]; return derivePDA(seeds, programId); } /** * Create a deterministic seed for account derivation */ function createSeed(input) { // Truncate or pad to 32 bytes for PDA seeds const buffer = Buffer.from(input, "utf8"); if (buffer.length > 32) { return buffer.slice(0, 32); } if (buffer.length < 32) { const padded = Buffer.alloc(32); buffer.copy(padded); return padded; } return buffer; } /** * Wait for transaction confirmation with retry */ async function confirmTransaction(connection, signature, maxRetries = 10, delay = 1000) { for (let i = 0; i < maxRetries; i++) { try { const result = await connection.getTransaction(signature); if (result && result.meta && result.meta.err === null) { return true; } } catch (error) { // Transaction might not be confirmed yet } if (i < maxRetries - 1) { await sleep(delay); } } return false; } /** * Convert various address formats to Address type */ function normalizeAddress(addr) { if (typeof addr === 'string') { return address(addr); } return addr; } /** * Format SOL amounts */ function formatSOL(lamports) { const sol = Number(lamports) / 1000000000; return `${sol.toFixed(4)} SOL`; } /** * Validate public key format (for backward compatibility) */ function isValidAddress(pubkey) { try { normalizeAddress(typeof pubkey === 'string' ? pubkey : String(pubkey)); return true; } catch { return false; } } function getMessageTypeName(type) { switch (type) { case MessageType.TEXT: return "Text"; case MessageType.IMAGE: return "Image"; case MessageType.CODE: return "Code"; case MessageType.FILE: return "File"; default: return "Unknown"; } } function parseMessageTypeFromId(id) { if (id === 0) return { type: MessageType.TEXT }; if (id === 1) return { type: MessageType.IMAGE }; if (id === 2) return { type: MessageType.CODE }; if (id === 3) return { type: MessageType.FILE }; return { type: MessageType.TEXT }; } function serializeMessageType(messageType) { switch (messageType.type) { case MessageType.TEXT: return 0; case MessageType.IMAGE: return 1; case MessageType.CODE: return 2; case MessageType.FILE: return 3; default: return 0; } } function mapMessageTypeToNumber(type) { const lowerType = type.toLowerCase(); switch (lowerType) { case "text": return MessageType.TEXT; case "image": return MessageType.IMAGE; case "code": return MessageType.CODE; case "file": return MessageType.FILE; default: return MessageType.TEXT; } } export { MessageType, addCapability, confirmTransaction, convertMessageTypeFromProgram, convertMessageTypeToProgram, convertTimestamp, createSeed, estimateTransactionFee, findAgentPDA, findChannelMessagePDA, findChannelPDA, findEscrowPDA, findInvitationPDA, findMessagePDA, findParticipantPDA, formatAddress, formatBytes, formatDuration, formatSOL, generateMessageId, getAccountCreatedAt, getAccountLastUpdated, getAccountTimestamp, getCapabilityNames, getMessageTypeFromId, getMessageTypeId, getMessageTypeIdFromObject, getMessageTypeName, getVisibilityString, hasCapability, hashPayload, isValidAddress, lamportsToSol, mapMessageTypeToNumber, normalizeAddress, parseMessageTypeFromId, parseMessageTypeFromNumber, parseMessageTypeFromProgram, parseMessageTypeFromString, removeCapability, retry, serializeMessageType, serializeMessageTypeToProgram, sleep, solToLamports, validateCapabilities }; //# sourceMappingURL=utils.esm.js.map