@unclaimedsol/spl-burn-close-sdk 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,14 +1,16 @@
1
1
  # Burn and Close Solana SPL Tokens SDK
2
2
 
3
- A lightweight Solana SDK to **burn SPL tokens and close token accounts** with a **fixed 5%** protocol fee.
3
+ A lightweight Solana SDK to **burn SPL tokens and close token accounts** with the June 2026 Unclaimed SOL token cleanup fee policy.
4
4
  This library only **builds unsigned transactions** – you handle signing and sending them, whether in the browser (Wallet Adapter) or server (Keypair).
5
5
 
6
6
  ## ✨ Features
7
7
 
8
8
  - 🔥 Burn all tokens in provided ATAs.
9
9
  - 🧹 Close token accounts, reclaiming rent to the user.
10
- - 💸 Automatic 5% protocol fee (hardcoded in program).
10
+ - 💸 On-chain fee recipient is enforced by the program.
11
+ - 🧾 Standard token cleanup lets the user keep up to 0.002 SOL per closed account; normal-account fee is 39,280 lamports.
11
12
  - ⚡ Automatic batching (default 12 ATAs per transaction).
13
+ - 🪙 Supports SPL Token and Token-2022.
12
14
  - 🔐 Supports **frontend partial signing** and **backend full signing** workflows.
13
15
  - 🚀 No hidden config: single function to integrate.
14
16
 
@@ -18,7 +20,7 @@ This library only **builds unsigned transactions** – you handle signing and se
18
20
 
19
21
  1. The SDK prepares TransactionInstructions for your on-chain burn+close program.
20
22
  2. Program burns tokens in each ATA, then closes it, sending rent back to the owner.
21
- 3. A 5% fee is enforced on-chain and sent to the hardcoded recipient.
23
+ 3. The program sends recovered rent above 2,000,000 lamports per successfully closed account to the hardcoded fee recipient.
22
24
  4. The SDK never asks for private keys — you sign & send transactions yourself.
23
25
 
24
26
  ---
@@ -29,7 +31,7 @@ This library only **builds unsigned transactions** – you handle signing and se
29
31
  npm i @unclaimedsol/spl-burn-close-sdk
30
32
  ```
31
33
 
32
- We use @solana/web3.js in peerDependencies to avoid version conflicts. So, make sure you already have it installed.
34
+ We use @solana/web3.js in peerDependencies to avoid version conflicts, so make sure you already have it installed.
33
35
 
34
36
  Requires Node 18+.
35
37
 
@@ -40,7 +42,7 @@ Requires Node 18+.
40
42
  ### Signing with Keypair
41
43
 
42
44
  ```typescript
