Skip to Content

Reclaim SOL

Complete flow from wallet scan to confirmed burn. This recipe shows the full happy path with error handling.

Full example

import { BurnerClient, SessionScope } from '@burnandclaim/sdk' import type { WalletItem, ItemSelection, BuiltTransaction, SubmissionResult, } from '@burnandclaim/sdk' import { VersionedTransaction } from '@solana/web3.js' /** * Server-side: create a session token with all required scopes. */ async function createSession(walletAddress: string): Promise<string> { const server = new BurnerClient({ apiKey: process.env.BURNER_API_KEY, }) const session = await server.createSession({ walletAddress, scopes: [ SessionScope.WalletRead, SessionScope.TransactionsBuild, SessionScope.TransactionsSubmit, ], ttlSeconds: 600, }) return session.token } /** * Client-side: scan, build, sign, and submit. */ async function reclaimSol( sessionToken: string, walletAddress: string, signAllTransactions: (txs: VersionedTransaction[]) => Promise<VersionedTransaction[]> ) { const client = new BurnerClient({ sessionToken }) // 1. Scan the wallet const { items, counts } = await client.getWalletAssets(walletAddress) if (items.length === 0) { return { reclaimed: 0, burned: 0 } } // 2. Select all closeable/burnable items const selections: ItemSelection[] = items .filter((item) => item.actions.length > 0) .map((item) => ({ type: item.type, address: item.address, action: item.actions[0], // prefer the first action })) // 3. Build unsigned transactions const { transactions, totals } = await client.buildTransactions( walletAddress, { items: selections } ) console.log(`Building ${transactions.length} transactions for ${totals.itemCount} items`) console.log(`Estimated reclaim: ${(totals.netRebate / 1e9).toFixed(6)} SOL`) // 4. Deserialize and sign const unsigned = transactions.map((tx) => VersionedTransaction.deserialize( Buffer.from(tx.transactionBase64, 'base64') ) ) const signed = await signAllTransactions(unsigned) // 5. Submit const { results } = await client.submitTransactions(walletAddress, { signed: signed.map((tx, i) => ({ transactionBase64: Buffer.from(tx.serialize()).toString('base64'), submissionTicket: transactions[i].submissionTicket, })), }) // 6. Report let successCount = 0 let failCount = 0 for (const result of results) { switch (result.status) { case 'submitted': successCount++ console.log(`OK: https://solscan.io/tx/${result.signature}`) break case 'duplicate': successCount++ // already submitted, count as success break case 'failed': failCount++ console.error(`FAIL: ${result.error?.code} - ${result.error?.message}`) break } } return { reclaimed: totals.netRebate / 1e9, burned: successCount, failed: failCount, } }

Filtering by item type

You may want to let users choose which types of items to burn. Filter the selections before building:

// Only close empty accounts (safest -- no tokens are destroyed) const emptyAccountsOnly = items .filter((item) => item.type === 'empty_account') .map((item) => ({ type: item.type, address: item.address, action: 'close' as const, }))

Handling expired blockhashes

If the user takes too long to sign, the blockhash may expire. Catch the TICKET_EXPIRED or RPC_ERROR and rebuild:

const { results } = await client.submitTransactions(walletAddress, { signed }) const expired = results.filter( (r) => r.status === 'failed' && r.error?.code === 'TICKET_EXPIRED' ) if (expired.length > 0) { // Rebuild only the failed items const failedSelections = expired.flatMap((r) => r.includes) const rebuilt = await client.buildTransactions(walletAddress, { items: failedSelections, }) // Sign and submit again... }

Displaying rebate estimates

The totals object from buildTransactions has everything you need for a confirmation dialog:

function formatConfirmation(totals: { itemCount: number grossRebate: number netRebate: number donationAmount: number fees: number }) { return { items: totals.itemCount, grossSol: (totals.grossRebate / 1e9).toFixed(6), netSol: (totals.netRebate / 1e9).toFixed(6), feeSol: (totals.fees / 1e9).toFixed(6), donationSol: (totals.donationAmount / 1e9).toFixed(6), } } // e.g. { items: 15, grossSol: "0.030589", netSol: "0.029060", feeSol: "0.001529", donationSol: "0.000000" }
Last updated on