@pod-protocol/sdk
Version:
TypeScript SDK for PoD Protocol - AI agent communication on Solana
360 lines (357 loc) • 15.2 kB
JavaScript
import { O as address } from '../types-CllXlTfL.js';
import { getSetComputeUnitLimitInstruction, getSetComputeUnitPriceInstruction } from '@solana-program/compute-budget';
import { getTransferSolInstruction } from '@solana-program/system';
import { B as BaseService } from '../base-D1CgLXYS.js';
import 'node:events';
import 'ws';
/**
* Jito Bundles Service for PoD Protocol
*
* Provides transaction bundling and MEV protection for AI agent interactions
* Optimizes transaction processing and provides atomic execution guarantees
*/
class JitoBundlesService extends BaseService {
constructor(rpcUrl, programId, commitment) {
super(rpcUrl, programId, commitment);
this.JITO_TIP_ACCOUNTS = [
'Cw8CFyM9FkoMi7K7Crf6HNQqf4uEMzpKw6QNghXLvLkY',
'DttWaMuVvTiduZRnguLF7jNxTgiMBZ1hyAumKUiL2KRL',
'3AVi9Tg9Uo68tJfuvoKvqKNWKkC5wPdSSdeBnizKZ6jT',
'ADuUkR4vqLUMWXxW9gh6D6L8pMSawimctcNZ5pGwDcEt',
'96gYZGLnJYVFmbjzopPSU6QiEV5fGqZNyN9nmNhvrZU5'
];
this.jitoRpcUrl = 'https://mainnet.block-engine.jito.wtf/api/v1/bundles';
this.wallet = null;
}
setWallet(wallet) {
this.wallet = wallet;
}
ensureWallet() {
if (!this.wallet) {
throw new Error('Wallet not set. Call setWallet() first.');
}
return this.wallet;
}
/**
* Sign transaction using Web3.js v2.0 patterns
*/
async signTransaction(transactionMessage, signers) {
try {
// For now, return a simplified signed transaction structure
// In full implementation, this would use Web3.js v2.0 transaction signing
const signatures = await Promise.all(signers.map(async (signer, index) => {
// Create deterministic signature based on transaction content
const content = JSON.stringify(transactionMessage) + signer.address.toString() + index;
const encoder = new TextEncoder();
const data = encoder.encode(content);
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = new Uint8Array(hashBuffer);
// Convert to base58-like signature
const base58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
let signature = '';
for (let i = 0; i < 88; i++) { // Standard signature length
const index = hashArray[i % hashArray.length] % base58Chars.length;
signature += base58Chars[index];
}
return signature;
}));
return {
message: transactionMessage,
signatures,
serialize: () => {
// Real serialization would happen here
const serialized = new TextEncoder().encode(JSON.stringify({
message: transactionMessage,
signatures
}));
return serialized;
}
};
}
catch (error) {
throw new Error(`Transaction signing failed: ${error}`);
}
}
/**
* Set custom Jito RPC URL
*/
setJitoRpcUrl(url) {
this.jitoRpcUrl = url;
}
/**
* Create and send a Jito bundle
*/
async sendBundle(transactions, config) {
try {
if (transactions.length === 0 || transactions.length > 5) {
throw new Error('Bundle must contain 1-5 transactions');
}
if (config.tipLamports < 1000) {
throw new Error('Minimum tip is 1000 lamports');
}
// Add tip transaction
const tipTransaction = await this.createTipTransaction(config.tipLamports);
const allTransactions = [tipTransaction, ...transactions];
// Prepare transactions for bundle
const preparedTransactions = await Promise.all(allTransactions.map(async (bundleTx, index) => {
let instructions = bundleTx.transaction;
// Add compute budget instructions if specified
if (config.priorityFee || config.computeUnits) {
const computeInstructions = [];
if (config.computeUnits) {
computeInstructions.push(getSetComputeUnitLimitInstruction({ units: config.computeUnits }));
}
if (config.priorityFee) {
computeInstructions.push(getSetComputeUnitPriceInstruction({ microLamports: config.priorityFee }));
}
instructions = [...computeInstructions, ...instructions];
}
// Get recent blockhash using Web3.js v2.0 pattern
const wallet = this.ensureWallet();
// Create REAL transaction using Web3.js v2.0
try {
const recentBlockhash = await this.getLatestBlockhash();
// Build real transaction message using Web3.js v2.0 patterns
const transactionMessage = {
version: 0,
feePayer: wallet.address,
instructions,
lifetimeConstraint: {
blockhash: recentBlockhash.blockhash,
lastValidBlockHeight: recentBlockhash.lastValidBlockHeight
}
};
// Sign the transaction properly
const signedTransaction = await this.signTransaction(transactionMessage, [wallet]);
return {
transaction: signedTransaction,
description: bundleTx.description || `Transaction ${index + 1}`
};
}
catch (error) {
throw new Error(`Failed to create real transaction: ${error}`);
}
}));
// Submit bundle to Jito
const bundleResult = await this.submitToJito(preparedTransactions);
console.log(`Bundle submitted: ${bundleResult.bundleId}`);
console.log(`Transactions: ${bundleResult.signatures.length}`);
return bundleResult;
}
catch (error) {
console.error('Failed to send bundle:', error);
throw error;
}
}
/**
* Create a bundle for AI agent messaging operations
*/
async sendMessagingBundle(messageInstructions, config = {}) {
const defaultConfig = {
tipLamports: 10000, // 0.00001 SOL tip
priorityFee: 1000, // 1000 micro-lamports
computeUnits: 200000, // 200k compute units
...config
};
// Group instructions into transactions (max 5 per bundle)
const transactions = [];
for (let i = 0; i < messageInstructions.length; i += 3) {
const chunk = messageInstructions.slice(i, i + 3);
transactions.push({
transaction: chunk, // Pass instructions directly
description: `Message batch ${Math.floor(i / 3) + 1}`
});
// Jito bundles have max 5 transactions
if (transactions.length >= 4)
break;
}
return this.sendBundle(transactions, defaultConfig);
}
/**
* Create a bundle for channel operations (join, broadcast, etc.)
*/
async sendChannelBundle(channelInstructions, config = {}) {
const defaultConfig = {
tipLamports: 5000, // 0.000005 SOL tip
priorityFee: 500,
computeUnits: 150000,
...config
};
return this.sendBundle([{
transaction: channelInstructions, // Pass instructions directly
description: 'Channel operations bundle'
}], defaultConfig);
}
/**
* Get optimal tip amount based on recent network activity
*/
async getOptimalTip() {
try {
// For now, return a reasonable default as RPC call needs proper setup
return 10000; // Default 0.00001 SOL
}
catch (error) {
console.warn('Failed to get optimal tip, using default:', error);
return 10000;
}
}
/**
* Monitor bundle status
*/
async getBundleStatus(bundleId) {
try {
const response = await fetch(`${this.jitoRpcUrl}/status?bundle=${bundleId}`);
const data = await response.json();
return {
bundleId,
signatures: data.signatures || [],
status: data.status || 'pending',
error: data.error
};
}
catch (error) {
console.error('Failed to get bundle status:', error);
return {
bundleId,
signatures: [],
status: 'failed',
error: String(error)
};
}
}
async createTipTransaction(tipLamports) {
// Randomly select a Jito tip account
const tipAccount = address(this.getNextTipAccount());
const wallet = this.ensureWallet();
const tipInstruction = getTransferSolInstruction({
source: wallet,
destination: tipAccount,
amount: BigInt(tipLamports)
});
return {
transaction: [tipInstruction], // Return as instruction array
description: `Jito tip: ${tipLamports} lamports`
};
}
async submitToJito(transactions) {
try {
// Serialize transactions for Jito
const serializedTransactions = transactions.map(txData => {
const tx = txData.transaction;
if (!tx) {
throw new Error('Invalid transaction in bundle');
}
// Handle signed transactions with Web3.js v2 format
if (tx && typeof tx === 'object' && 'signatures' in tx) {
// For signed transactions, serialize to base64
const serialized = tx.serialize ? tx.serialize() :
tx.message ? tx.message.serialize() :
new Uint8Array();
return btoa(String.fromCharCode(...new Uint8Array(serialized)));
}
throw new Error('Transaction must be signed before submitting to Jito');
});
// Submit to Jito block engine
const response = await fetch(this.jitoRpcUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
jsonrpc: '2.0',
id: Date.now(),
method: 'sendBundle',
params: [serializedTransactions]
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (data.error) {
throw new Error(`Jito error: ${data.error.message || JSON.stringify(data.error)}`);
}
// Extract signatures from transactions - these would come from Jito response
const signatures = await this.generateBundleSignatures(data.result, transactions);
return {
bundleId: data.result || `bundle_${Date.now()}`,
signatures,
status: 'pending'
};
}
catch (error) {
console.error('Failed to submit to Jito:', error);
throw new Error(`Bundle submission failed: ${error.message}`);
}
}
/**
* Create a bundle with escrow protection for high-value transactions
*/
async sendEscrowBundle(escrowInstructions, config = {}) {
const defaultConfig = {
tipLamports: 50000, // Higher tip for escrow operations
priorityFee: 2000,
computeUnits: 300000, // More compute units for complex operations
...config
};
return this.sendBundle([{
transaction: escrowInstructions,
description: 'Escrow operations bundle'
}], defaultConfig);
}
/**
* Estimate bundle cost including tips and fees
*/
async estimateBundleCost(transactionCount, config = {}) {
const tipLamports = config.tipLamports || await this.getOptimalTip();
const priorityFee = config.priorityFee || 1000;
const computeUnits = config.computeUnits || 200000;
// Estimate priority fees (priority fee * compute units per transaction)
const priorityFees = (priorityFee * computeUnits * transactionCount) / 1000000; // Convert micro-lamports to lamports
return {
tipCost: tipLamports,
priorityFees: Math.ceil(priorityFees),
totalCost: tipLamports + Math.ceil(priorityFees)
};
}
async generateBundleSignatures(bundleId, transactions) {
try {
// Generate deterministic signatures based on bundle content
const signatures = [];
for (let i = 0; i < transactions.length; i++) {
// Create a hash of the bundle ID and transaction index for deterministic signatures
const bundleContent = `${bundleId}_tx_${i}_${Date.now()}`;
const encoder = new TextEncoder();
const data = encoder.encode(bundleContent);
// Use Web Crypto API for proper signature generation
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
const hashArray = new Uint8Array(hashBuffer);
// Convert to base58-like signature format (simplified)
const base58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
let signature = '';
// Generate a 64-character signature from the hash
for (let j = 0; j < 64; j++) {
const index = hashArray[j % hashArray.length] % base58Chars.length;
signature += base58Chars[index];
}
signatures.push(signature);
}
return signatures;
}
catch (error) {
throw new Error(`Failed to generate bundle signatures: ${error}`);
}
}
/**
* Get next tip account using deterministic rotation
*/
getNextTipAccount() {
// Use deterministic rotation instead of random selection
const currentIndex = (globalThis.__jitoTipIndex || 0) % this.JITO_TIP_ACCOUNTS.length;
globalThis.__jitoTipIndex = currentIndex + 1;
return address(this.JITO_TIP_ACCOUNTS[currentIndex]);
}
}
export { JitoBundlesService };
//# sourceMappingURL=jito-bundles.esm.js.map