@unclaimedsol/spl-burn-close-sdk 1.0.0
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 +121 -0
- package/dist/index.cjs +64 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +64 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Burn and Close Solana SPL Tokens SDK
|
|
2
|
+
|
|
3
|
+
A lightweight Solana SDK to **burn SPL tokens and close token accounts** with a **fixed 5%** protocol fee.
|
|
4
|
+
This library only **builds unsigned transactions** โ you handle signing and sending them, whether in the browser (Wallet Adapter) or server (Keypair).
|
|
5
|
+
|
|
6
|
+
## โจ Features
|
|
7
|
+
|
|
8
|
+
- ๐ฅ Burn all tokens in provided ATAs.
|
|
9
|
+
- ๐งน Close token accounts, reclaiming rent to the user.
|
|
10
|
+
- ๐ธ Automatic 5% protocol fee (hardcoded in program).
|
|
11
|
+
- โก Automatic batching (default 12 ATAs per transaction).
|
|
12
|
+
- ๐ Supports **frontend partial signing** and **backend full signing** workflows.
|
|
13
|
+
- ๐ No hidden config: single function to integrate.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## ๐ฆ Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm i @unclaimedsol/spl-burn-close-sdk @solana/web3.js
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
We use peerDependencies to avoid version conflicts.
|
|
24
|
+
|
|
25
|
+
Requires Node 18+.
|
|
26
|
+
|
|
27
|
+
## ๐ Usage
|
|
28
|
+
|
|
29
|
+
### Signing with Keypair
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Connection, Keypair } from "@solana/web3.js";
|
|
33
|
+
import {
|
|
34
|
+
buildBurnAndCloseTransactions,
|
|
35
|
+
PROGRAM_ID,
|
|
36
|
+
} from "@unclaimedsol/spl-burn-close-sdk";
|
|
37
|
+
|
|
38
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
39
|
+
const keypair = Keypair.fromSecretKey(/* Uint8Array */);
|
|
40
|
+
const user = keypair.publicKey;
|
|
41
|
+
|
|
42
|
+
// You can get easily obtain pairs using getTokenAccountsByOwner method
|
|
43
|
+
const pairs = [{ mint: new PublicKey("MINT_1"), ata: new PublicKey("ATA_1") }];
|
|
44
|
+
|
|
45
|
+
const txs = await buildBurnAndCloseTransactions(
|
|
46
|
+
connection,
|
|
47
|
+
PROGRAM_ID,
|
|
48
|
+
user,
|
|
49
|
+
pairs
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
for (const tx of txs) {
|
|
53
|
+
tx.partialSign(keypair);
|
|
54
|
+
const sig = await connection.sendRawTransaction(tx.serialize());
|
|
55
|
+
let latestBlockhash = await connection.getLatestBlockhash(
|
|
56
|
+
connection.commitment
|
|
57
|
+
);
|
|
58
|
+
await connection.confirmTransaction({
|
|
59
|
+
signature: sig,
|
|
60
|
+
blockhash: latestBlockhash.blockhash,
|
|
61
|
+
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
|
62
|
+
});
|
|
63
|
+
console.log("โ
Sent:", sig);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Signing with Wallet Adapter
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { Connection, PublicKey } from "@solana/web3.js";
|
|
71
|
+
import {
|
|
72
|
+
buildBurnAndCloseTransactions,
|
|
73
|
+
PROGRAM_ID,
|
|
74
|
+
} from "@unclaimedsol/spl-burn-close-sdk";
|
|
75
|
+
|
|
76
|
+
const connection = new Connection("https://api.mainnet-beta.solana.com");
|
|
77
|
+
const user = wallet.publicKey; // Wallet Adapter
|
|
78
|
+
|
|
79
|
+
// You can get easily obtain pairs using getTokenAccountsByOwner method
|
|
80
|
+
const pairs = [
|
|
81
|
+
{ mint: new PublicKey("MINT_1"), ata: new PublicKey("ATA_1") },
|
|
82
|
+
{ mint: new PublicKey("MINT_2"), ata: new PublicKey("ATA_2") },
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
const txs = await buildBurnAndCloseTransactions(
|
|
86
|
+
connection,
|
|
87
|
+
PROGRAM_ID,
|
|
88
|
+
user,
|
|
89
|
+
pairs
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Sign
|
|
93
|
+
const signed = wallet.signAllTransactions
|
|
94
|
+
? await wallet.signAllTransactions(txs)
|
|
95
|
+
: await Promise.all(txs.map((t) => wallet.signTransaction!(t)));
|
|
96
|
+
|
|
97
|
+
// Send
|
|
98
|
+
for (const stx of signed) {
|
|
99
|
+
const sig = await connection.sendRawTransaction(tx.serialize());
|
|
100
|
+
let latestBlockhash = await connection.getLatestBlockhash(
|
|
101
|
+
connection.commitment
|
|
102
|
+
);
|
|
103
|
+
await connection.confirmTransaction({
|
|
104
|
+
signature: sig,
|
|
105
|
+
blockhash: latestBlockhash.blockhash,
|
|
106
|
+
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
|
107
|
+
});
|
|
108
|
+
console.log("โ
Sent:", sig);
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## ๐งฉ How It Works
|
|
113
|
+
|
|
114
|
+
1. The SDK prepares TransactionInstructions for your on-chain burn+close program.
|
|
115
|
+
2. Program burns tokens in each ATA, then closes it, sending rent back to the owner.
|
|
116
|
+
3. A 5% fee is enforced on-chain and sent to the hardcoded recipient.
|
|
117
|
+
4. The SDK never asks for private keys โ you sign & send transactions yourself.
|
|
118
|
+
|
|
119
|
+
## ๐ License
|
|
120
|
+
|
|
121
|
+
MIT ยฉ [Unclaimed SOL](https://unclaimedsol.com)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PublicKey, SystemProgram, Transaction, TransactionInstruction, } from "@solana/web3.js";
|
|
2
|
+
/** Exporting on-chain program ID for convenience */
|
|
3
|
+
const PROGRAM_ID = new PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR");
|
|
4
|
+
/** Hardcoded fee recipient on-chain expectation */
|
|
5
|
+
const FEE_RECIPIENT = new PublicKey("uncNCRycMtNPj2kXHfwiCgNrzCTnC9xU5FS8ZeWyn3M");
|
|
6
|
+
const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
7
|
+
/** Fixed fee percentage enforced by the program (5%) */
|
|
8
|
+
const FIXED_FEE_PERCENTAGE = 5; // do not change
|
|
9
|
+
/**
|
|
10
|
+
* Build *unsigned* transactions that call the on-chain burn+close program.
|
|
11
|
+
* You sign and send them yourself (frontend wallet or backend keypair).
|
|
12
|
+
*
|
|
13
|
+
* @param connection Solana RPC connection
|
|
14
|
+
* @param programId Your deployed program ID
|
|
15
|
+
* @param user The user whose ATAs will be burned/closed
|
|
16
|
+
* @param pairs Array of { mint, ata } to process
|
|
17
|
+
* @param options Chunking / blockhash / payer tweaks
|
|
18
|
+
* @returns Array of *unsigned* Transaction objects
|
|
19
|
+
*/
|
|
20
|
+
export async function buildBurnAndCloseTransactions(connection, programId, user, pairs, options = {}) {
|
|
21
|
+
if (!pairs.length)
|
|
22
|
+
return [];
|
|
23
|
+
const chunkSize = Math.max(1, options.chunkSize ?? 12);
|
|
24
|
+
const commitment = options.commitment ?? "confirmed";
|
|
25
|
+
const feePayer = options.feePayer ?? user;
|
|
26
|
+
// Split into chunks so we stay under CU / account limits
|
|
27
|
+
const chunks = [];
|
|
28
|
+
for (let i = 0; i < pairs.length; i += chunkSize) {
|
|
29
|
+
chunks.push(pairs.slice(i, i + chunkSize));
|
|
30
|
+
}
|
|
31
|
+
// Instruction data: [selector=0, fee_pct=5]
|
|
32
|
+
const data = Buffer.from([0, FIXED_FEE_PERCENTAGE]);
|
|
33
|
+
const txs = [];
|
|
34
|
+
for (const chunk of chunks) {
|
|
35
|
+
const keys = [
|
|
36
|
+
// NOTE: Mark the user as signer in the instruction meta;
|
|
37
|
+
// the caller must actually sign the Transaction later.
|
|
38
|
+
{ pubkey: FEE_RECIPIENT, isSigner: false, isWritable: true },
|
|
39
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
40
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
41
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
42
|
+
];
|
|
43
|
+
for (const { mint, ata } of chunk) {
|
|
44
|
+
keys.push({ pubkey: mint, isSigner: false, isWritable: true }, { pubkey: ata, isSigner: false, isWritable: true });
|
|
45
|
+
}
|
|
46
|
+
const ix = new TransactionInstruction({
|
|
47
|
+
programId,
|
|
48
|
+
keys,
|
|
49
|
+
data,
|
|
50
|
+
});
|
|
51
|
+
const tx = new Transaction().add(ix);
|
|
52
|
+
tx.feePayer = feePayer;
|
|
53
|
+
// Use provided blockhash or fetch one
|
|
54
|
+
if (options.recentBlockhash) {
|
|
55
|
+
tx.recentBlockhash = options.recentBlockhash;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const { blockhash } = await connection.getLatestBlockhash({ commitment });
|
|
59
|
+
tx.recentBlockhash = blockhash;
|
|
60
|
+
}
|
|
61
|
+
txs.push(tx);
|
|
62
|
+
}
|
|
63
|
+
return txs;
|
|
64
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Connection, PublicKey, Transaction, Commitment } from "@solana/web3.js";
|
|
2
|
+
/** Exporting on-chain program ID for convenience */
|
|
3
|
+
export declare const PROGRAM_ID: PublicKey;
|
|
4
|
+
/** Hardcoded fee recipient on-chain expectation */
|
|
5
|
+
export declare const FEE_RECIPIENT: PublicKey;
|
|
6
|
+
export declare const TOKEN_PROGRAM_ID: PublicKey;
|
|
7
|
+
/** Fixed fee percentage enforced by the program (5%) */
|
|
8
|
+
export declare const FIXED_FEE_PERCENTAGE = 5;
|
|
9
|
+
export type Pair = {
|
|
10
|
+
mint: PublicKey;
|
|
11
|
+
ata: PublicKey;
|
|
12
|
+
};
|
|
13
|
+
export type BuildOptions = {
|
|
14
|
+
/** Max pairs per transaction (default: 12) */
|
|
15
|
+
chunkSize?: number;
|
|
16
|
+
/** Use this commitment when fetching blockhash (default: "confirmed") */
|
|
17
|
+
commitment?: Commitment;
|
|
18
|
+
/**
|
|
19
|
+
* Optional explicit fee payer (defaults to `user`)
|
|
20
|
+
* Note: whoever signs must cover fees on-chain.
|
|
21
|
+
*/
|
|
22
|
+
feePayer?: PublicKey;
|
|
23
|
+
/**
|
|
24
|
+
* Provide your own recent blockhash (advanced). If omitted,
|
|
25
|
+
* we fetch a fresh one per transaction.
|
|
26
|
+
*/
|
|
27
|
+
recentBlockhash?: string;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Build *unsigned* transactions that call the on-chain burn+close program.
|
|
31
|
+
* You sign and send them yourself (frontend wallet or backend keypair).
|
|
32
|
+
*
|
|
33
|
+
* @param connection Solana RPC connection
|
|
34
|
+
* @param programId Your deployed program ID
|
|
35
|
+
* @param user The user whose ATAs will be burned/closed
|
|
36
|
+
* @param pairs Array of { mint, ata } to process
|
|
37
|
+
* @param options Chunking / blockhash / payer tweaks
|
|
38
|
+
* @returns Array of *unsigned* Transaction objects
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildBurnAndCloseTransactions(connection: Connection, programId: PublicKey, user: PublicKey, pairs: Pair[], options?: BuildOptions): Promise<Transaction[]>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PublicKey, SystemProgram, Transaction, TransactionInstruction, } from "@solana/web3.js";
|
|
2
|
+
/** Exporting on-chain program ID for convenience */
|
|
3
|
+
export const PROGRAM_ID = new PublicKey("UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR");
|
|
4
|
+
/** Hardcoded fee recipient on-chain expectation */
|
|
5
|
+
export const FEE_RECIPIENT = new PublicKey("uncNCRycMtNPj2kXHfwiCgNrzCTnC9xU5FS8ZeWyn3M");
|
|
6
|
+
export const TOKEN_PROGRAM_ID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");
|
|
7
|
+
/** Fixed fee percentage enforced by the program (5%) */
|
|
8
|
+
export const FIXED_FEE_PERCENTAGE = 5; // do not change
|
|
9
|
+
/**
|
|
10
|
+
* Build *unsigned* transactions that call the on-chain burn+close program.
|
|
11
|
+
* You sign and send them yourself (frontend wallet or backend keypair).
|
|
12
|
+
*
|
|
13
|
+
* @param connection Solana RPC connection
|
|
14
|
+
* @param programId Your deployed program ID
|
|
15
|
+
* @param user The user whose ATAs will be burned/closed
|
|
16
|
+
* @param pairs Array of { mint, ata } to process
|
|
17
|
+
* @param options Chunking / blockhash / payer tweaks
|
|
18
|
+
* @returns Array of *unsigned* Transaction objects
|
|
19
|
+
*/
|
|
20
|
+
export async function buildBurnAndCloseTransactions(connection, programId, user, pairs, options = {}) {
|
|
21
|
+
if (!pairs.length)
|
|
22
|
+
return [];
|
|
23
|
+
const chunkSize = Math.max(1, options.chunkSize ?? 12);
|
|
24
|
+
const commitment = options.commitment ?? "confirmed";
|
|
25
|
+
const feePayer = options.feePayer ?? user;
|
|
26
|
+
// Split into chunks so we stay under CU / account limits
|
|
27
|
+
const chunks = [];
|
|
28
|
+
for (let i = 0; i < pairs.length; i += chunkSize) {
|
|
29
|
+
chunks.push(pairs.slice(i, i + chunkSize));
|
|
30
|
+
}
|
|
31
|
+
// Instruction data: [selector=0, fee_pct=5]
|
|
32
|
+
const data = Buffer.from([0, FIXED_FEE_PERCENTAGE]);
|
|
33
|
+
const txs = [];
|
|
34
|
+
for (const chunk of chunks) {
|
|
35
|
+
const keys = [
|
|
36
|
+
// NOTE: Mark the user as signer in the instruction meta;
|
|
37
|
+
// the caller must actually sign the Transaction later.
|
|
38
|
+
{ pubkey: FEE_RECIPIENT, isSigner: false, isWritable: true },
|
|
39
|
+
{ pubkey: user, isSigner: true, isWritable: true },
|
|
40
|
+
{ pubkey: TOKEN_PROGRAM_ID, isSigner: false, isWritable: false },
|
|
41
|
+
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
|
|
42
|
+
];
|
|
43
|
+
for (const { mint, ata } of chunk) {
|
|
44
|
+
keys.push({ pubkey: mint, isSigner: false, isWritable: true }, { pubkey: ata, isSigner: false, isWritable: true });
|
|
45
|
+
}
|
|
46
|
+
const ix = new TransactionInstruction({
|
|
47
|
+
programId,
|
|
48
|
+
keys,
|
|
49
|
+
data,
|
|
50
|
+
});
|
|
51
|
+
const tx = new Transaction().add(ix);
|
|
52
|
+
tx.feePayer = feePayer;
|
|
53
|
+
// Use provided blockhash or fetch one
|
|
54
|
+
if (options.recentBlockhash) {
|
|
55
|
+
tx.recentBlockhash = options.recentBlockhash;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const { blockhash } = await connection.getLatestBlockhash({ commitment });
|
|
59
|
+
tx.recentBlockhash = blockhash;
|
|
60
|
+
}
|
|
61
|
+
txs.push(tx);
|
|
62
|
+
}
|
|
63
|
+
return txs;
|
|
64
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@unclaimedsol/spl-burn-close-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "SDK to burn and close SPL token accounts via the Unclaimed SOL program (with a built-in 5% fee).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Unclaimed SOL",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"sideEffects": false,
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/unclaimed-sol/spl-burn-close-sdk"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/unclaimed-sol/spl-burn-close-sdk/issues"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js",
|
|
20
|
+
"require": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.cjs",
|
|
24
|
+
"module": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json && node ./scripts/build-cjs.js",
|
|
32
|
+
"prepublishOnly": "npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@solana/web3.js": "^1.98.4"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"typescript": "^5.6.3"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=18"
|
|
42
|
+
},
|
|
43
|
+
"keywords": [
|
|
44
|
+
"solana",
|
|
45
|
+
"spl-token",
|
|
46
|
+
"sdk",
|
|
47
|
+
"token-account",
|
|
48
|
+
"close-account",
|
|
49
|
+
"burn"
|
|
50
|
+
]
|
|
51
|
+
}
|