developer-content
developer-content copied to clipboard
Combine Immutable Owner extension and Non-transferable extension together fail.
Following guides, https://solana.com/developers/guides/token-extensions/non-transferable#build-instructions and https://solana.com/developers/guides/token-extensions/immutable-owner#conclusion
They can work separately well. However, if I put them together, immutable-owner always show Error processing Instruction 2: invalid account data for instruction. My code is based on guide.
import * as anchor from "@coral-xyz/anchor";
import {Program, web3} from "@coral-xyz/anchor";
import { ZkmeSol } from "../target/types/zkme_sol";
import {it} from "mocha";
import {Connection, Keypair, sendAndConfirmTransaction, SystemProgram, Transaction} from "@solana/web3.js";
import {
AuthorityType,
createInitializeAccountInstruction,
createInitializeImmutableOwnerInstruction,
createInitializeMintInstruction,
createInitializeNonTransferableMintInstruction,
createMint,
ExtensionType,
getAccountLen, getMintLen, setAuthority,
TOKEN_2022_PROGRAM_ID
} from "@solana/spl-token";
import {min} from "bn.js";
//anchor test
describe("zkme_sol", () => {
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.ZkmeSol as Program<ZkmeSol>;
const zkmeSeed = anchor.utils.bytes.utf8.encode("zkme_conf");
let adminPubKey;
before( async () => {
[adminPubKey] = await anchor.web3.PublicKey.findProgramAddress(
[zkmeSeed, anchor.AnchorProvider.env().wallet.publicKey.toBytes()],
program.programId
);
});
it( "Is createAdmin", async () => {
const payer = web3.Keypair.generate();
// Connection to devnet cluster
const connection = new Connection('http://127.0.0.1:8899', 'confirmed');
// Transaction signature returned from sent transaction
let transactionSignature: string;
const airdropSignature = await connection.requestAirdrop(
payer.publicKey,
web3.LAMPORTS_PER_SOL, // 1 SOL
);
await connection.confirmTransaction(airdropSignature);
const balanceA = await connection.getBalance(payer.publicKey)
console.log("airdropSignature success", balanceA);
// Authority that can mint new tokens
const mintAuthority = payer.publicKey;
// Decimals for Mint Account
const decimals = 2;
const mintLen = getMintLen([ExtensionType.NonTransferable]);
// Minimum lamports required for Mint Account
let lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
const mintKeypair = Keypair.generate();
// Address for Mint Account
const mint = mintKeypair.publicKey;
const createAccountInstructionA = SystemProgram.createAccount({
fromPubkey: payer.publicKey, // Account that will transfer lamports to created account
newAccountPubkey: mint, // Address of the account to create
space: mintLen, // Amount of bytes to allocate to the created account
lamports, // Amount of lamports transferred to created account
programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});
console.log("createAccountInstruction",createAccountInstructionA)
// Instruction to initialize the NonTransferable Extension
const initializeNonTransferableMintInstructionA =
createInitializeNonTransferableMintInstruction(
mint, // Mint Account address
TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
);
console.log("initializeNonTransferableMintInstruction",initializeNonTransferableMintInstructionA)
// Instruction to initialize Mint Account data
const initializeMintInstructionA = createInitializeMintInstruction(
mint, // Mint Account Address
decimals, // Decimals of Mint
anchor.AnchorProvider.env().publicKey, // Designated Mint Authority
null, // Optional Freeze Authority
TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
);
console.log("initializeMintInstruction",initializeMintInstructionA)
// Add instructions to new transaction
const transactionA = new Transaction().add(
createAccountInstructionA,
initializeNonTransferableMintInstructionA,
initializeMintInstructionA,
);
console.log("mint:",mint)
// Send transaction
transactionSignature = await sendAndConfirmTransaction(
connection,
transactionA,
[payer, mintKeypair], // Signers
);
console.log(
"transactionSignature",transactionSignature
)
// Random keypair to use as owner of Token Account
const tokenAccountKeypair = Keypair.generate();
// Address for Token Account
const tokenAccount = tokenAccountKeypair.publicKey;
// Size of Token Account with extension
const accountLen = getAccountLen([ExtensionType.ImmutableOwner]);
// Minimum lamports required for Token Account
lamports = await connection.getMinimumBalanceForRentExemption(accountLen);
// Instruction to invoke System Program to create new account
const createAccountInstruction = SystemProgram.createAccount({
fromPubkey: payer.publicKey, // Account that will transfer lamports to created account
newAccountPubkey: tokenAccount, // Address of the account to create
space: accountLen, // Amount of bytes to allocate to the created account
lamports, // Amount of lamports transferred to created account
programId: TOKEN_2022_PROGRAM_ID, // Program assigned as owner of created account
});
// Instruction to initialize the ImmutableOwner Extension
const initializeImmutableOwnerInstruction =
createInitializeImmutableOwnerInstruction(
tokenAccount, // Token Account address
TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
);
// Instruction to initialize Token Account data
const initializeAccountInstruction = createInitializeAccountInstruction(
tokenAccount, // Token Account Address
mint, // Mint Account
payer.publicKey, // Token Account Owner
TOKEN_2022_PROGRAM_ID, // Token Extension Program ID
);
console.log("mint:",mint)
// Add instructions to new transaction
const transaction = new Transaction().add(
createAccountInstruction,
// initializeImmutableOwnerInstruction,
initializeAccountInstruction,
);
// Send transaction
transactionSignature = await sendAndConfirmTransaction(
connection,
transaction,
[payer, tokenAccountKeypair], // Signers
);
console.log(
"\nCreate Token Account:",
`https://solana.fm/tx/${transactionSignature}?cluster=devnet-solana`,
);
})
});