43
- import { Connection, Keypair } from "@solana/web3.js";
45
+ import { Connection, Keypair, PublicKey } from "@solana/web3.js";
44
46
  import {
45
47
  buildBurnAndCloseTransactions,
46
48
  PROGRAM_ID,
@@ -82,6 +84,7 @@ import { Connection, PublicKey } from "@solana/web3.js";
82
84
  import {
83
85
  buildBurnAndCloseTransactions,
84
86
  PROGRAM_ID,
87
+ TOKEN_2022_PROGRAM_ID,
85
88
  } from "@unclaimedsol/spl-burn-close-sdk";
86
89
 
87
90
  const connection = new Connection("https://api.mainnet-beta.solana.com");
@@ -100,6 +103,16 @@ const txs = await buildBurnAndCloseTransactions(
100
103
  pairs
101
104
  );
102
105
 
106
+ // For Token-2022 accounts, pass TOKEN_2022_PROGRAM_ID and consider using a smaller chunkSize:
107
+ // const txs = await buildBurnAndCloseTransactions(
108
+ // connection,
109
+ // PROGRAM_ID,
110
+ // user,
111
+ // pairs,
112
+ // { chunkSize: 2 },
113
+ // TOKEN_2022_PROGRAM_ID
114
+ // );
115
+
103
116
  // Sign
104
117
  const signed = wallet.signAllTransactions
105
118
  ? await wallet.signAllTransactions(txs)
@@ -107,7 +120,7 @@ const signed = wallet.signAllTransactions
107
120
 
108
121
  // Send
109
122
  for (const stx of signed) {
110
- const sig = await connection.sendRawTransaction(tx.serialize());
123
+ const sig = await connection.sendRawTransaction(stx.serialize());
111
124
  let latestBlockhash = await connection.getLatestBlockhash(
112
125
  connection.commitment
113
126
  );
@@ -122,6 +135,13 @@ for (const stx of signed) {
122
135
 
123
136
  ---
124
137
 
138
+ ## 🌐 Cluster support
139
+
140
+ This SDK targets the Unclaimed SOL mainnet program (`PROGRAM_ID`) and fee recipient (`FEE_RECIPIENT`).
141
+ Transactions will only succeed on a cluster where that program is deployed.
142
+
143
+ ---
144
+
125
145
  ## 💻 Demo
126
146
 
127
147
  There is a nice tutorial guide with a demo repository that shows the usage of the SDK to burn and close all token accounts for the wallet. You can find it [here](https://blog.unclaimedsol.com/close-burn-solana-tokens-web3js-sdk/).
package/dist/index.cjs CHANGED
@@ -1,9 +1,15 @@
1
- import { PublicKey, SystemProgram, Transaction, TransactionInstruction, } from "@solana/web3.js";
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TOKEN_2022_PROGRAM_ID = exports.TOKEN_PROGRAM_ID = exports.FEE_RECIPIENT = exports.PROGRAM_ID = void 0;
4
+ exports.buildBurnAndCloseTransactions = buildBurnAndCloseTransactions;
5
+ exports.buildBurnAndCloseInstruction = buildBurnAndCloseInstruction;
6
+ const web3_js_1 = require("@solana/web3.js");
2
7
  /** Exporting on-chain program ID for convenience */
3
- const PROGRAM_ID = new PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR");
8
+ exports.PROGRAM_ID = new web3_js_1.PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR");
4
9
  /** Hardcoded fee recipient on-chain expectation */
5
- const FEE_RECIPIENT = new PublicKey("uncNCRycMtNPj2kXHfwiCgNrzCTnC9xU5FS8ZeWyn3M");
6
- const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
10
+ exports.FEE_RECIPIENT = new web3_js_1.PublicKey("uncNCRycMtNPj2kXHfwiCgNrzCTnC9xU5FS8ZeWyn3M");
11
+ exports.TOKEN_PROGRAM_ID = new web3_js_1.PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
12
+ exports.TOKEN_2022_PROGRAM_ID = new web3_js_1.PublicKey("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
7
13
  /**
8
14
  * Build *unsigned* transactions that call the on-chain burn+close program.
9
15
  * You sign and send them yourself (frontend wallet or backend keypair).
@@ -16,11 +22,11 @@ const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ
16
22
  * @param tokenProgramId Support for multiple Token programs
17
23
  * @returns Array of *unsigned* Transaction objects
18
24
  */
19
- export async function buildBurnAndCloseTransactions(connection, programId, user, pairs, options = {}, tokenProgramId = TOKEN_PROGRAM_ID) {
25
+ async function buildBurnAndCloseTransactions(connection, programId, user, pairs, options = {}, tokenProgramId = exports.TOKEN_PROGRAM_ID) {
20
26
  if (!pairs.length)
21
27
  return [];
22
28
  const chunkSize = Math.max(1, options.chunkSize ?? 12);
23
- const commitment = options.commitment ?? "processed";
29
+ const commitment = options.commitment ?? "confirmed";
24
30
  const feePayer = options.feePayer ?? user;
25
31
  // Split into chunks so we stay under CU / account limits
26
32
  const chunks = [];
@@ -28,35 +34,31 @@ export async function buildBurnAndCloseTransactions(connection, programId, user,
28
34
  chunks.push(pairs.slice(i, i + chunkSize));
29
35
  }
30
36
  // Instruction data: [selector=0]
31
- const data = Buffer.from([0]);
37
+ // Use Uint8Array (browser-safe) and cast for web3.js' Buffer typing.
38
+ const data = new Uint8Array([0]);
39
+ const recentBlockhash = options.recentBlockhash ??
40
+ (await connection.getLatestBlockhash({ commitment })).blockhash;
32
41
  const txs = [];
33
42
  for (const chunk of chunks) {
34
43
  const keys = [
35
44
  // NOTE: Mark the user as signer in the instruction meta;
36
45
  // the caller must actually sign the Transaction later.
37
- { pubkey: FEE_RECIPIENT, isSigner: false, isWritable: true },
46
+ { pubkey: exports.FEE_RECIPIENT, isSigner: false, isWritable: true },
38
47
  { pubkey: user, isSigner: true, isWritable: true },
39
48
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
40
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
49
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
41
50
  ];
42
51
  for (const { mint, ata } of chunk) {
43
52
  keys.push({ pubkey: mint, isSigner: false, isWritable: true }, { pubkey: ata, isSigner: false, isWritable: true });
44
53
  }
45
- const ix = new TransactionInstruction({
54
+ const ix = new web3_js_1.TransactionInstruction({
46
55
  programId,
47
56
  keys,
48
57
  data,
49
58
  });
50
- const tx = new Transaction().add(ix);
59
+ const tx = new web3_js_1.Transaction().add(ix);
51
60
  tx.feePayer = feePayer;
52
- // Use provided blockhash or fetch one
53
- if (options.recentBlockhash) {
54
- tx.recentBlockhash = options.recentBlockhash;
55
- }
56
- else {
57
- const { blockhash } = await connection.getLatestBlockhash({ commitment });
58
- tx.recentBlockhash = blockhash;
59
- }
61
+ tx.recentBlockhash = recentBlockhash;
60
62
  txs.push(tx);
61
63
  }
62
64
  return txs;
@@ -69,28 +71,28 @@ export async function buildBurnAndCloseTransactions(connection, programId, user,
69
71
  * create or sign full transactions — it only constructs one
70
72
  * instruction for a batch of (mint, ATA) pairs.
71
73
  */
72
- export async function buildBurnAndCloseInstruction(programId, user, pairs, tokenProgramId) {
74
+ async function buildBurnAndCloseInstruction(programId, user, pairs, tokenProgramId) {
73
75
  if (!pairs.length) {
74
76
  return null;
75
77
  }
76
78
  // instruction data selector: 0 = burn_and_close_token_accounts
77
- const data = Buffer.from([0]);
79
+ const data = new Uint8Array([0]);
78
80
  // accounts order MUST match the on-chain program's expectation:
79
- // [0] fee_payer_account (your fee recipient)
81
+ // [0] fee_recipient_account (your fee recipient)
80
82
  // [1] user_wallet_account (the signer)
81
83
  // [2] token_program_account (SPL Token program or Token-2022 program)
82
84
  // [3] system_program_account
83
85
  // then repeating (mint, ata) pairs, both writable
84
86
  const keys = [
85
- { pubkey: FEE_RECIPIENT, isSigner: false, isWritable: true },
87
+ { pubkey: exports.FEE_RECIPIENT, isSigner: false, isWritable: true },
86
88
  { pubkey: user, isSigner: true, isWritable: true },
87
89
  { pubkey: tokenProgramId, isSigner: false, isWritable: false },
88
- { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
90
+ { pubkey: web3_js_1.SystemProgram.programId, isSigner: false, isWritable: false },
89
91
  ];
90
92
  for (const { mint, ata } of pairs) {
91
93
  keys.push({ pubkey: mint, isSigner: false, isWritable: true }, { pubkey: ata, isSigner: false, isWritable: true });
92
94
  }
93
- return new TransactionInstruction({
95
+ return new web3_js_1.TransactionInstruction({
94
96
  programId,
95
97
  keys,
96
98
  data,
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export declare const PROGRAM_ID: PublicKey;
4
4
  /** Hardcoded fee recipient on-chain expectation */
5
5
  export declare const FEE_RECIPIENT: PublicKey;
6
6
  export declare const TOKEN_PROGRAM_ID: PublicKey;
7
+ export declare const TOKEN_2022_PROGRAM_ID: PublicKey;
7
8
  export type Pair = {
8
9
  mint: PublicKey;
9
10
  ata: PublicKey;
@@ -20,7 +21,7 @@ export type BuildOptions = {
20
21
  feePayer?: PublicKey;
21
22
  /**
22
23
  * Provide your own recent blockhash (advanced). If omitted,
23
- * we fetch a fresh one per transaction.
24
+ * we fetch a fresh one and reuse it for all built transactions.
24
25
  */
25
26
  recentBlockhash?: string;
26
27
  };
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export const PROGRAM_ID = new PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWP
4
4
  /** Hardcoded fee recipient on-chain expectation */
5
5
  export const FEE_RECIPIENT = new PublicKey("uncNCRycMtNPj2kXHfwiCgNrzCTnC9xU5FS8ZeWyn3M");
6
6
  export const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
7
+ export const TOKEN_2022_PROGRAM_ID = new PublicKey("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
7
8
  /**
8
9
  * Build *unsigned* transactions that call the on-chain burn+close program.
9
10
  * You sign and send them yourself (frontend wallet or backend keypair).
@@ -20,7 +21,7 @@ export async function buildBurnAndCloseTransactions(connection, programId, user,
20
21
  if (!pairs.length)
21
22
  return [];
22
23
  const chunkSize = Math.max(1, options.chunkSize ?? 12);
23
- const commitment = options.commitment ?? "processed";
24
+ const commitment = options.commitment ?? "confirmed";
24
25
  const feePayer = options.feePayer ?? user;
25
26
  // Split into chunks so we stay under CU / account limits
26
27
  const chunks = [];
@@ -28,7 +29,10 @@ export async function buildBurnAndCloseTransactions(connection, programId, user,
28
29
  chunks.push(pairs.slice(i, i + chunkSize));
29
30
  }
30
31
  // Instruction data: [selector=0]
31
- const data = Buffer.from([0]);
32
+ // Use Uint8Array (browser-safe) and cast for web3.js' Buffer typing.
33
+ const data = new Uint8Array([0]);
34
+ const recentBlockhash = options.recentBlockhash ??
35
+ (await connection.getLatestBlockhash({ commitment })).blockhash;
32
36
  const txs = [];
33
37
  for (const chunk of chunks) {
34
38
  const keys = [
@@ -49,14 +53,7 @@ export async function buildBurnAndCloseTransactions(connection, programId, user,
49
53
  });
50
54
  const tx = new Transaction().add(ix);
51
55
  tx.feePayer = feePayer;
52
- // Use provided blockhash or fetch one
53
- if (options.recentBlockhash) {
54
- tx.recentBlockhash = options.recentBlockhash;
55
- }
56
- else {
57
- const { blockhash } = await connection.getLatestBlockhash({ commitment });
58
- tx.recentBlockhash = blockhash;
59
- }
56
+ tx.recentBlockhash = recentBlockhash;
60
57
  txs.push(tx);
61
58
  }
62
59
  return txs;
@@ -74,9 +71,9 @@ export async function buildBurnAndCloseInstruction(programId, user, pairs, token
74
71
  return null;
75
72
  }
76
73
  // instruction data selector: 0 = burn_and_close_token_accounts
77
- const data = Buffer.from([0]);
74
+ const data = new Uint8Array([0]);
78
75
  // accounts order MUST match the on-chain program's expectation:
79
- // [0] fee_payer_account (your fee recipient)
76
+ // [0] fee_recipient_account (your fee recipient)
80
77
  // [1] user_wallet_account (the signer)
81
78
  // [2] token_program_account (SPL Token program or Token-2022 program)
82
79
  // [3] system_program_account
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unclaimedsol/spl-burn-close-sdk",
3
- "version": "1.2.0",
4
- "description": "SDK to burn and close SPL token accounts via the Unclaimed SOL program (with a built-in 5% fee).",
3
+ "version": "1.2.2",
4
+ "description": "SDK to burn and close SPL token accounts via the Unclaimed SOL program with benchmarked rent cleanup fees.",
5
5
  "license": "MIT",
6
6
  "author": "Unclaimed SOL",
7
7
  "type": "module",
@@ -28,13 +28,14 @@
28
28
  "README.md"
29
29
  ],
30
30
  "scripts": {
31
- "build": "tsc -p tsconfig.json && node ./scripts/build-cjs.js",
31
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.cjs.json && node ./scripts/build-cjs.js",
32
32
  "prepublishOnly": "npm run build"
33
33
  },
34
- "dependencies": {
34
+ "peerDependencies": {
35
35
  "@solana/web3.js": "^1.98.4"
36
36
  },
37
37
  "devDependencies": {
38
+ "@solana/web3.js": "^1.98.4",
38
39
  "typescript": "^5.6.3"
39
40
  },
40
41
  "engines": {