@macalinao/grill
Version:
Modern Solana development kit for React applications with automatic account batching, caching, and transaction notifications
144 lines • 5.87 kB
JavaScript
import { compressTransactionMessageUsingAddressLookupTables, signAndSendTransactionMessageWithSigners, } from "@solana/kit";
import { createTransaction } from "gill";
import { getSignatureFromBytes } from "../get-signature-from-bytes.js";
import { pollConfirmTransaction } from "../poll-confirm-transaction.js";
/**
* Creates a function to send transactions using the modern @solana/kit API
* while maintaining compatibility with the wallet adapter.
*/
export const createSendTX = ({ signer, rpc, refetchAccounts, onTransactionStatusEvent, getExplorerLink, }) => {
return async (name, ixs, options = {}) => {
const txId = Math.random().toString(36).substring(2, 15);
const baseEvent = {
id: txId,
title: name,
};
if (!signer) {
onTransactionStatusEvent({
...baseEvent,
type: "error-wallet-not-connected",
});
throw new Error("Wallet not connected");
}
onTransactionStatusEvent({
...baseEvent,
type: "preparing",
});
const { value: latestBlockhash } = await rpc.getLatestBlockhash().send();
const transactionMessage = createTransaction({
version: 0,
feePayer: signer,
instructions: [...ixs],
latestBlockhash,
// the compute budget values are HIGHLY recommend to be set in order to maximize your transaction landing rate
computeUnitLimit: options.computeUnitLimit === null
? undefined
: (options.computeUnitLimit ?? 1_400_000),
computeUnitPrice: options.computeUnitPrice === null
? undefined
: (options.computeUnitPrice ?? 100000n),
});
// Apply address lookup tables if provided to compress the transaction
const addressLookupTables = options.lookupTables ?? {};
const finalTransactionMessage = Object.keys(addressLookupTables).length > 0
? compressTransactionMessageUsingAddressLookupTables(transactionMessage, addressLookupTables)
: transactionMessage;
onTransactionStatusEvent({
...baseEvent,
type: "awaiting-wallet-signature",
});
// Send transaction using wallet adapter
let sigBytes;
try {
sigBytes = await signAndSendTransactionMessageWithSigners(finalTransactionMessage);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : "Failed to send transaction";
onTransactionStatusEvent({
...baseEvent,
type: "error-transaction-send-failed",
errorMessage,
});
throw error;
}
const sig = getSignatureFromBytes(sigBytes);
const sentTxEvent = {
...baseEvent,
sig,
explorerLink: getExplorerLink({ transaction: sig }),
};
onTransactionStatusEvent({
...sentTxEvent,
type: "waiting-for-confirmation",
});
try {
const result = await pollConfirmTransaction({
signature: sig,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
rpc,
});
onTransactionStatusEvent({
...sentTxEvent,
type: "confirmed",
});
// Reload the accounts that were written to
const writableAccounts = result.transaction.message.accountKeys
.filter((key) => key.writable)
.map((k) => k.pubkey);
if (writableAccounts.length > 0) {
const waitForAccountRefetch = options.waitForAccountRefetch ?? true;
if (waitForAccountRefetch) {
await refetchAccounts(writableAccounts);
}
else {
// Refetch in background without waiting
refetchAccounts(writableAccounts).catch((error) => {
console.warn("Failed to refetch accounts in background:", error);
});
}
}
if (result.meta?.logMessages) {
console.log(name, result.meta.logMessages.join("\n"));
}
// Return the signature as a base58 string
return sig;
}
catch (error) {
// Log error details for debugging
console.error(`${name} transaction failed:`, error);
// Extract error logs
const extractErrorLogs = (err) => {
if (err &&
typeof err === "object" &&
"logs" in err &&
Array.isArray(err.logs)) {
return err.logs;
}
if (err &&
typeof err === "object" &&
"context" in err &&
typeof err.context === "object" &&
err.context.logs &&
Array.isArray(err.context.logs)) {
return err.context.logs;
}
return [];
};
const errorLogs = extractErrorLogs(error);
if (errorLogs.length > 0) {
console.log("Transaction logs:");
for (const log of errorLogs) {
console.log(" ", log);
}
}
const errorMessage = error instanceof Error ? error.message : "Transaction failed.";
onTransactionStatusEvent({
...sentTxEvent,
type: "error-transaction-failed",
errorMessage,
});
throw error;
}
};
};
//# sourceMappingURL=create-send-tx.js.map