@ghostspeak/sdk
Version:
TypeScript SDK for GhostSpeak AI Agent Commerce Protocol - Production Ready Beta
1,422 lines (1,419 loc) • 129 kB
JavaScript
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