Skip to content

Commit b032b6c

Browse files
committed
Needs work but getting there
1 parent 1f0b339 commit b032b6c

File tree

5 files changed

+616
-286
lines changed

5 files changed

+616
-286
lines changed

src/components/home/item-list.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ type Props = {
1717
export const ItemList = ({ initialItems, totalValue }: Props) => {
1818
const { publicKey, sendTransaction, signAllTransactions } = useWallet(); // Get wallet details from useWallet hook
1919
const { connection } = useConnection(); // Get connection from useConnection hook
20-
const { closeTokenAccount } = useCloseTokenAccount(); // Get closeTokenAccount function from useCloseTokenAccount hook
20+
const { closeTokenAccount, closeTokenAccountsAndSendTransaction } = useCloseTokenAccount(); // Get closeTokenAccount function from useCloseTokenAccount hook
2121

2222
const [items] = useState<TokenData[]>(initialItems); // Initialize items state
2323
const [sortedItems, setSortedItems] = useState<TokenData[]>(initialItems); // Initialize sorted items state
@@ -100,17 +100,21 @@ export const ItemList = ({ initialItems, totalValue }: Props) => {
100100

101101
const handleCloseTokenAccounts = async () => {
102102
if (closableTokenAccounts.length > 0 && publicKey && signAllTransactions) {
103-
let closeAccountInstructions: TransactionInstruction[] = [];
104-
for (const closable of closableTokenAccounts) {
105-
const closeAccountInstr = await closeTokenAccount(new PublicKey(closable.tokenAddress)); // Get close account instruction
106-
closeAccountInstructions.push(closeAccountInstr); // Add instruction to list
103+
try {
104+
setMessage("Preparing to close accounts...");
105+
const tokenAccountPubkeys = closableTokenAccounts.map(
106+
account => new PublicKey(account.tokenAddress)
107+
);
108+
const success = await closeTokenAccountsAndSendTransaction(tokenAccountPubkeys, setMessage);
109+
if (success) {
110+
setClosableTokenAccounts([]);
111+
setMessage("");
112+
}
113+
} catch (error) {
114+
console.error("Error closing accounts:", error);
115+
toast.error("Error Closing Token Accounts, Please Reload Page.");
116+
setMessage("");
107117
}
108-
// Send transaction batch to close token accounts
109-
await sendTransactionBatch(closeAccountInstructions, publicKey, signAllTransactions, connection, setMessage, sendTransaction, 'Closing token accounts');
110-
setClosableTokenAccounts([]); // Reset closable token accounts
111-
} else {
112-
toast.error("Error Closing Token Accounts, Please Reload Page."); // Show error toast
113-
setClosableTokenAccounts([]); // Reset closable token accounts
114118
}
115119
};
116120

src/pages/api/sign/create.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import {
22
Connection,
33
Keypair,
44
PublicKey,
5-
Transaction,
6-
TransactionInstruction,
75
VersionedTransaction
86
} from "@solana/web3.js";
97
import { NextApiRequest, NextApiResponse } from "next";
@@ -38,9 +36,6 @@ export default async function handler(
3836
return;
3937
}
4038

41-
const connection = new Connection(NETWORK);
42-
const publicKey = new PublicKey(publicKeyStr);
43-
const wallet = new Wallet(Keypair.fromSecretKey(bs58.decode(privateKey)));
4439

4540
// Get the quote for the swap
4641
const quoteResponse = await (
@@ -67,7 +62,6 @@ export default async function handler(
6762
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
6863
// transaction.feePayer = publicKey;
6964

70-
const blockHash = (await connection.getLatestBlockhash("finalized")).blockhash;
7165
// transaction.recentBlockhash = blockHash;
7266

7367
// Serialize the transaction

src/utils/hooks/useCloseTokenAccount.ts

Lines changed: 86 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
import { useWallet, useConnection } from "@solana/wallet-adapter-react";
2-
import { PublicKey, Transaction, TransactionInstruction } from "@solana/web3.js";
2+
import {
3+
PublicKey,
4+
TransactionInstruction,
5+
VersionedTransaction,
6+
TransactionMessage
7+
} from "@solana/web3.js";
38
import { createCloseAccountInstruction } from "@solana/spl-token";
49
import { toast } from "react-hot-toast";
5-
import { useState } from "react";
10+
import { getTipAccounts, sendTxUsingJito, waitForBundleConfirmation } from "@utils/hooks/useCreateSwapInstructions";
11+
import { SystemProgram } from "@solana/web3.js";
12+
13+
const MAX_ACCOUNTS_PER_TX = 12; // Conservative limit for accounts per transaction
14+
const BUNDLE_TIP = 1000; // Tip amount for Jito bundles
15+
const USER_REJECTION_ERROR = 'User rejected the request';
616

717
export const useCloseTokenAccount = () => {
8-
const { publicKey, signTransaction, sendTransaction } = useWallet();
18+
const { publicKey, signAllTransactions } = useWallet();
919
const { connection } = useConnection();
1020

1121
const closeTokenAccount = async (tokenAccountPubkey: PublicKey): Promise<TransactionInstruction> => {
12-
if (!publicKey || !signTransaction || !tokenAccountPubkey) {
22+
if (!publicKey || !tokenAccountPubkey) {
1323
throw new Error("Wallet not connected or not able to sign transactions or Error with token account address");
1424
}
1525

@@ -37,34 +47,82 @@ export const useCloseTokenAccount = () => {
3747
return closeInstruction;
3848
};
3949

40-
const closeTokenAccountsAndSendTransaction = async (instructions: TransactionInstruction[]) => {
41-
if (!publicKey || !signTransaction) {
42-
throw new Error("Wallet not connected or not able to sign transactions");
50+
const closeTokenAccountsAndSendTransaction = async (
51+
tokenAccounts: PublicKey[],
52+
setMessage?: (msg: string) => void
53+
) => {
54+
if (!publicKey || !signAllTransactions) {
55+
throw new Error("Wallet not connected");
4356
}
4457

45-
const transaction = new Transaction().add(...instructions);
46-
4758
try {
59+
setMessage?.("Preparing to close token accounts...");
60+
const tipAccount = await getTipAccounts();
61+
62+
// Split token accounts into chunks
63+
const chunks: PublicKey[][] = [];
64+
for (let i = 0; i < tokenAccounts.length; i += MAX_ACCOUNTS_PER_TX) {
65+
chunks.push(tokenAccounts.slice(i, i + MAX_ACCOUNTS_PER_TX));
66+
}
67+
68+
console.log(`Split ${tokenAccounts.length} accounts into ${chunks.length} chunks`);
69+
70+
// Create all transactions
71+
const transactions: VersionedTransaction[] = [];
4872
const { blockhash } = await connection.getLatestBlockhash();
49-
const latestBlockHash = await connection.getLatestBlockhash();
50-
transaction.recentBlockhash = blockhash;
51-
transaction.feePayer = publicKey;
52-
53-
const signedTransaction = await signTransaction(transaction);
54-
const signature = await sendTransaction(signedTransaction, connection);
55-
56-
await connection.confirmTransaction({
57-
blockhash,
58-
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
59-
signature
60-
});
61-
62-
console.log("Transaction confirmed with signature:", signature);
63-
toast.success("Transaction confirmed successfully!");
64-
return signature;
65-
} catch (error) {
66-
console.error("Failed to send transaction:", error);
67-
toast.error("Failed to send transaction. Please try again.");
73+
74+
for (const chunk of chunks) {
75+
const instructions = await Promise.all(
76+
chunk.map(account => closeTokenAccount(account))
77+
);
78+
79+
const tipInstruction = SystemProgram.transfer({
80+
fromPubkey: publicKey,
81+
toPubkey: new PublicKey(tipAccount),
82+
lamports: BUNDLE_TIP,
83+
});
84+
85+
const messageV0 = new TransactionMessage({
86+
payerKey: publicKey,
87+
recentBlockhash: blockhash,
88+
instructions: [...instructions, tipInstruction],
89+
}).compileToV0Message();
90+
91+
transactions.push(new VersionedTransaction(messageV0));
92+
}
93+
94+
setMessage?.("Please approve the transaction in your wallet...");
95+
96+
try {
97+
// Sign all transactions at once
98+
const signedTransactions = await signAllTransactions(transactions);
99+
100+
// Send transactions as Jito bundles
101+
for (let i = 0; i < signedTransactions.length; i++) {
102+
setMessage?.(`Processing chunk ${i + 1}/${signedTransactions.length}...`);
103+
const signedTx = signedTransactions[i];
104+
const serializedTx = signedTx.serialize();
105+
const bundleId = await sendTxUsingJito([serializedTx]);
106+
107+
setMessage?.(`Confirming chunk ${i + 1}...`);
108+
const confirmed = await waitForBundleConfirmation(bundleId);
109+
if (!confirmed) {
110+
throw new Error(`Bundle ${bundleId} failed to confirm`);
111+
}
112+
}
113+
114+
toast.success("Successfully closed all token accounts!");
115+
return true;
116+
} catch (error: any) {
117+
if (error?.message?.includes(USER_REJECTION_ERROR)) {
118+
toast.error("Transaction rejected by user");
119+
return false;
120+
}
121+
throw error;
122+
}
123+
} catch (error: any) {
124+
console.error("Failed to close token accounts:", error);
125+
toast.error(error?.message || "Failed to close token accounts. Please try again.");
68126
throw error;
69127
}
70128
};

0 commit comments

Comments
 (0)