UNPKG

@symmetry-hq/agents-sdk

Version:

Symmetry Agents SDK

233 lines (214 loc) 7.62 kB
import { AddressLookupTableAccount, ComputeBudgetProgram, Connection, Keypair, PublicKey, TransactionInstruction, TransactionMessage, TransactionSignature, VersionedTransaction } from "@solana/web3.js"; import { COMPUTE_UNITS } from "../constants"; export interface VersionedTxs { blockhash: string; lastValidBlockHeight: number; versionedTxs: VersionedTransaction[]; batches: number[]; } export async function delay(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } export async function getAddressLookupTableAccounts( connection: Connection, keys: PublicKey[] ): Promise<AddressLookupTableAccount[]> { const addressLookupTableAccountInfos = await connection.getMultipleAccountsInfo( keys, "confirmed" ); return addressLookupTableAccountInfos.reduce((acc, accountInfo, index) => { const addressLookupTableAddress = keys[index]; if (accountInfo) { const addressLookupTableAccount = new AddressLookupTableAccount({ key: new PublicKey(addressLookupTableAddress), state: AddressLookupTableAccount.deserialize(accountInfo.data), }); acc.push(addressLookupTableAccount); } return acc; }, new Array<AddressLookupTableAccount>()); }; export async function getMultipleAddressLookupTableAccounts( connection: Connection, keys: PublicKey[][], ): Promise<AddressLookupTableAccount[][]> { let allLuts: string[] = []; keys.forEach(luts => luts.forEach(lut => allLuts.push(lut.toBase58())) ) allLuts = [...new Set(allLuts)]; const addressLookupTableAccounts: AddressLookupTableAccount[] = await getAddressLookupTableAccounts( connection, allLuts.map(lut => new PublicKey(lut)) ); const map: {[key: string]: AddressLookupTableAccount} = {}; allLuts.forEach((pubkey, id) => map[pubkey] = addressLookupTableAccounts[id]); return keys.map(luts => luts.map(lut => map[lut.toBase58()])); } export function wrapV0Transaction( blockhash: string, addressLookupTableAccounts: AddressLookupTableAccount[], payerPubkey: PublicKey, priorityFee: number, ixs: TransactionInstruction[], ): VersionedTransaction { ixs = [ ...ixs, ComputeBudgetProgram.setComputeUnitLimit({units: COMPUTE_UNITS}), ComputeBudgetProgram.setComputeUnitPrice({microLamports: priorityFee}), ]; const txMessage = new TransactionMessage({ payerKey: payerPubkey, recentBlockhash: blockhash, instructions: ixs, }); let versionedTx = new VersionedTransaction( txMessage.compileToV0Message(addressLookupTableAccounts) ); try { let tt = versionedTx.serialize().length; if (tt > 1232) { throw new Error("Transaction too large"); } } catch (e: any) { throw new Error(e.message); } return versionedTx; } export async function sendV0Transaction( connection: Connection, tx: VersionedTransaction, blockhash: string, lastValidBlockHeight: number, simulateTransactions: boolean, ): Promise<TransactionSignature> { const serializedTx = tx.serialize(); let txId: TransactionSignature = "Error"; if (simulateTransactions) { txId = await connection.sendRawTransaction( serializedTx, { preflightCommitment: "confirmed"} ).catch(e => {console.log(e.message); return "Error"}); console.log("Simulation txId:", txId); for (let i = 0; i < 4; i++) { await delay(1000).then(() => connection.sendRawTransaction( serializedTx, {preflightCommitment: "confirmed"} ).catch(() => {}) ); } if (txId === "Error") throw new Error("Simulation failed"); } else { connection.sendRawTransaction(serializedTx, {preflightCommitment: "confirmed"}) .catch(e => {console.log(e.message)}); txId = await connection.sendRawTransaction( serializedTx, { skipPreflight: true} ); console.log("Sending tx:", txId); for (let i = 0; i < 4; i++) { await delay(1000).then(() => connection.sendRawTransaction(serializedTx, {skipPreflight: true}).catch(() => {}) ); } } let confirmation = null; let result = null; connection.confirmTransaction({ blockhash, lastValidBlockHeight, signature: txId, }, "confirmed").then((res) => confirmation = res); let iterations = 20; while (confirmation === null && result === null && iterations > 0) { await delay(2500); result = await connection.getTransaction(txId, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (result && result.meta && result.meta?.err) { throw new Error(txId); } iterations--; } if (result) return txId; //@ts-ignore if (!confirmation || confirmation.value.err) { //@ts-ignore console.log(confirmation?.value.err); throw new Error(txId); } return txId; } export async function prepareV0Transactions(params: { connection: Connection, payer: PublicKey, priorityFee: number, multipleIxs: TransactionInstruction[][], multipleLookupTableAddresses: PublicKey[][], signers: Keypair[][], batches: number[], },): Promise<VersionedTxs> { const { connection, payer, priorityFee, multipleIxs, multipleLookupTableAddresses, signers, batches } = params; const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash(); const multipleAddressLookupTableAccounts: AddressLookupTableAccount[][] = await getMultipleAddressLookupTableAccounts( connection, multipleLookupTableAddresses ); const txs = multipleIxs.map((ixs, index) => { let tx = null; try { tx = wrapV0Transaction( blockhash, multipleAddressLookupTableAccounts[index], payer, priorityFee, ixs, ); if (signers[index].length > 0) tx.sign(signers[index]); } catch (e: any) { console.log("Error signing tx:", e.message); } return tx; }).filter(tx => tx !== null); return { blockhash, lastValidBlockHeight, versionedTxs: txs, batches, }; } export async function sendV0Transactions( connection: Connection, txParams: VersionedTxs, simulateTransactions: boolean, ): Promise<TransactionSignature[]> { const { versionedTxs, blockhash, lastValidBlockHeight, batches } = txParams; const signedTxs = versionedTxs; let lastIndex = 0; let txIds: TransactionSignature[] = []; for (let i = 0; i < batches.length; i++) { const ids = await Promise.all( signedTxs.slice(lastIndex, lastIndex + batches[i]).map( signedTx => sendV0Transaction( connection, signedTx, blockhash, lastValidBlockHeight, simulateTransactions ).catch(e => { console.log("Transaction failed:", e.message); return "Error" }) ) ); txIds = [...txIds, ...ids]; lastIndex += batches[i]; } return txIds; }