UNPKG

@ghostspeak/sdk

Version:

TypeScript SDK for GhostSpeak AI Agent Commerce Protocol - Production Ready Beta

1,422 lines (1,419 loc) 129 kB
import { init_reputation_tag_engine, createSolanaClient, createErrorContext, logEnhancedError, IPFSClient, SYSTEM_PROGRAM_ADDRESS, ReputationCalculator, ReputationTagEngine, REPUTATION_CONSTANTS } from './chunk-HIDBANFS.js'; import { getInitializeGovernanceProposalInstructionAsync, getInitializeStakingConfigInstructionAsync, getStakeGhostInstructionAsync, getUnstakeGhostInstructionAsync, getRegisterAgentInstructionAsync, getRegisterAgentCompressedInstructionAsync, getUpdateAgentInstruction, getVerifyAgentInstructionAsync, getDeactivateAgentInstruction, getActivateAgentInstruction, getClaimGhostInstruction } from './chunk-IQM5RASO.js'; import { __export, __esm } from './chunk-UP2VWCW5.js'; import { lamports, pipe, createTransactionMessage, setTransactionMessageFeePayerSigner, setTransactionMessageLifetimeUsingBlockhash, appendTransactionMessageInstructions, signTransactionMessageWithSigners, setTransactionMessageFeePayer, compileTransactionMessage, getBase64EncodedWireTransaction, getProgramDerivedAddress, getUtf8Encoder, getAddressEncoder } from '@solana/kit'; import { LRUCache } from 'lru-cache'; import { address } from '@solana/addresses'; import bs58 from 'bs58'; import { sha256 } from '@noble/hashes/sha256'; // src/modules/reputation/MultiSourceAggregator.ts var MultiSourceAggregator_exports = {}; __export(MultiSourceAggregator_exports, { MultiSourceAggregator: () => MultiSourceAggregator }); var MultiSourceAggregator; var init_MultiSourceAggregator = __esm({ "src/modules/reputation/MultiSourceAggregator.ts"() { MultiSourceAggregator = class { adapters = /* @__PURE__ */ new Map(); configs = /* @__PURE__ */ new Map(); /** Conflict threshold (30% variance) */ CONFLICT_THRESHOLD = 300; // 30% of 1000 scale /** * Create a new multi-source aggregator */ constructor() { } /** * Add a reputation source * * @param adapter - Reputation source adapter * @param config - Source configuration */ addSource(adapter, config) { if (!config.enabled) { return; } this.adapters.set(adapter.source, adapter); this.configs.set(adapter.source, config); } /** * Remove a reputation source * * @param source - Source to remove */ removeSource(source) { this.adapters.delete(source); this.configs.delete(source); } /** * Update source weight * * @param source - Source to update * @param weight - New weight in basis points (0-10000) */ updateSourceWeight(source, weight) { const config = this.configs.get(source); if (config) { config.weight = weight; } } /** * Update source reliability * * @param source - Source to update * @param reliability - New reliability in basis points (0-10000) */ updateSourceReliability(source, reliability) { const config = this.configs.get(source); if (config) { config.reliability = reliability; } } /** * Aggregate reputation from all sources * * @param agentId - Agent identifier * @returns Aggregated reputation data */ async aggregateReputation(agentId) { const sourceDataList = []; for (const [source, adapter] of this.adapters.entries()) { try { const data = await adapter.fetchReputationData(agentId.toString()); if (adapter.validateData(data)) { sourceDataList.push(data); } } catch (error) { console.warn(`Failed to fetch reputation from ${source}:`, error); } } const aggregateScore = this.calculateWeightedScore(sourceDataList); const { hasConflicts, conflicts } = this.detectConflicts(sourceDataList); const sourceScores = this.buildSourceBreakdowns(sourceDataList); const totalDataPoints = sourceDataList.reduce((sum, data) => sum + data.dataPoints, 0); return { agentId, aggregateScore, sourceScores, hasConflicts, conflicts, totalDataPoints, timestamp: /* @__PURE__ */ new Date() }; } /** * Calculate weighted aggregate score * * Formula: Σ(score × weight × reliability) / Σ(weight × reliability) * * @param sourceDataList - Data from all sources * @returns Weighted aggregate score (0-1000) */ calculateWeightedScore(sourceDataList) { if (sourceDataList.length === 0) { return 0; } let totalContribution = 0; let totalNormalization = 0; for (const data of sourceDataList) { const config = this.configs.get(data.source); if (!config) continue; const weight = config.weight / 1e4; const reliability = data.reliability; const contribution = data.score * weight * reliability; const normalization = weight * reliability; totalContribution += contribution; totalNormalization += normalization; } if (totalNormalization === 0) { return 0; } return Math.round(totalContribution / totalNormalization); } /** * Detect conflicts between sources * * Flags conflicts if max_score - min_score > 30% * * @param sourceDataList - Data from all sources * @returns Conflict detection result */ detectConflicts(sourceDataList) { if (sourceDataList.length < 2) { return { hasConflicts: false, conflicts: [] }; } const scores = sourceDataList.map((d) => d.score); const maxScore = Math.max(...scores); const minScore = Math.min(...scores); const variance = maxScore - minScore; const conflicts = []; if (variance > this.CONFLICT_THRESHOLD) { const maxSource = sourceDataList.find((d) => d.score === maxScore)?.source; const minSource = sourceDataList.find((d) => d.score === minScore)?.source; conflicts.push( `High variance detected: ${variance} (${(variance / 1e3 * 100).toFixed(1)}%) between ${maxSource} (${maxScore}) and ${minSource} (${minScore})` ); return { hasConflicts: true, conflicts }; } return { hasConflicts: false, conflicts: [] }; } /** * Build source score breakdowns * * @param sourceDataList - Data from all sources * @returns Source score breakdowns */ buildSourceBreakdowns(sourceDataList) { return sourceDataList.map((data) => { const config = this.configs.get(data.source); if (!config) { throw new Error(`No config found for source: ${data.source}`); } const weight = config.weight; const reliability = data.reliability * 1e4; const contribution = data.score * (weight / 1e4) * (reliability / 1e4) / this.calculateNormalizationFactor(sourceDataList); return { source: data.source, score: data.score, weight, reliability: Math.round(reliability), dataPoints: data.dataPoints, contribution: Math.round(contribution), lastUpdated: data.timestamp }; }); } /** * Calculate normalization factor for weighted scoring */ calculateNormalizationFactor(sourceDataList) { let total = 0; for (const data of sourceDataList) { const config = this.configs.get(data.source); if (!config) continue; const weight = config.weight / 1e4; const reliability = data.reliability; total += weight * reliability; } return total || 1; } /** * Get detailed source breakdown for an agent * * @param agentId - Agent identifier * @returns Source breakdowns with full details */ async getSourceBreakdown(agentId) { const result = await this.aggregateReputation(agentId); return result.sourceScores; } /** * Get list of registered sources */ getRegisteredSources() { return Array.from(this.adapters.keys()); } /** * Get source configuration * * @param source - Source identifier * @returns Source configuration or undefined */ getSourceConfig(source) { return this.configs.get(source); } /** * Enable a source * * @param source - Source to enable */ enableSource(source) { const config = this.configs.get(source); if (config) { config.enabled = true; } } /** * Disable a source * * @param source - Source to disable */ disableSource(source) { const config = this.configs.get(source); if (config) { config.enabled = false; } } /** * Check if source is enabled * * @param source - Source to check * @returns True if enabled */ isSourceEnabled(source) { const config = this.configs.get(source); return config?.enabled ?? false; } }; } }); var RpcClient = class { client; commitment; endpoint; maxRetries; retryDelay; timeout; constructor(config) { this.endpoint = config.endpoint; this.client = createSolanaClient({ urlOrMoniker: config.endpoint }); this.commitment = config.commitment ?? "confirmed"; this.maxRetries = config.maxRetries ?? 3; this.retryDelay = config.retryDelay ?? 1e3; this.timeout = config.timeout ?? 6e4; } /** * Get the underlying RPC client for direct access * This provides access to Gill's rpc object for advanced operations */ get rpc() { return this.client.rpc; } /** * Get account information with automatic retries */ async getAccountInfo(address2, options) { return this.withRetry(async () => { const result = await this.client.rpc.getAccountInfo(address2, { commitment: options?.commitment ?? this.commitment, encoding: "base64" }).send(); if (!result.value) return null; return this.parseAccountInfo(result.value); }); } /** * Get multiple accounts efficiently */ async getMultipleAccounts(addresses, options) { return this.withRetry(async () => { const result = await this.client.rpc.getMultipleAccounts(addresses, { commitment: options?.commitment ?? this.commitment, encoding: "base64" }).send(); return result.value.map( (account) => account ? this.parseAccountInfo(account) : null ); }); } /** * Get program accounts with filters */ async getProgramAccounts(programId, options) { return this.withRetry(async () => { const result = await this.client.rpc.getProgramAccounts(programId, { commitment: options?.commitment ?? this.commitment, encoding: "base64", filters: options?.filters }).send(); return result.map((item) => { const typedItem = item; return { pubkey: typedItem.pubkey, account: this.parseAccountInfo(typedItem.account) }; }); }); } /** * Get latest blockhash with automatic caching */ blockhashCache = null; async getLatestBlockhash() { const now = Date.now(); if (this.blockhashCache && now - this.blockhashCache.timestamp < 1e3) { return this.blockhashCache.value; } const result = await this.withRetry(async () => { const response = await this.client.rpc.getLatestBlockhash({ commitment: this.commitment }).send(); return { blockhash: response.value.blockhash, lastValidBlockHeight: BigInt(response.value.lastValidBlockHeight) }; }); this.blockhashCache = { value: result, timestamp: now }; return result; } /** * Send transaction with enhanced error handling */ async sendTransaction(transaction, options) { return this.withRetry(async () => { const result = await this.client.rpc.sendTransaction(transaction, { encoding: "base64", skipPreflight: options?.skipPreflight ?? false, preflightCommitment: options?.preflightCommitment ?? this.commitment, maxRetries: options?.maxRetries ? BigInt(options.maxRetries) : void 0 }).send(); return result; }); } /** * Get signature statuses with batch support */ async getSignatureStatuses(signatures) { return this.withRetry(async () => { const result = await this.client.rpc.getSignatureStatuses(signatures).send(); return result.value.map((status) => { if (!status) return null; const typedStatus = status; return { slot: typedStatus.slot, confirmations: typedStatus.confirmations, err: typedStatus.err, confirmationStatus: typedStatus.confirmationStatus }; }); }); } /** * Simulate transaction with detailed error info */ async simulateTransaction(transaction, options) { return this.withRetry(async () => { const result = await this.client.rpc.simulateTransaction(transaction, { encoding: "base64", commitment: options?.commitment ?? this.commitment, replaceRecentBlockhash: options?.replaceRecentBlockhash ?? false }).send(); return { err: result.value.err, logs: result.value.logs ?? [], unitsConsumed: result.value.unitsConsumed ? BigInt(result.value.unitsConsumed) : void 0, returnData: result.value.returnData }; }); } /** * Get fee for message */ async getFeeForMessage(encodedMessage) { return this.withRetry(async () => { const result = await this.client.rpc.getFeeForMessage(encodedMessage, { commitment: this.commitment }).send(); return result.value ? BigInt(result.value) : null; }); } /** * Health check */ async isHealthy() { try { await this.client.rpc.getHealth().send(); return true; } catch { return false; } } /** * Get RPC node version */ async getVersion() { const result = await this.client.rpc.getVersion().send(); return result; } /** * Subscribe to account changes (WebSocket) * * Note: This is a polling-based implementation for backward compatibility. * For production use with real-time subscriptions, access the rpcSubscriptions * directly via client.rpcSubscriptions from Gill. */ async subscribeToAccount(address2, callback) { console.warn("Account subscription using polling fallback. For real-time subscriptions, use client.rpcSubscriptions directly."); const intervalId = setInterval(async () => { try { const accountInfo = await this.getAccountInfo(address2); callback(accountInfo); } catch (error) { console.error("Subscription polling error:", error); } }, 5e3); return () => { clearInterval(intervalId); }; } /** * Get the underlying Gill client for advanced operations * This provides direct access to Gill's SolanaClient for features like: * - rpcSubscriptions for WebSocket subscriptions * - sendAndConfirmTransaction for transaction handling */ getGillClient() { return this.client; } // Private helper methods async withRetry(operation, retries = this.maxRetries) { let lastError; for (let i = 0; i <= retries; i++) { try { return await operation(); } catch (error) { lastError = error; if (i < retries) { await new Promise((resolve) => setTimeout(resolve, this.retryDelay * Math.pow(2, i))); } } } throw lastError; } parseAccountInfo(rawAccount) { const account = rawAccount; const dataArray = account.data; const base64Data = Array.isArray(dataArray) ? dataArray[0] : dataArray; return { executable: account.executable, lamports: typeof account.lamports === "number" ? lamports(BigInt(account.lamports)) : account.lamports, owner: account.owner, rentEpoch: account.rentEpoch !== void 0 ? typeof account.rentEpoch === "number" ? BigInt(account.rentEpoch) : account.rentEpoch : BigInt(0), data: Buffer.from(base64Data, "base64"), space: account.space ? typeof account.space === "number" ? BigInt(account.space) : account.space : void 0 }; } }; // src/utils/transaction-urls.ts function getSolanaExplorerUrl(signature, cluster = "mainnet-beta") { const baseUrl = "https://explorer.solana.com/tx"; switch (cluster) { case "devnet": return `${baseUrl}/${signature}?cluster=devnet`; case "testnet": return `${baseUrl}/${signature}?cluster=testnet`; case "localnet": return `${baseUrl}/${signature}?cluster=custom&customUrl=http://localhost:8899`; default: return `${baseUrl}/${signature}`; } } function getSolscanUrl(signature, cluster = "mainnet-beta") { const baseUrl = "https://solscan.io/tx"; switch (cluster) { case "devnet": return `${baseUrl}/${signature}?cluster=devnet`; case "testnet": return `${baseUrl}/${signature}?cluster=testnet`; case "localnet": return `Local transaction: ${signature} (not viewable on Solscan)`; default: return `${baseUrl}/${signature}`; } } function getSolanaFMUrl(signature, cluster = "mainnet-beta") { const baseUrl = "https://solana.fm/tx"; switch (cluster) { case "devnet": return `${baseUrl}/${signature}?cluster=devnet-solana`; case "testnet": return `${baseUrl}/${signature}?cluster=testnet-solana`; case "localnet": return `Local transaction: ${signature} (not viewable on SolanaFM)`; default: return `${baseUrl}/${signature}`; } } function getXrayUrl(signature, cluster = "mainnet-beta") { const baseUrl = "https://xray.helius.xyz/tx"; switch (cluster) { case "devnet": return `${baseUrl}/${signature}?network=devnet`; case "testnet": return `${baseUrl}/${signature}?network=testnet`; case "localnet": return `Local transaction: ${signature} (not viewable on XRAY)`; default: return `${baseUrl}/${signature}`; } } function generateExplorerUrls(signature, cluster = "mainnet-beta") { return { solanaExplorer: getSolanaExplorerUrl(signature, cluster), solscan: getSolscanUrl(signature, cluster), solanaFM: getSolanaFMUrl(signature, cluster), xray: getXrayUrl(signature, cluster) }; } function createTransactionResult(signature, cluster, commitment = "confirmed") { return { signature, cluster, urls: generateExplorerUrls(signature, cluster), commitment, timestamp: Date.now() }; } // src/core/DevTools.ts var DevTools = class _DevTools { static instance = null; rpcClient; config; isDevelopment; logs = []; timings = /* @__PURE__ */ new Map(); constructor(config) { this.config = config; this.rpcClient = new RpcClient({ endpoint: config.rpcEndpoint ?? "https://api.devnet.solana.com", commitment: config.commitment }); this.isDevelopment = process.env.NODE_ENV === "development" || config.cluster === "localnet" || false; } /** * Get singleton instance */ static getInstance(config) { if (!_DevTools.instance && config) { _DevTools.instance = new _DevTools(config); } if (!_DevTools.instance) { throw new Error("DevTools not initialized. Call with config first."); } return _DevTools.instance; } /** * Enable development mode */ enableDevMode() { this.isDevelopment = true; console.log("\u{1F6E0}\uFE0F GhostSpeak Development Mode Enabled"); console.log(" - Transaction simulation before sending"); console.log(" - Cost estimates for all operations"); console.log(" - Enhanced error messages"); console.log(" - Performance timing"); } /** * Check if in development mode */ isDevMode() { return this.isDevelopment; } /** * Analyze transaction instructions */ analyzeTransaction(instructions) { const writableAccounts = /* @__PURE__ */ new Set(); const readonlyAccounts = /* @__PURE__ */ new Set(); const signers = /* @__PURE__ */ new Set(); const warnings = []; let totalSize = 64; const instructionAnalyses = instructions.map((instr, index) => { totalSize += 32; totalSize += (instr.accounts?.length ?? 0) * 32; totalSize += instr.data?.length ?? 0; const accounts = (instr.accounts ?? []).map((acc) => { const isWritable = acc.role.toString().includes("writable") || acc.role.toString().includes("WRITABLE") || acc.role === 1 || // AccountRole.WRITABLE = 1 acc.role === 3; const isSigner = acc.role.toString().includes("signer") || acc.role.toString().includes("SIGNER") || typeof acc === "object" && "signer" in acc || acc.role === 3; if (isWritable) { writableAccounts.add(acc.address); } else { readonlyAccounts.add(acc.address); } if (isSigner) { signers.add(acc.address); } return { address: acc.address, isWritable, isSigner, role: acc.role }; }); const humanReadable = this.getInstructionDescription(instr, index); return { index, programId: instr.programAddress, accountCount: accounts.length, dataSize: instr.data?.length ?? 0, humanReadable, accounts }; }); const estimatedComputeUnits = this.estimateComputeUnits(instructions); const estimatedFee = this.estimateFee(estimatedComputeUnits, instructions.length); if (totalSize > 1232) { warnings.push(`Transaction size (${totalSize} bytes) exceeds limit (1232 bytes)`); } if (signers.size === 0) { warnings.push("No signers found in transaction"); } if (estimatedComputeUnits > BigInt(14e5)) { warnings.push(`High compute usage: ${estimatedComputeUnits} units`); } return { instructions: instructionAnalyses, totalAccounts: writableAccounts.size + readonlyAccounts.size, signerCount: signers.size, writableAccounts: Array.from(writableAccounts), readonlyAccounts: Array.from(readonlyAccounts), estimatedSize: totalSize, estimatedComputeUnits, estimatedFee, warnings }; } /** * Get human-readable instruction description */ getInstructionDescription(instruction, _index) { const programId = instruction.programAddress; if (programId === this.config.programId) { return this.decodeGhostSpeakInstruction(instruction); } if (programId === "11111111111111111111111111111111") { return "System Program: Transfer or Create Account"; } if (programId === "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA") { return "Token Program: Token Operation"; } if (programId === "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb") { return "Token-2022 Program: Advanced Token Operation"; } return `Unknown Instruction`; } /** * Decode GhostSpeak instruction */ decodeGhostSpeakInstruction(instruction) { if (!instruction.data || instruction.data.length < 8) { return "GhostSpeak: Unknown Instruction"; } const discriminator = Array.from(instruction.data.slice(0, 8)).map((b) => b.toString(16).padStart(2, "0")).join(""); const instructionMap = { "a8c5e109d3d1b8d5": "Register Agent", "b7f3c8e0a2d4e9f1": "Create Escrow", "c4d2f7a9b8e1c3f5": "Send Message", "d9e8f2b5c1a7e4f8": "Create Channel" // Add more mappings as needed }; return `GhostSpeak: ${instructionMap[discriminator] ?? "Custom Instruction"}`; } /** * Estimate compute units for instructions */ estimateComputeUnits(instructions) { let totalUnits = BigInt(0); for (const instr of instructions) { totalUnits += BigInt(200); totalUnits += BigInt((instr.accounts?.length ?? 0) * 150); totalUnits += BigInt((instr.data?.length ?? 0) * 10); if (instr.programAddress === this.config.programId) { totalUnits += BigInt(5e3); } } return totalUnits; } /** * Estimate transaction fee */ estimateFee(computeUnits, instructionCount) { const baseFee = BigInt(5e3); const computeFee = computeUnits * BigInt(5) / BigInt(1e6); const priorityFee = BigInt(instructionCount * 1e3); return baseFee + computeFee + priorityFee; } /** * Log debug message */ log(message, data) { if (!this.isDevelopment) return; const timestamp = (/* @__PURE__ */ new Date()).toISOString(); const logEntry = `[${timestamp}] ${message}`; this.logs.push(logEntry); console.log(`\u{1F50D} ${logEntry}`); if (data) { console.log(" Data:", data); } } /** * Start timing an operation */ startTiming(label) { if (!this.isDevelopment) return; const perfNow = typeof performance !== "undefined" ? performance.now() : Date.now(); this.timings.set(label, perfNow); } /** * End timing and log result */ endTiming(label) { if (!this.isDevelopment) return; const start = this.timings.get(label); if (!start) return; const perfNow = typeof performance !== "undefined" ? performance.now() : Date.now(); const duration = perfNow - start; this.log(`${label} took ${duration.toFixed(2)}ms`); this.timings.delete(label); } /** * Format transaction for display */ formatTransaction(analysis) { const lines = [ "\u{1F4CB} Transaction Analysis", "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550", `Instructions: ${analysis.instructions.length}`, `Total Accounts: ${analysis.totalAccounts}`, `Signers: ${analysis.signerCount}`, `Estimated Size: ${analysis.estimatedSize} bytes`, `Estimated Compute Units: ${analysis.estimatedComputeUnits.toLocaleString()}`, `Estimated Fee: ${(Number(analysis.estimatedFee) / 1e9).toFixed(6)} SOL`, "" ]; lines.push("Instructions:"); for (const instr of analysis.instructions) { lines.push(` ${instr.index + 1}. ${instr.humanReadable}`); lines.push(` Program: ${instr.programId.slice(0, 8)}...`); lines.push(` Accounts: ${instr.accountCount}, Data: ${instr.dataSize} bytes`); } if (analysis.warnings.length > 0) { lines.push(""); lines.push("\u26A0\uFE0F Warnings:"); for (const warning of analysis.warnings) { lines.push(` - ${warning}`); } } lines.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"); return lines.join("\n"); } /** * Export debug logs */ exportLogs() { return [...this.logs]; } /** * Clear debug logs */ clearLogs() { this.logs = []; this.timings.clear(); } }; function validateInstruction(instruction) { const inst = instruction; if (!inst.programAddress) { throw new Error("Invalid instruction format"); } } var InstructionBuilder = class { rpcClient; config; devTools; debugMode = false; constructor(config) { this.config = config; this.rpcClient = new RpcClient({ endpoint: config.rpcEndpoint ?? "https://api.devnet.solana.com", wsEndpoint: config.wsEndpoint, commitment: config.commitment ?? "confirmed" }); this.devTools = DevTools.getInstance(config); } /** * Execute a single instruction with unified error handling and transaction patterns */ async execute(instructionName, instructionGetter, signers, options) { const context = createErrorContext( "execute", instructionName, signers.map((s) => ({ address: s.address, name: "signer" })), { programId: this.config.programId } ); try { if (this.devTools.isDevMode()) { this.devTools.startTiming(instructionName); } const instruction = await Promise.resolve(instructionGetter()); validateInstruction(instruction); if (this.debugMode || this.devTools.isDevMode()) { const analysis = this.devTools.analyzeTransaction([instruction]); console.log(this.devTools.formatTransaction(analysis)); this.debugMode = false; } if (options?.simulate) { return await this.simulateInstruction(instruction, signers); } const latestBlockhashResult = await this.rpcClient.getLatestBlockhash(); const latestBlockhash = { blockhash: latestBlockhashResult.blockhash, lastValidBlockHeight: latestBlockhashResult.lastValidBlockHeight }; const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signers[0], tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions([instruction], tx) ); const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); const signatureResult = await this.sendAndConfirm( signedTransaction, options?.skipPreflight ?? false, options?.maxRetries ?? 30 ); if (typeof signatureResult !== "string") { throw new Error("Transaction signature is not a string"); } const signature = signatureResult; if (options?.returnDetails) { const cluster = this.config.cluster ?? "devnet"; const result = createTransactionResult(signature, cluster, this.config.commitment ?? "confirmed"); if (this.devTools.isDevMode()) { this.devTools.endTiming(instructionName); } return result; } if (this.devTools.isDevMode()) { this.devTools.endTiming(instructionName); } return signature; } catch (error) { logEnhancedError(error, context); throw error; } } /** * Execute multiple instructions in a single transaction */ async executeBatch(batchName, instructionGetters, signers, options) { const context = createErrorContext( "executeBatch", batchName, signers.map((s) => ({ address: s.address, name: "signer" })), { programId: this.config.programId, instructionCount: instructionGetters.length } ); try { const instructions = await Promise.all( instructionGetters.map(async (getter, i) => { const instruction = await Promise.resolve(getter()); try { validateInstruction(instruction); return instruction; } catch (error) { throw new Error(`Instruction ${i} in ${batchName}: ${error.message}`); } }) ); const estimatedSize = this.estimateTransactionSize(instructions); if (estimatedSize > 1232) { throw new Error(`Transaction too large: ${estimatedSize} bytes (max: 1232)`); } if (options?.simulate) { return await this.simulateBatch(instructions, signers); } const latestBlockhashResult = await this.rpcClient.getLatestBlockhash(); const latestBlockhash = { blockhash: latestBlockhashResult.blockhash, lastValidBlockHeight: latestBlockhashResult.lastValidBlockHeight }; const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signers[0], tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); const signatureResult = await this.sendAndConfirm( signedTransaction, options?.skipPreflight ?? false ); if (typeof signatureResult !== "string") { throw new Error("Transaction signature is not a string"); } const signature = signatureResult; if (options?.returnDetails) { const cluster = this.config.cluster ?? "devnet"; const result = createTransactionResult(signature, cluster, this.config.commitment ?? "confirmed"); if (this.devTools.isDevMode()) { this.devTools.endTiming(batchName); } return result; } if (this.devTools.isDevMode()) { this.devTools.endTiming(batchName); } return signature; } catch (error) { logEnhancedError(error, context); throw error; } } /** * Get and decode account data with unified error handling */ async getAccount(address2, decoderImportName) { try { const accountInfo = await this.rpcClient.getAccountInfo(address2, { commitment: this.config.commitment }); if (!accountInfo) return null; const generated = await import('./generated-QJREJQ2C.js'); const decoderGetter = generated[decoderImportName]; const decoder = decoderGetter(); const rawData = this.extractRawData(accountInfo.data); return decoder.decode(rawData); } catch (error) { console.warn(`Failed to fetch account ${address2}:`, error); return null; } } /** * Get multiple accounts with unified pattern */ async getAccounts(addresses, decoderImportName) { try { const accounts = await this.rpcClient.getMultipleAccounts(addresses, { commitment: this.config.commitment }); const generated = await import('./generated-QJREJQ2C.js'); const decoderGetter = generated[decoderImportName]; const decoder = decoderGetter(); return accounts.map((accountInfo) => { if (!accountInfo) return null; try { const rawData = this.extractRawData(accountInfo.data); return decoder.decode(rawData); } catch { return null; } }); } catch (error) { console.warn("Failed to fetch multiple accounts:", error); return addresses.map(() => null); } } /** * Get program accounts with filters */ async getProgramAccounts(decoderImportName, filters) { try { const convertedFilters = filters?.map((filter) => { if ("dataSize" in filter) { return { dataSize: filter.dataSize }; } else { const encoding = filter.memcmp.encoding ?? "base58"; if (encoding === "base64") { return { memcmp: { offset: filter.memcmp.offset, bytes: filter.memcmp.bytes, encoding: "base64" } }; } else { return { memcmp: { offset: filter.memcmp.offset, bytes: filter.memcmp.bytes, encoding: "base58" } }; } } }); const accounts = await this.rpcClient.getProgramAccounts(this.config.programId, { commitment: this.config.commitment, filters: convertedFilters ?? [] }); const generated = await import('./generated-QJREJQ2C.js'); const decoderGetter = generated[decoderImportName]; const decoder = decoderGetter(); const decodedAccounts = []; for (const { pubkey, account } of accounts) { try { const rawData = this.extractRawData(account.data); const decodedData = decoder.decode(rawData); decodedAccounts.push({ address: pubkey, data: decodedData }); } catch { } } return decodedAccounts; } catch (error) { console.error("Failed to get program accounts:", error); return []; } } /** * Enable debug mode for next transaction */ enableDebug() { this.debugMode = true; return this; } /** * Debug transaction - analyze without executing */ async debug(instructionName, instructionGetters) { this.devTools.log(`Debugging ${instructionName}`); const instructions = await Promise.all( instructionGetters.map(async (getter) => { const instruction = await Promise.resolve(getter()); validateInstruction(instruction); return instruction; }) ); const analysis = this.devTools.analyzeTransaction(instructions); console.log(this.devTools.formatTransaction(analysis)); return analysis; } /** * Get human-readable explanation of transaction */ async explain(instructionName, instructionGetters) { const analysis = await this.debug(instructionName, instructionGetters); const lines = [ `\u{1F50D} Transaction: ${instructionName}`, "", "This transaction will:", ...analysis.instructions.map((instr, i) => ` ${i + 1}. ${instr.humanReadable}`), "", `Cost: ~${(Number(analysis.estimatedFee) / 1e9).toFixed(6)} SOL`, `Size: ${analysis.estimatedSize} bytes`, `Compute: ${analysis.estimatedComputeUnits.toLocaleString()} units` ]; if (analysis.warnings.length > 0) { lines.push("", "\u26A0\uFE0F Warnings:"); lines.push(...analysis.warnings.map((w) => ` - ${w}`)); } return lines.join("\n"); } /** * Estimate transaction cost */ async estimateCost(instructionGetters) { try { const instructions = await Promise.all( instructionGetters.map(async (getter) => { const instruction = await Promise.resolve(getter()); validateInstruction(instruction); return instruction; }) ); const latestBlockhashResult = await this.rpcClient.getLatestBlockhash(); const latestBlockhash = { blockhash: latestBlockhashResult.blockhash, lastValidBlockHeight: latestBlockhashResult.lastValidBlockHeight }; const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayer(this.config.defaultFeePayer, tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); const compiledMessage = compileTransactionMessage(transactionMessage); const encodedMessage = Buffer.from(compiledMessage).toString("base64"); const fee = await this.rpcClient.getFeeForMessage(encodedMessage); return BigInt(fee ?? 0); } catch { const baseFee = 5000n; const perInstructionFee = 1000n; return baseFee + BigInt(instructionGetters.length) * perInstructionFee; } } // Private helper methods async sendAndConfirm(signedTransaction, skipPreflight, maxRetries = 30) { const wireTransaction = getBase64EncodedWireTransaction(signedTransaction); const signature = await this.rpcClient.sendTransaction(wireTransaction, { skipPreflight, preflightCommitment: this.config.commitment }); console.log("\u{1F50D} Transaction sent, signature:", signature); console.log("\u{1F50D} Signature length:", signature.length); console.log("\u{1F50D} Signature type:", typeof signature); let confirmed = false; let attempts = 0; let currentDelay = 1e3; const maxConfirmationTime = 3e4; const startTime = Date.now(); while (!confirmed && attempts < maxRetries && Date.now() - startTime < maxConfirmationTime) { try { console.log(`\u{1F50D} Confirmation attempt ${attempts + 1}/${maxRetries}`); const statuses = await this.rpcClient.getSignatureStatuses([signature]); if (statuses[0]) { console.log("\u{1F50D} Status found:", statuses[0]); if (statuses[0].err) { throw new Error(`Transaction failed: ${JSON.stringify(statuses[0].err, (_, v) => typeof v === "bigint" ? v.toString() : v)}`); } const confirmationStatus = statuses[0].confirmationStatus; if (confirmationStatus === this.config.commitment || this.config.commitment === "confirmed" && confirmationStatus === "finalized") { confirmed = true; console.log("\u2705 Transaction confirmed via status check"); break; } } else { console.log("\u{1F50D} No status found, trying transaction details..."); try { const directClient = createSolanaClient({ urlOrMoniker: this.config.rpcEndpoint ?? "https://api.devnet.solana.com" }); const transaction = await directClient.rpc.getTransaction(signature, { commitment: this.config.commitment ?? "confirmed", encoding: "json", maxSupportedTransactionVersion: 0 }).send(); if (transaction && transaction.meta) { if (transaction.meta.err) { throw new Error(`Transaction failed: ${JSON.stringify(transaction.meta.err, (_, v) => typeof v === "bigint" ? v.toString() : v)}`); } confirmed = true; console.log("\u2705 Transaction confirmed via direct lookup"); break; } } catch { console.log("\u{1F50D} Transaction details not yet available"); } } attempts++; await new Promise((resolve) => setTimeout(resolve, currentDelay)); currentDelay = Math.min(currentDelay * 1.5, 5e3); } catch (error) { if (error instanceof Error && error.message.includes("Transaction failed")) { throw error; } console.log(`\u{1F50D} Confirmation attempt failed:`, error.message); attempts++; await new Promise((resolve) => setTimeout(resolve, currentDelay * 2)); } } if (!confirmed) { console.log("\u{1F50D} Final confirmation attempt via transaction lookup..."); try { const directClient = createSolanaClient({ urlOrMoniker: this.config.rpcEndpoint ?? "https://api.devnet.solana.com" }); const transaction = await directClient.rpc.getTransaction(signature, { commitment: this.config.commitment ?? "confirmed", encoding: "json", maxSupportedTransactionVersion: 0 }).send(); if (transaction && transaction.meta) { if (transaction.meta.err) { throw new Error(`Transaction failed: ${JSON.stringify(transaction.meta.err, (_, v) => typeof v === "bigint" ? v.toString() : v)}`); } console.log("\u2705 Transaction confirmed on final check - returning success"); return signature; } } catch (finalError) { console.log("\u{1F50D} Final check failed:", finalError.message); } console.log("\u26A0\uFE0F Transaction confirmation timed out, but transaction was sent"); console.log(` Check status at: https://explorer.solana.com/tx/${signature}?cluster=${this.config.cluster || "devnet"}`); return signature; } return signature; } async simulateInstruction(instruction, signers) { const latestBlockhashResult = await this.rpcClient.getLatestBlockhash(); const latestBlockhash = { blockhash: latestBlockhashResult.blockhash, lastValidBlockHeight: latestBlockhashResult.lastValidBlockHeight }; const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signers[0], tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions([instruction], tx) ); const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); const wireTransaction = getBase64EncodedWireTransaction(signedTransaction); return this.rpcClient.simulateTransaction(wireTransaction, { commitment: this.config.commitment, replaceRecentBlockhash: true }); } async simulateBatch(instructions, signers) { const latestBlockhashResult = await this.rpcClient.getLatestBlockhash(); const latestBlockhash = { blockhash: latestBlockhashResult.blockhash, lastValidBlockHeight: latestBlockhashResult.lastValidBlockHeight }; const transactionMessage = pipe( createTransactionMessage({ version: 0 }), (tx) => setTransactionMessageFeePayerSigner(signers[0], tx), (tx) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, tx), (tx) => appendTransactionMessageInstructions(instructions, tx) ); const signedTransaction = await signTransactionMessageWithSigners(transactionMessage); const wireTransaction = getBase64EncodedWireTransaction(signedTransaction); return this.rpcClient.simulateTransaction(wireTransaction, { commitment: this.config.commitment, replaceRecentBlockhash: true }); } estimateTransactionSize(instructions) { let totalSize = 64; for (const instruction of instructions) { totalSize += 32; totalSize += (instruction.accounts?.length ?? 0) * 32; totalSize += instruction.data.length; } return totalSize; } extractRawData(data) { if (Buffer.isBuffer(data) || data instanceof Uint8Array) { return new Uint8Array(data); } if (typeof data === "object" && data !== null && "data" in data) { return Buffer.from(data.data, "base64"); } if (typeof data === "string") { return Buffer.from(data, "base64"); } throw new Error("Invalid account data format"); } // ===================================================== // H2A PROTOCOL INSTRUCTION METHODS // ===================================================== /** * Create a communication session instruction */ async createCommunicationSession(_params) { return { programAddress: this.config.programId, accounts: [], data: new Uint8Array(0) // Placeholder - would contain serialized instruction data }; } /** * Send a communication message instruction */ async sendCommunicationMessage(_sessionAddress, _params) { return { programAddress: this.config.programId, accounts: [], data: new Uint8Array(0) // Placeholder - would contain serialized instruction data }; } /** * Update participant status instruction */ async updateParticipantStatus(_params) { return { programAddress: this.config.programId, accounts: [], data: new Uint8Array(0) // Placeholder - would contain serialized instruction data }; } }; var DEFAULT_TTLS = { processed: 500, // 500ms - very volatile confirmed: 2e3, // 2s - less volatile finalized: 3e4 // 30s - stable }; var CacheManager = class { accountCache; pdaCache; config; ttls; constructor(config = {}) { this.config = { enabled: config.enabled ?? false, maxSize: config.maxSize ?? 1e3, ttlOverrides: config.ttlOverrides ?? {} }; this.ttls = { processed: config.ttlOverrides?.processed ?? DEFAULT_TTLS.processed, confirmed: config.ttlOverrides?.confirmed ?? DEFAULT_TTLS.confirmed, finalized: config.ttlOverrides?.finalized ?? DEFAULT_TTLS.finalized }; this.accountCache = new LRUCache({ max: this.config.maxSize, ttl: DEFAULT_TTLS.finalized // Default TTL (overridden per entry) }); this.pdaCache = new LRUCache({ max: this.config.maxSize }); } /** * Check if caching is enabled */ isEnabled() { return this.config.enabled; } /** * Get cached account data * * @param address - Account address * @param commitment - Commitment level * @param currentSlot - Current blockchain slot (for staleness check) * @returns Cached data or undefined */ getAccount(address2, commitment, currentSlot) { if (!this.config.enabled) return void 0; const key = `${address2}:${commitment}`; const entry = this.accountCache.get(key); if (!entry) return void 0; if (currentSlot !== void 0 && entry.slot < currentSlot) { this.accountCache.delete(key); return void 0; } return entry.data; } /** * Cache account data * * @param address - Account address * @param data - Account data to cache * @param commitment - Commitment level * @param slot - Blockchain slot when data was fetched */ setAccount(address2, data, commitment, slot) { if (!this.config.enabled) return; const key = `${address2}:${commitment}`; const entry = { data, slot, commitment, timestamp: Date.now() }; this.accountCache.set(key, entry, { ttl: this.ttls[commitment] }); } /** * Get cached PDA * * PDAs are cached indefinitely as they're deterministic. * * @param seeds - Serialized seed components * @returns Cached PDA or undefined */ getPDA(seeds) { if (!this.config.enabled) return void 0; return this.pdaCache.get(seeds); } /** * Cache PDA derivation * * @param seeds - Serialized seed components (use JSON.stringify for consistency) * @param pda - Derived PDA address */ setPDA(seeds, pda) { if (!this.config.enabled) return; this.pdaCache.set(seeds, pda); } /** * Invalidate account cache entry * * @param address - Account address to invalidate * @param commitment - Optional commitment level (invalidates all if not specified) */ invalidateAccount(address2, commitment) { if (!this.config.enabled