test-raydium-sdk-v2
Version:
An SDK for building applications on top of Raydium.
855 lines (794 loc) • 32 kB
text/typescript
import {
Connection,
PublicKey,
sendAndConfirmTransaction,
Signer,
Transaction,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from "@solana/web3.js";
import axios from "axios";
import { SignAllTransactions, ComputeBudgetConfig } from "@/raydium/type";
import { TxVersion } from "./txType";
import { Owner } from "../owner";
import { getRecentBlockHash, addComputeBudget, checkLegacyTxSize, checkV0TxSize, printSimulate } from "./txUtils";
import { CacheLTA, getMultipleLookupTableInfo, LOOKUP_TABLE_CACHE } from "./lookupTable";
interface SolanaFeeInfo {
min: number;
max: number;
avg: number;
priorityTx: number;
nonVotes: number;
priorityRatio: number;
avgCuPerBlock: number;
blockspaceUsageRatio: number;
}
type SolanaFeeInfoJson = {
"1": SolanaFeeInfo;
"5": SolanaFeeInfo;
"15": SolanaFeeInfo;
};
interface TxBuilderInit {
connection: Connection;
feePayer: PublicKey;
owner?: Owner;
signAllTransactions?: SignAllTransactions;
}
export interface AddInstructionParam {
addresses?: Record<string, PublicKey>;
instructions?: TransactionInstruction[];
endInstructions?: TransactionInstruction[];
lookupTableAddress?: string[];
signers?: Signer[];
instructionTypes?: string[];
endInstructionTypes?: string[];
}
export interface TxBuildData<T = Record<string, any>> {
builder: TxBuilder;
transaction: Transaction;
instructionTypes: string[];
signers: Signer[];
execute: () => Promise<{ txId: string; signedTx: Transaction }>;
extInfo: T;
}
export interface TxV0BuildData<T = Record<string, any>> extends Omit<TxBuildData<T>, "transaction" | "execute"> {
builder: TxBuilder;
transaction: VersionedTransaction;
buildProps?: {
lookupTableCache?: CacheLTA;
lookupTableAddress?: string[];
};
execute: () => Promise<{ txId: string; signedTx: VersionedTransaction }>;
}
export interface ExecuteParam {
sequentially: boolean;
onTxUpdate?: (completeTxs: { txId: string; status: "success" | "error" | "sent" }[]) => void;
}
export interface MultiTxBuildData<T = Record<string, any>> {
builder: TxBuilder;
transactions: Transaction[];
instructionTypes: string[];
signers: Signer[][];
execute: (executeParams?: ExecuteParam) => Promise<{ txIds: string[]; signedTxs: Transaction[] }>;
extInfo: T;
}
export interface MultiTxV0BuildData<T = Record<string, any>>
extends Omit<MultiTxBuildData<T>, "transactions" | "execute"> {
builder: TxBuilder;
transactions: VersionedTransaction[];
buildProps?: {
lookupTableCache?: CacheLTA;
lookupTableAddress?: string[];
};
execute: (executeParams?: ExecuteParam) => Promise<{ txIds: string[]; signedTxs: VersionedTransaction[] }>;
}
export type MakeMultiTxData<T = TxVersion.LEGACY, O = Record<string, any>> = T extends TxVersion.LEGACY
? MultiTxBuildData<O>
: MultiTxV0BuildData<O>;
export type MakeTxData<T = TxVersion.LEGACY, O = Record<string, any>> = T extends TxVersion.LEGACY
? TxBuildData<O>
: TxV0BuildData<O>;
export class TxBuilder {
private connection: Connection;
private owner?: Owner;
private instructions: TransactionInstruction[] = [];
private endInstructions: TransactionInstruction[] = [];
private lookupTableAddress: string[] = [];
private signers: Signer[] = [];
private instructionTypes: string[] = [];
private endInstructionTypes: string[] = [];
private feePayer: PublicKey;
private signAllTransactions?: SignAllTransactions;
constructor(params: TxBuilderInit) {
this.connection = params.connection;
this.feePayer = params.feePayer;
this.signAllTransactions = params.signAllTransactions;
this.owner = params.owner;
}
get AllTxData(): {
instructions: TransactionInstruction[];
endInstructions: TransactionInstruction[];
signers: Signer[];
instructionTypes: string[];
endInstructionTypes: string[];
lookupTableAddress: string[];
} {
return {
instructions: this.instructions,
endInstructions: this.endInstructions,
signers: this.signers,
instructionTypes: this.instructionTypes,
endInstructionTypes: this.endInstructionTypes,
lookupTableAddress: this.lookupTableAddress,
};
}
get allInstructions(): TransactionInstruction[] {
return [...this.instructions, ...this.endInstructions];
}
public async getComputeBudgetConfig(): Promise<ComputeBudgetConfig | undefined> {
const json = (
await axios.get<SolanaFeeInfoJson>(`https://solanacompass.com/api/fees?cacheFreshTime=${5 * 60 * 1000}`)
).data;
const { avg } = json?.[15] ?? {};
if (!avg) return undefined;
return {
units: 600000,
microLamports: Math.min(Math.ceil((avg * 1000000) / 600000), 25000),
};
}
public addCustomComputeBudget(config?: ComputeBudgetConfig) {
if (config) {
const { instructions, instructionTypes } = addComputeBudget(config);
this.instructions.unshift(...instructions);
this.instructionTypes.unshift(...instructionTypes);
return true;
}
return false;
}
public async calComputeBudget({
config: propConfig,
defaultIns,
}: {
config?: ComputeBudgetConfig;
defaultIns?: TransactionInstruction[];
}): Promise<void> {
try {
const config = propConfig || (await this.getComputeBudgetConfig());
if (this.addCustomComputeBudget(config)) return;
defaultIns && this.instructions.unshift(...defaultIns);
} catch {
defaultIns && this.instructions.unshift(...defaultIns);
}
}
public addInstruction({
instructions = [],
endInstructions = [],
signers = [],
instructionTypes = [],
endInstructionTypes = [],
lookupTableAddress = [],
}: AddInstructionParam): TxBuilder {
this.instructions.push(...instructions);
this.endInstructions.push(...endInstructions);
this.signers.push(...signers);
this.instructionTypes.push(...instructionTypes);
this.endInstructionTypes.push(...endInstructionTypes);
this.lookupTableAddress.push(...lookupTableAddress.filter((address) => address !== PublicKey.default.toString()));
return this;
}
public async versionBuild<O = Record<string, any>>({
txVersion,
extInfo,
}: {
txVersion?: TxVersion;
extInfo?: O;
}): Promise<MakeTxData<TxVersion.LEGACY, O> | MakeTxData<TxVersion.V0, O>> {
if (txVersion === TxVersion.V0) return (await this.buildV0({ ...(extInfo || {}) })) as MakeTxData<TxVersion.V0, O>;
return this.build<O>(extInfo) as MakeTxData<TxVersion.LEGACY, O>;
}
public build<O = Record<string, any>>(extInfo?: O): MakeTxData<TxVersion.LEGACY, O> {
const transaction = new Transaction();
if (this.allInstructions.length) transaction.add(...this.allInstructions);
transaction.feePayer = this.feePayer;
return {
builder: this,
transaction,
signers: this.signers,
instructionTypes: [...this.instructionTypes, ...this.endInstructionTypes],
execute: async () => {
const recentBlockHash = await getRecentBlockHash(this.connection);
transaction.recentBlockhash = recentBlockHash;
if (this.signers.length) transaction.sign(...this.signers);
printSimulate([transaction]);
if (this.owner?.isKeyPair) {
return {
txId: await sendAndConfirmTransaction(this.connection, transaction, this.signers),
signedTx: transaction,
};
}
if (this.signAllTransactions) {
const txs = await this.signAllTransactions([transaction]);
return {
txId: await this.connection.sendRawTransaction(txs[0].serialize(), { skipPreflight: true }),
signedTx: txs[0],
};
}
throw new Error("please connect wallet first");
},
extInfo: extInfo || ({} as O),
};
}
public buildMultiTx<T = Record<string, any>>(params: {
extraPreBuildData?: MakeTxData<TxVersion.LEGACY>[];
extInfo?: T;
}): MultiTxBuildData {
const { extraPreBuildData = [], extInfo } = params;
const { transaction } = this.build(extInfo);
const filterExtraBuildData = extraPreBuildData.filter((data) => data.transaction.instructions.length > 0);
const allTransactions: Transaction[] = [transaction, ...filterExtraBuildData.map((data) => data.transaction)];
const allSigners: Signer[][] = [this.signers, ...filterExtraBuildData.map((data) => data.signers)];
const allInstructionTypes: string[] = [
...this.instructionTypes,
...filterExtraBuildData.map((data) => data.instructionTypes).flat(),
];
return {
builder: this,
transactions: allTransactions,
signers: allSigners,
instructionTypes: allInstructionTypes,
execute: async (executeParams?: ExecuteParam) => {
const { sequentially, onTxUpdate } = executeParams || {};
const recentBlockHash = await getRecentBlockHash(this.connection);
if (this.owner?.isKeyPair) {
return {
txIds: await await Promise.all(
allTransactions.map(async (tx, idx) => {
tx.recentBlockhash = recentBlockHash;
return await sendAndConfirmTransaction(this.connection, tx, allSigners[idx]);
}),
),
signedTxs: allTransactions,
};
}
if (this.signAllTransactions) {
const partialSignedTxs = allTransactions.map((tx, idx) => {
tx.recentBlockhash = recentBlockHash;
if (allSigners[idx].length) tx.sign(...allSigners[idx]);
return tx;
});
printSimulate(partialSignedTxs);
const signedTxs = await this.signAllTransactions(partialSignedTxs);
if (sequentially) {
let i = 0;
const processedTxs: { txId: string; status: "success" | "error" | "sent" }[] = [];
const checkSendTx = async (): Promise<void> => {
if (!signedTxs[i]) return;
const txId = await this.connection.sendRawTransaction(signedTxs[i].serialize(), { skipPreflight: true });
processedTxs.push({ txId, status: "sent" });
onTxUpdate?.([...processedTxs]);
i++;
this.connection.onSignature(
txId,
(signatureResult) => {
const targetTxIdx = processedTxs.findIndex((tx) => tx.txId === txId);
if (targetTxIdx > -1) processedTxs[targetTxIdx].status = signatureResult.err ? "error" : "success";
onTxUpdate?.([...processedTxs]);
checkSendTx();
},
"processed",
);
this.connection.getSignatureStatus(txId);
};
await checkSendTx();
return {
txIds: processedTxs.map((d) => d.txId),
signedTxs,
};
} else {
const txIds: string[] = [];
for (let i = 0; i < signedTxs.length; i += 1) {
const txId = await this.connection.sendRawTransaction(signedTxs[i].serialize(), { skipPreflight: true });
txIds.push(txId);
}
return {
txIds,
signedTxs,
};
}
}
throw new Error("please connect wallet first");
},
extInfo: extInfo || {},
};
}
public async versionMultiBuild<T extends TxVersion, O = Record<string, any>>({
extraPreBuildData,
txVersion,
extInfo,
}: {
extraPreBuildData?: MakeTxData<TxVersion.V0>[] | MakeTxData<TxVersion.LEGACY>[];
txVersion?: T;
extInfo?: O;
}): Promise<MakeMultiTxData<T, O>> {
if (txVersion === TxVersion.V0)
return (await this.buildV0MultiTx({
extraPreBuildData: extraPreBuildData as MakeTxData<TxVersion.V0>[],
buildProps: extInfo || {},
})) as MakeMultiTxData<T, O>;
return this.buildMultiTx<O>({
extraPreBuildData: extraPreBuildData as MakeTxData<TxVersion.LEGACY>[],
extInfo,
}) as MakeMultiTxData<T, O>;
}
public async buildV0<O = Record<string, any>>(
props?: O & {
lookupTableCache?: CacheLTA;
lookupTableAddress?: string[];
forerunCreate?: boolean;
},
): Promise<MakeTxData<TxVersion.V0, O>> {
const { lookupTableCache = {}, lookupTableAddress = [], forerunCreate, ...extInfo } = props || {};
const lookupTableAddressAccount = {
...LOOKUP_TABLE_CACHE,
...lookupTableCache,
};
const allLTA = Array.from(new Set<string>([...lookupTableAddress, ...this.lookupTableAddress]));
const needCacheLTA: PublicKey[] = [];
for (const item of allLTA) {
if (lookupTableAddressAccount[item] === undefined) needCacheLTA.push(new PublicKey(item));
}
const newCacheLTA = await getMultipleLookupTableInfo({ connection: this.connection, address: needCacheLTA });
for (const [key, value] of Object.entries(newCacheLTA)) lookupTableAddressAccount[key] = value;
const messageV0 = new TransactionMessage({
payerKey: this.feePayer,
recentBlockhash: forerunCreate ? PublicKey.default.toBase58() : await getRecentBlockHash(this.connection),
instructions: [...this.allInstructions],
}).compileToV0Message(Object.values(lookupTableAddressAccount));
const transaction = new VersionedTransaction(messageV0);
transaction.sign(this.signers);
return {
builder: this,
transaction,
signers: this.signers,
instructionTypes: [...this.instructionTypes, ...this.endInstructionTypes],
execute: async () => {
printSimulate([transaction]);
if (this.owner?.isKeyPair) {
transaction.sign([this.owner.signer as Signer]);
return {
txId: await this.connection.sendTransaction(transaction, { skipPreflight: true }),
signedTx: transaction,
};
}
if (this.signAllTransactions) {
const txs = await this.signAllTransactions<VersionedTransaction>([transaction]);
return {
txId: await this.connection.sendTransaction(txs[0], { skipPreflight: true }),
signedTx: txs[0],
};
}
throw new Error("please connect wallet first");
},
extInfo: (extInfo || {}) as O,
};
}
public async buildV0MultiTx<T = Record<string, any>>(params: {
extraPreBuildData?: MakeTxData<TxVersion.V0>[];
buildProps?: T & {
lookupTableCache?: CacheLTA;
lookupTableAddress?: string[];
};
}): Promise<MultiTxV0BuildData> {
const { extraPreBuildData = [], buildProps } = params;
const { transaction } = await this.buildV0(buildProps);
const filterExtraBuildData = extraPreBuildData.filter((data) => data.builder.instructions.length > 0);
const allTransactions: VersionedTransaction[] = [
transaction,
...filterExtraBuildData.map((data) => data.transaction),
];
const allSigners: Signer[][] = [this.signers, ...filterExtraBuildData.map((data) => data.signers)];
const allInstructionTypes: string[] = [
...this.instructionTypes,
...filterExtraBuildData.map((data) => data.instructionTypes).flat(),
];
allTransactions.forEach(async (tx, idx) => {
tx.sign(allSigners[idx]);
});
return {
builder: this,
transactions: allTransactions,
signers: allSigners,
instructionTypes: allInstructionTypes,
buildProps,
execute: async (executeParams?: ExecuteParam) => {
printSimulate(allTransactions);
const { sequentially, onTxUpdate } = executeParams || {};
if (this.owner?.isKeyPair) {
allTransactions.forEach((tx) => tx.sign([this.owner!.signer as Signer]));
return {
txIds: await Promise.all(
allTransactions.map(async (tx) => {
return await this.connection.sendTransaction(tx);
}),
),
signedTxs: allTransactions,
};
}
if (this.signAllTransactions) {
const signedTxs = await this.signAllTransactions(allTransactions);
if (sequentially) {
let i = 0;
const processedTxs: { txId: string; status: "success" | "error" | "sent" }[] = [];
const checkSendTx = async (): Promise<void> => {
if (!signedTxs[i]) return;
const txId = await this.connection.sendTransaction(signedTxs[i], { skipPreflight: true });
processedTxs.push({ txId, status: "sent" });
onTxUpdate?.([...processedTxs]);
i++;
this.connection.onSignature(
txId,
(signatureResult) => {
const targetTxIdx = processedTxs.findIndex((tx) => tx.txId === txId);
if (targetTxIdx > -1) processedTxs[targetTxIdx].status = signatureResult.err ? "error" : "success";
onTxUpdate?.([...processedTxs]);
checkSendTx();
},
"processed",
);
this.connection.getSignatureStatus(txId);
};
checkSendTx();
return {
txIds: [],
signedTxs,
};
} else {
const txIds: string[] = [];
for (let i = 0; i < signedTxs.length; i += 1) {
const txId = await this.connection.sendTransaction(signedTxs[i], { skipPreflight: true });
txIds.push(txId);
}
return { txIds, signedTxs };
}
}
throw new Error("please connect wallet first");
},
extInfo: buildProps || {},
};
}
public async sizeCheckBuild(
props?: Record<string, any> & { autoComputeBudget?: boolean },
): Promise<MultiTxBuildData> {
const { autoComputeBudget = false, ...extInfo } = props || {};
let computeBudgetData: { instructions: TransactionInstruction[]; instructionTypes: string[] } = {
instructions: [],
instructionTypes: [],
};
if (autoComputeBudget) {
const computeConfig = autoComputeBudget ? await this.getComputeBudgetConfig() : undefined;
computeBudgetData =
autoComputeBudget && computeConfig
? addComputeBudget(computeConfig)
: { instructions: [], instructionTypes: [] };
}
const signerKey: { [key: string]: Signer } = this.signers.reduce(
(acc, cur) => ({ ...acc, [cur.publicKey.toBase58()]: cur }),
{},
);
const allTransactions: Transaction[] = [];
const allSigners: Signer[][] = [];
let instructionQueue: TransactionInstruction[] = [];
this.allInstructions.forEach((item) => {
const _itemIns = [...instructionQueue, item];
const _itemInsWithCompute = autoComputeBudget ? [...computeBudgetData.instructions, ..._itemIns] : _itemIns;
const _signerStrs = new Set<string>(
_itemIns.map((i) => i.keys.filter((ii) => ii.isSigner).map((ii) => ii.pubkey.toString())).flat(),
);
const _signer = [..._signerStrs.values()].map((i) => new PublicKey(i));
if (
(instructionQueue.length < 12 &&
checkLegacyTxSize({ instructions: _itemInsWithCompute, payer: this.feePayer, signers: _signer })) ||
checkLegacyTxSize({ instructions: _itemIns, payer: this.feePayer, signers: _signer })
) {
// current ins add to queue still not exceed tx size limit
instructionQueue.push(item);
} else {
if (instructionQueue.length === 0) throw Error("item ins too big");
// if add computeBudget still not exceed tx size limit
if (
checkLegacyTxSize({
instructions: autoComputeBudget
? [...computeBudgetData.instructions, ...instructionQueue]
: [...instructionQueue],
payer: this.feePayer,
signers: _signer,
})
) {
allTransactions.push(new Transaction().add(...computeBudgetData.instructions, ...instructionQueue));
} else {
allTransactions.push(new Transaction().add(...instructionQueue));
}
allSigners.push(
Array.from(
new Set<string>(
instructionQueue.map((i) => i.keys.filter((ii) => ii.isSigner).map((ii) => ii.pubkey.toString())).flat(),
),
)
.map((i) => signerKey[i])
.filter((i) => i !== undefined),
);
instructionQueue = [item];
}
});
if (instructionQueue.length > 0) {
const _signerStrs = new Set<string>(
instructionQueue.map((i) => i.keys.filter((ii) => ii.isSigner).map((ii) => ii.pubkey.toString())).flat(),
);
const _signers = [..._signerStrs.values()].map((i) => signerKey[i]).filter((i) => i !== undefined);
if (
checkLegacyTxSize({
instructions: autoComputeBudget
? [...computeBudgetData.instructions, ...instructionQueue]
: [...instructionQueue],
payer: this.feePayer,
signers: _signers.map((s) => s.publicKey),
})
) {
allTransactions.push(new Transaction().add(...computeBudgetData.instructions, ...instructionQueue));
} else {
allTransactions.push(new Transaction().add(...instructionQueue));
}
allSigners.push(_signers);
}
allTransactions.forEach((tx) => (tx.feePayer = this.feePayer));
return {
builder: this,
transactions: allTransactions,
signers: allSigners,
instructionTypes: this.instructionTypes,
execute: async (executeParams?: ExecuteParam) => {
const { sequentially, onTxUpdate } = executeParams || {};
const recentBlockHash = await getRecentBlockHash(this.connection);
allTransactions.forEach(async (tx, idx) => {
tx.recentBlockhash = recentBlockHash;
if (allSigners[idx].length) tx.sign(...allSigners[idx]);
});
printSimulate(allTransactions);
if (this.owner?.isKeyPair) {
return {
txIds: await Promise.all(
allTransactions.map(async (tx, idx) => {
return await sendAndConfirmTransaction(this.connection, tx, allSigners[idx]);
}),
),
signedTxs: allTransactions,
};
}
if (this.signAllTransactions) {
const signedTxs = await this.signAllTransactions(allTransactions);
if (sequentially) {
let i = 0;
const processedTxs: { txId: string; status: "success" | "error" | "sent" }[] = [];
const checkSendTx = async (): Promise<void> => {
if (!signedTxs[i]) return;
const txId = await this.connection.sendRawTransaction(signedTxs[i].serialize(), { skipPreflight: true });
processedTxs.push({ txId, status: "sent" });
onTxUpdate?.([...processedTxs]);
i++;
this.connection.onSignature(
txId,
(signatureResult) => {
const targetTxIdx = processedTxs.findIndex((tx) => tx.txId === txId);
if (targetTxIdx > -1) processedTxs[targetTxIdx].status = signatureResult.err ? "error" : "success";
onTxUpdate?.([...processedTxs]);
checkSendTx();
},
"processed",
);
this.connection.getSignatureStatus(txId);
};
await checkSendTx();
return {
txIds: processedTxs.map((d) => d.txId),
signedTxs,
};
} else {
const txIds: string[] = [];
for (let i = 0; i < signedTxs.length; i += 1) {
const txId = await this.connection.sendRawTransaction(signedTxs[i].serialize(), { skipPreflight: true });
txIds.push(txId);
}
return { txIds, signedTxs };
}
}
throw new Error("please connect wallet first");
},
extInfo: extInfo || {},
};
}
public async sizeCheckBuildV0(
props?: Record<string, any> & {
autoComputeBudget?: boolean;
lookupTableCache?: CacheLTA;
lookupTableAddress?: string[];
},
): Promise<MultiTxV0BuildData> {
const { autoComputeBudget = false, lookupTableCache = {}, lookupTableAddress = [], ...extInfo } = props || {};
const lookupTableAddressAccount = {
...LOOKUP_TABLE_CACHE,
...lookupTableCache,
};
const allLTA = Array.from(new Set<string>([...this.lookupTableAddress, ...lookupTableAddress]));
const needCacheLTA: PublicKey[] = [];
for (const item of allLTA) {
if (lookupTableAddressAccount[item] === undefined) needCacheLTA.push(new PublicKey(item));
}
const newCacheLTA = await getMultipleLookupTableInfo({ connection: this.connection, address: needCacheLTA });
for (const [key, value] of Object.entries(newCacheLTA)) lookupTableAddressAccount[key] = value;
let computeBudgetData: { instructions: TransactionInstruction[]; instructionTypes: string[] } = {
instructions: [],
instructionTypes: [],
};
if (autoComputeBudget) {
const computeConfig = autoComputeBudget ? await this.getComputeBudgetConfig() : undefined;
computeBudgetData =
autoComputeBudget && computeConfig
? addComputeBudget(computeConfig)
: { instructions: [], instructionTypes: [] };
}
const blockHash = await getRecentBlockHash(this.connection);
const signerKey: { [key: string]: Signer } = this.signers.reduce(
(acc, cur) => ({ ...acc, [cur.publicKey.toBase58()]: cur }),
{},
);
const allTransactions: VersionedTransaction[] = [];
const allSigners: Signer[][] = [];
let instructionQueue: TransactionInstruction[] = [];
this.allInstructions.forEach((item) => {
const _itemIns = [...instructionQueue, item];
const _itemInsWithCompute = autoComputeBudget ? [...computeBudgetData.instructions, ..._itemIns] : _itemIns;
if (
(instructionQueue.length < 12 &&
checkV0TxSize({ instructions: _itemInsWithCompute, payer: this.feePayer, lookupTableAddressAccount })) ||
checkV0TxSize({ instructions: _itemIns, payer: this.feePayer, lookupTableAddressAccount })
) {
// current ins add to queue still not exceed tx size limit
instructionQueue.push(item);
} else {
if (instructionQueue.length === 0) throw Error("item ins too big");
const lookupTableAddress: undefined | CacheLTA = {};
for (const item of [...new Set<string>(allLTA)]) {
if (lookupTableAddressAccount[item] !== undefined) lookupTableAddress[item] = lookupTableAddressAccount[item];
}
// if add computeBudget still not exceed tx size limit
if (
autoComputeBudget &&
checkV0TxSize({
instructions: [...computeBudgetData.instructions, ...instructionQueue],
payer: this.feePayer,
lookupTableAddressAccount,
recentBlockhash: blockHash,
})
) {
const messageV0 = new TransactionMessage({
payerKey: this.feePayer,
recentBlockhash: blockHash,
instructions: [...computeBudgetData.instructions, ...instructionQueue],
}).compileToV0Message(Object.values(lookupTableAddressAccount));
allTransactions.push(new VersionedTransaction(messageV0));
} else {
const messageV0 = new TransactionMessage({
payerKey: this.feePayer,
recentBlockhash: blockHash,
instructions: [...instructionQueue],
}).compileToV0Message(Object.values(lookupTableAddressAccount));
allTransactions.push(new VersionedTransaction(messageV0));
}
allSigners.push(
Array.from(
new Set<string>(
instructionQueue.map((i) => i.keys.filter((ii) => ii.isSigner).map((ii) => ii.pubkey.toString())).flat(),
),
)
.map((i) => signerKey[i])
.filter((i) => i !== undefined),
);
instructionQueue = [item];
}
});
if (instructionQueue.length > 0) {
const _signerStrs = new Set<string>(
instructionQueue.map((i) => i.keys.filter((ii) => ii.isSigner).map((ii) => ii.pubkey.toString())).flat(),
);
const _signers = [..._signerStrs.values()].map((i) => signerKey[i]).filter((i) => i !== undefined);
if (
autoComputeBudget &&
checkV0TxSize({
instructions: [...computeBudgetData.instructions, ...instructionQueue],
payer: this.feePayer,
lookupTableAddressAccount,
recentBlockhash: blockHash,
})
) {
const messageV0 = new TransactionMessage({
payerKey: this.feePayer,
recentBlockhash: blockHash,
instructions: [...computeBudgetData.instructions, ...instructionQueue],
}).compileToV0Message(Object.values(lookupTableAddressAccount));
allTransactions.push(new VersionedTransaction(messageV0));
} else {
const messageV0 = new TransactionMessage({
payerKey: this.feePayer,
recentBlockhash: blockHash,
instructions: [...instructionQueue],
}).compileToV0Message(Object.values(lookupTableAddressAccount));
allTransactions.push(new VersionedTransaction(messageV0));
}
allSigners.push(_signers);
}
return {
builder: this,
transactions: allTransactions,
buildProps: props,
signers: allSigners,
instructionTypes: this.instructionTypes,
execute: async (executeParams?: ExecuteParam) => {
const { sequentially, onTxUpdate } = executeParams || {};
allTransactions.map(async (tx, idx) => {
if (allSigners[idx].length) tx.sign(allSigners[idx]);
});
printSimulate(allTransactions);
if (this.owner?.isKeyPair) {
return {
txIds: await Promise.all(
allTransactions.map(async (tx) => {
return await this.connection.sendTransaction(tx, { skipPreflight: true });
}),
),
signedTxs: allTransactions,
};
}
if (this.signAllTransactions) {
const signedTxs = await this.signAllTransactions(allTransactions);
if (sequentially) {
let i = 0;
const processedTxs: { txId: string; status: "success" | "error" | "sent" }[] = [];
const checkSendTx = async (): Promise<void> => {
if (!signedTxs[i]) return;
const txId = await this.connection.sendTransaction(signedTxs[i], { skipPreflight: true });
processedTxs.push({ txId, status: "sent" });
onTxUpdate?.([...processedTxs]);
i++;
this.connection.onSignature(
txId,
(signatureResult) => {
const targetTxIdx = processedTxs.findIndex((tx) => tx.txId === txId);
if (targetTxIdx > -1) processedTxs[targetTxIdx].status = signatureResult.err ? "error" : "success";
onTxUpdate?.([...processedTxs]);
checkSendTx();
},
"processed",
);
this.connection.getSignatureStatus(txId);
};
checkSendTx();
return {
txIds: [],
signedTxs,
};
} else {
const txIds: string[] = [];
for (let i = 0; i < signedTxs.length; i += 1) {
const txId = await this.connection.sendTransaction(signedTxs[i], { skipPreflight: true });
txIds.push(txId);
}
return { txIds, signedTxs };
}
}
throw new Error("please connect wallet first");
},
extInfo: extInfo || {},
};
}
}