@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 +26 -6
- package/dist/index.cjs +27 -25
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -12
- package/package.json +5 -4
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
|
|
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
|
-
- 💸
|
|
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.
|
|
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
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
8
|
+
exports.PROGRAM_ID = new web3_js_1.PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR");
|
|
4
9
|
/** Hardcoded fee recipient on-chain expectation */
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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 ?? "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
79
|
+
const data = new Uint8Array([0]);
|
|
78
80
|
// accounts order MUST match the on-chain program's expectation:
|
|
79
|
-
// [0]
|
|
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
|
|
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 ?? "
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
74
|
+
const data = new Uint8Array([0]);
|
|
78
75
|
// accounts order MUST match the on-chain program's expectation:
|
|
79
|
-
// [0]
|
|
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.
|
|
4
|
-
"description": "SDK to burn and close SPL token accounts via the Unclaimed SOL program
|
|
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
|
-
"
|
|
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": {
|