-
Notifications
You must be signed in to change notification settings - Fork 2
feat(cli): add top-level blesstoken-disable-mint command #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,119 @@ | ||||||||||
| const { Command, Argument } = require("commander"); | ||||||||||
| const { | ||||||||||
| getBlsContractClient, | ||||||||||
| getPath, | ||||||||||
| readKeypair, | ||||||||||
| bs58Message, | ||||||||||
| } = require("./utils"); | ||||||||||
| const { WALLET_PATH } = require("../lib/constants"); | ||||||||||
| const chalk = require("chalk"); | ||||||||||
| const { PublicKey } = require("@solana/web3.js"); | ||||||||||
| const BLESS_CONTRACT_STATE_SEED = "bless_contract_state"; | ||||||||||
|
|
||||||||||
| const blessTokenDisableMintCommand = new Command("blesstoken-disable-mint") | ||||||||||
| .alias("blesstoken_disable_mint") | ||||||||||
| .alias("disable-mint") | ||||||||||
| .alias("disable_mint") | ||||||||||
| .option( | ||||||||||
| "--cluster <cluster>", | ||||||||||
| "solana cluster: mainnet, testnet, devnet, localnet, <custom>", | ||||||||||
| ) | ||||||||||
| .option( | ||||||||||
| "--signer <signer>", | ||||||||||
| "the signer is the payer/admin of bless token meta: " + WALLET_PATH, | ||||||||||
| ) | ||||||||||
| .option( | ||||||||||
| "--programId <programId>", | ||||||||||
| "Program ID: Specify the program ID when working on devnet, testnet, or localnet; it will not work on mainnet.", | ||||||||||
| ) | ||||||||||
| .option( | ||||||||||
| "--squads <true/false>", | ||||||||||
| "squads: if squads true, use squads to signature, default is false.", | ||||||||||
|
||||||||||
| "squads: if squads true, use squads to signature, default is false.", | |
| "squads: if squads true, use squads to signature, default is false.", | |
| (value) => value === "true", | |
| false, |
Copilot
AI
Mar 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in error message: "invaild" should be "invalid".
| console.log(chalk.red("invaild mint parameter: " + e)); | |
| console.log(chalk.red("invalid mint parameter: " + e)); |
Copilot
AI
Mar 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blessStatePda is computed unconditionally but only used in the non-Squads path. Consider moving the PDA derivation into the else branch to avoid extra work and reduce confusion about which inputs are required for Squads mode.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,147 @@ | ||
| const { Command, Argument } = require("commander"); | ||
| const { getPath, readKeypair, getConnection, bs58Message } = require("./utils"); | ||
| const { WALLET_PATH } = require("../lib/constants"); | ||
| const Loader = require("./loader"); | ||
| const chalk = require("chalk"); | ||
| const { bs58 } = require("@coral-xyz/anchor/dist/cjs/utils/bytes"); | ||
| const { PublicKey, Transaction, TransactionInstruction, SystemProgram } = require("@solana/web3.js"); | ||
|
|
||
| const extendCommand = new Command("extend") | ||
| .option( | ||
| "--cluster <cluster>", | ||
| "solana cluster: mainnet, testnet, devnet, localnet, <custom>", | ||
| ) | ||
| .option("--payer <payer>", "the default payer: " + WALLET_PATH) | ||
| .option( | ||
| "--authority <authority>", | ||
| "the authority keypair file (default: " + WALLET_PATH + ")", | ||
| ) | ||
| .option( | ||
| "--squads <true/false>", | ||
| "squads: if squads true, create a tx message for squads signing, default is false.", | ||
| ) | ||
| .option( | ||
| "--admin <admin>", | ||
| "admin: payer pubkey when using squads mode.", | ||
| ) | ||
| .description("extend: extend an upgradeable program data account"); | ||
|
|
||
| const programId = new Argument( | ||
| "programId", | ||
| "programId: the program id (base58 value)", | ||
| ); | ||
| const additionalBytes = new Argument( | ||
| "additionalBytes", | ||
| "additionalBytes: extra bytes to add to the ProgramData account", | ||
| ); | ||
|
|
||
| extendCommand | ||
| .addArgument(programId) | ||
| .addArgument(additionalBytes) | ||
| .action(async (programId, additionalBytes, options) => { | ||
| options.payer = options.payer || getPath(WALLET_PATH); | ||
| options.authority = options.authority || getPath(WALLET_PATH); | ||
| options.cluster = options.cluster || "localnet"; | ||
| try { | ||
| const payerKeypair = readKeypair(options.payer); | ||
| const authorityKeypair = readKeypair(options.authority); | ||
| const programIdPK = new PublicKey(programId); | ||
| const additionalBytesNum = Number(additionalBytes); | ||
| if ( | ||
| !Number.isInteger(additionalBytesNum) || | ||
| additionalBytesNum <= 0 || | ||
| additionalBytesNum > 0xffffffff | ||
| ) { | ||
| throw new Error("additionalBytes must be an integer in [1, 4294967295]"); | ||
| } | ||
|
|
||
| const connection = getConnection(options.cluster); | ||
| const useSquads = | ||
| options.squads === true || | ||
| options.squads === "true" || | ||
| options.squads === 1 || | ||
| options.squads === "1"; | ||
| if (useSquads) { | ||
| const programAccount = await connection.getAccountInfo(programIdPK); | ||
| if (!programAccount) { | ||
| throw new Error(`Program account not found: ${programId}`); | ||
| } | ||
| const programDataAddress = new PublicKey( | ||
| programAccount.data.subarray(4, 36), | ||
| ); | ||
| const PROGRAMDATA_HEADER_SIZE = 45; | ||
| const programDataAccount = await connection.getAccountInfo( | ||
| programDataAddress, | ||
| ) | ||
| if (!programDataAccount) { | ||
| throw new Error( | ||
| `ProgramData account not found: ${programDataAddress.toBase58()}`, | ||
| ); | ||
| } | ||
|
|
||
| const data = Buffer.alloc(8); | ||
| data.writeUInt32LE(6, 0); // ExtendProgram discriminator | ||
| data.writeUInt32LE(Math.floor(additionalBytes * 1.05), 4); | ||
| const BPF_LOADER_UPGRADEABLE = new PublicKey( | ||
| "BPFLoaderUpgradeab1e11111111111111111111111", | ||
| ); | ||
| const admin = new PublicKey(options.admin); | ||
| const ix = new TransactionInstruction({ | ||
| programId: BPF_LOADER_UPGRADEABLE, | ||
| keys: [ | ||
| { pubkey: programDataAddress, isSigner: false, isWritable: true }, | ||
| { | ||
| pubkey: new PublicKey(programId), | ||
| isSigner: false, | ||
| isWritable: true, | ||
| }, | ||
| { | ||
| pubkey: SystemProgram.programId, | ||
| isSigner: false, | ||
| isWritable: false, | ||
| }, | ||
| { pubkey: admin, isSigner: true, isWritable: true }, | ||
| ], | ||
| data, | ||
| }); | ||
| const tx = new Transaction().add(ix); | ||
|
|
||
| tx.feePayer = admin; | ||
| tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; | ||
| const serialized = tx.serialize({ verifySignatures: false }); | ||
| console.log("Transaction:", { | ||
| b64: serialized.toString("base64"), | ||
| b58: bs58.encode(serialized), | ||
| }); | ||
| return | ||
| } | ||
|
|
||
| const extendIx = await Loader.extendProgramCheckedInstruction( | ||
| programIdPK, | ||
| authorityKeypair.publicKey, | ||
| payerKeypair.publicKey, | ||
| additionalBytesNum, | ||
| ); | ||
| const transaction = new Transaction(); | ||
| transaction.instructions = [extendIx]; | ||
|
|
||
| transaction.recentBlockhash = ( | ||
| await connection.getLatestBlockhash() | ||
| ).blockhash; | ||
| transaction.feePayer = payerKeypair.publicKey; | ||
| if ( | ||
| payerKeypair.publicKey.toBase58() === authorityKeypair.publicKey.toBase58() | ||
| ) { | ||
| transaction.sign(payerKeypair); | ||
| } else { | ||
| transaction.sign(payerKeypair, authorityKeypair); | ||
| } | ||
|
|
||
| const tx = await connection.sendRawTransaction(transaction.serialize()); | ||
| console.log(chalk.green("program extend success: " + tx)); | ||
| } catch (e) { | ||
| console.log(chalk.red("program extend fail: " + e)); | ||
| } | ||
| }); | ||
|
|
||
| module.exports = extendCommand; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The updated help text includes a typo: "disptch" should be "dispatch" (and consider aligning the CLI command description/help output accordingly so README and
--helpstay consistent).