edwin-sdk
Version:
SDK for integrating AI agents with DeFi protocols
164 lines (137 loc) • 5.62 kB
text/typescript
import axios, { AxiosInstance } from 'axios';
import edwinLogger from '../../../utils/logger';
interface JsonRpcRequest {
jsonrpc: string;
id: number;
method: string;
params: unknown[];
}
interface TipAccount {
address: string;
// Add other tip account properties as needed
}
interface BundleStatus {
status: 'Failed' | 'Landed' | 'Pending';
landed_slot?: number;
error?: string;
}
interface BundleResponse {
result: {
value: BundleStatus[];
};
}
export class JitoJsonRpcClient {
private baseUrl: string;
private uuid?: string;
private client: AxiosInstance;
constructor(baseUrl: string, uuid?: string) {
this.baseUrl = baseUrl;
this.uuid = uuid;
this.client = axios.create({
headers: {
'Content-Type': 'application/json',
},
});
}
async sendRequest<T>(endpoint: string, method: string, params: unknown[] = []): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const data: JsonRpcRequest = {
jsonrpc: '2.0',
id: 1,
method,
params,
};
try {
const response = await this.client.post<T>(url, data);
return response.data;
} catch (error) {
if (axios.isAxiosError(error)) {
edwinLogger.error(`HTTP error: ${error.message}`);
throw error;
} else {
edwinLogger.error(`Unexpected error: ${error}`);
throw new Error('An unexpected error occurred');
}
}
}
async getTipAccounts(): Promise<{ result: TipAccount[] }> {
const endpoint = this.uuid ? `/bundles?uuid=${this.uuid}` : '/bundles';
return this.sendRequest(endpoint, 'getTipAccounts');
}
async getRandomTipAccount(): Promise<TipAccount> {
const tipAccountsResponse = await this.getTipAccounts();
if (
tipAccountsResponse.result &&
Array.isArray(tipAccountsResponse.result) &&
tipAccountsResponse.result.length > 0
) {
const randomIndex = Math.floor(Math.random() * tipAccountsResponse.result.length);
return tipAccountsResponse.result[randomIndex];
}
throw new Error('No tip accounts available');
}
async sendBundle(params: unknown[]): Promise<unknown> {
const endpoint = this.uuid ? `/bundles?uuid=${this.uuid}` : '/bundles';
return this.sendRequest(endpoint, 'sendBundle', params);
}
async sendTxn(params: unknown[], bundleOnly = false): Promise<unknown> {
let endpoint = '/transactions';
const queryParams: string[] = [];
if (bundleOnly) {
queryParams.push('bundleOnly=true');
}
if (this.uuid) {
queryParams.push(`uuid=${this.uuid}`);
}
if (queryParams.length > 0) {
endpoint += `?${queryParams.join('&')}`;
}
return this.sendRequest(endpoint, 'sendTransaction', params);
}
async getInFlightBundleStatuses(bundleIds: (string | number)[]): Promise<BundleResponse> {
const endpoint = this.uuid ? `/bundles?uuid=${this.uuid}` : '/bundles';
return this.sendRequest(endpoint, 'getInflightBundleStatuses', [bundleIds]);
}
async getBundleStatuses(bundleIds: (string | number)[]): Promise<BundleResponse> {
const endpoint = this.uuid ? `/bundles?uuid=${this.uuid}` : '/bundles';
return this.sendRequest(endpoint, 'getBundleStatuses', [bundleIds]);
}
async confirmInflightBundle(bundleId: string | number, timeoutMs = 60000): Promise<BundleStatus> {
const start = Date.now();
while (Date.now() - start < timeoutMs) {
try {
const response = await this.getInFlightBundleStatuses([bundleId]);
if (
response.result?.value &&
Array.isArray(response.result.value) &&
response.result.value.length > 0
) {
const bundleStatus = response.result.value[0];
edwinLogger.info(`Bundle status: ${bundleStatus.status}, Landed slot: ${bundleStatus.landed_slot}`);
if (bundleStatus.status === 'Failed') {
return bundleStatus;
} else if (bundleStatus.status === 'Landed') {
const detailedStatus = await this.getBundleStatuses([bundleId]);
if (
detailedStatus.result?.value &&
Array.isArray(detailedStatus.result.value) &&
detailedStatus.result.value.length > 0
) {
return detailedStatus.result.value[0];
}
return bundleStatus;
}
} else {
edwinLogger.info('No status returned for the bundle. It may be invalid or very old.');
}
} catch (error) {
edwinLogger.error('Error checking bundle status:', error);
}
// Wait for a short time before checking again
await new Promise(resolve => setTimeout(resolve, 2000));
}
// If we've reached this point, the bundle hasn't reached a final state within the timeout
edwinLogger.info(`Bundle ${bundleId} has not reached a final state within ${timeoutMs}ms`);
return { status: 'Pending' };
}
}