Reputation: 345
I'm trying to mint NFTs using Metaplex's Candy Machine v3, but encountering a BorshIoError during the mint transaction. The error occurs despite following the official documentation.
mint failed: SendTransactionError: Simulation failed.
Message: Transaction simulation failed: Error processing Instruction 2: Failed to serialize or deserialize account data: Unknown.
Logs:
[
"Program log: Instruction: MintV2",
"Program 11111111111111111111111111111111 invoke [2]",
"Program 11111111111111111111111111111111 success",
"Program CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR invoke [2]",
"Program log: Instruction: MintV2",
"Program log: ProgramError occurred. Error Code: BorshIoError(\"Unexpected variant index: 1\"). Error Number: 64424509440. Error Message: IO Error: Unexpected variant index: 1.",
"Program CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR consumed 35507 of 748405 compute units",
"Program CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR failed: Failed to serialize or deserialize account data: Unknown",
"Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g consumed 86802 of 799700 compute units",
"Program Guard1JwRhJkVH6XZhzoYxeBVQe872VH6QggF4BWmS9g failed: Failed to serialize or deserialize account data: Unknown"
# basic umi setup
const umi = createUmi(
NFT_CONFIG.RPC_URL || 'https://api.devnet.solana.com' #test on devnet
)
.use(mplCandyMachine())
.use(mplTokenMetadata());
// set deploy wallet
const DEPLOY_WALLET_PATH = path.join(__dirname, 'deploy-wallet.json');
const secretKey = new Uint8Array(
JSON.parse(fs.readFileSync(DEPLOY_WALLET_PATH, 'utf-8'))
);
const keypair = umi.eddsa.createKeypairFromSecretKey(secretKey);
const signer = createSignerFromKeypair(umi, keypair);
umi.use(keypairIdentity(signer));
console.log('Using deploy wallet:', signer.publicKey.toString());
console.log('Creating Collection NFT...');
const collectionMint = generateSigner(umi);
const collectionUpdateAuthority = signer;
console.log('Collection Mint address:', collectionMint.publicKey.toString());
console.log('Collection Update Authority:', umi.identity.publicKey.toString());
await createNft(umi, {
mint: collectionMint,
authority: umi.identity,
name: "my-nft-name",
uri: "my-url-to-collection-json",
sellerFeeBasisPoints: percentAmount(5,2),
isCollection: true,
}).sendAndConfirm(umi);
console.log('Waiting for Collection NFT to be created...');
await new Promise(resolve => setTimeout(resolve, 5000));
console.log('Creating Candy Machine...');
const candyMachine = generateSigner(umi);
console.log('Candy Machine address:', candyMachine.publicKey.toString());
const tx = await create(umi, {
candyMachine,
collectionMint: collectionMint.publicKey,
collectionUpdateAuthority: umi.identity,
tokenStandard: TokenStandard.NonFungible,
sellerFeeBasisPoints: percentAmount(5, 2),
itemsAvailable: BigInt(500),
creators: [
{
address: publicKey(TREASURY_ADDRESS),
verified: true,
percentageShare: 100
}
],
configLineSettings: some({
prefixName: "",
nameLength: 32,
prefixUri: "",
uriLength: 200,
isSequential: true
}),
guards: {
startDate: some({
date: dateTime("2025-02-01T00:00:00.000Z")
}),
endDate: some({
date: dateTime("2025-04-01T00:00:00.000Z")
}),
solPayment: some({
lamports: sol(1),
destination: publicKey(TREASURY_ADDRESS),
})
}
});
async mint(candyMachineAddress: string) {
try {
const candyMachine = await fetchCandyMachine(
this.umi,
publicKey(candyMachineAddress)
);
const nftMint = generateSigner(this.umi);
const tx = await transactionBuilder()
.add(setComputeUnitLimit(this.umi, { units: 800_000 }))
.add(
mintV2(this.umi, {
candyMachine: candyMachine.publicKey,
nftMint,
collectionMint: candyMachine.collectionMint,
collectionUpdateAuthority: candyMachine.authority,
mintArgs: {
solPayment: some({
destination: publicKey(TREASURY_ADDRESS)
}),
}
})
);
return await tx.sendAndConfirm(this.umi);
} catch (error) {
console.error('Mint failed:', error);
throw error;
}
}
{
"dependencies": {
"@metaplex-foundation/mpl-candy-machine": "^6.1.0",
"@metaplex-foundation/mpl-token-metadata": "^3.4.0",
"@metaplex-foundation/umi": "^1.0.0",
"@metaplex-foundation/umi-bundle-defaults": "^1.0.0",
"@metaplex-foundation/umi-signer-wallet-adapters": "^1.0.0"
}
}
What could be causing this BorshIoError during minting, and how can I resolve it? The error suggests a serialization issue, but I'm following the official documentation structure.Don't know what's wrong, I think this is nothing to do with items insert function since I can verify all items were uploaded successfully through Solana Explorer. If you need any more code snippets from these files, please comment below:
Potentially relevant files:
As requested, here are the additional code snippets that might be relevant to the issue:
import { PinataSDK } from "pinata-web3";
import fs from 'fs';
import path from 'path';
import { config } from 'dotenv';
// set my environment
config({ path: path.resolve(process.cwd(), '.env.local') });
const PINATA_JWT = process.env.PINATA_JWT;
const GATEWAY_URL = process.env.NEXT_PUBLIC_GATEWAY_URL;
const IMAGES_DIR = path.join(__dirname, 'assets/images');
const METADATA_DIR = path.join(__dirname, 'assets');
// create Pinata instance
const pinata = new PinataSDK({
pinataJwt: PINATA_JWT!,
pinataGateway: GATEWAY_URL
});
async function uploadToStorage() {
try {
if (!PINATA_JWT) {
throw new Error('PINATA_JWT not found in .env.local');
}
//
const imageFiles = fs.readdirSync(IMAGES_DIR)
.filter(file => file.startsWith('nft-') && (file.endsWith('.jpg') || file.endsWith('.jpeg')))
.sort((a, b) => {
const numA = parseInt(a.replace('nft-', '').replace(/\.(jpg|jpeg)$/, ''));
const numB = parseInt(b.replace('nft-', '').replace(/\.(jpg|jpeg)$/, ''));
return numA - numB;
});
console.log(`Found ${imageFiles.length} images to upload`);
const uploadResults = [];
const batchSize = 3;
for (let i = 0; i < imageFiles.length; i += batchSize) {
const batch = imageFiles.slice(i, Math.min(i + batchSize, imageFiles.length));
const promises = batch.map(async (imageFile) => {
console.log(`Processing ${imageFile}...`);
// get index
const index = parseInt(imageFile.replace('nft-', '').replace(/\.(jpg|jpeg)$/, '')) - 1;
// get json files respectively
const jsonPath = path.join(METADATA_DIR, `${index}.json`);
const metadata = JSON.parse(fs.readFileSync(jsonPath, 'utf-8'));
// upload file
const imageData = await fs.promises.readFile(path.join(IMAGES_DIR, imageFile));
const imageFileObj = new File(
[imageData],
imageFile,
{ type: 'image/jpg' }
);
const imageUpload = await pinata.upload.file(imageFileObj);
const imageUrl = await pinata.gateways.convert(imageUpload.IpfsHash);
// update urls in metadata
metadata.image = imageUrl;
metadata.properties.files[0].uri = imageUrl;
// upload updated metadate
const metadataFileObj = new File(
[JSON.stringify(metadata, null, 2)],
`${index}.json`,
{ type: 'application/json' }
);
const metadataUpload = await pinata.upload.file(metadataFileObj);
const metadataUrl = await pinata.gateways.convert(metadataUpload.IpfsHash);
// save local updated metadata JSON
fs.writeFileSync(jsonPath, JSON.stringify(metadata, null, 2));
return {
index,
imageUrl,
metadataUrl,
metadata
};
});
// wait for current batch to complete
const results = await Promise.all(promises);
uploadResults.push(...results);
console.log(`Completed batch ${i / batchSize + 1}`);
}
// save upload results
const resultsPath = path.join(__dirname, 'upload-results.json');
fs.writeFileSync(resultsPath, JSON.stringify(uploadResults, null, 2));
console.log('\nAll files uploaded successfully!');
console.log(`Results saved to: ${resultsPath}`);
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
}
import {
createUmi,
keypairIdentity,
publicKey,
createSignerFromKeypair
} from "@metaplex-foundation/umi";
import {
mplCandyMachine,
addConfigLines,
fetchCandyMachine
} from "@metaplex-foundation/mpl-candy-machine";
import { createUmi as baseCreateUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { config } from 'dotenv';
import * as fs from 'fs';
import * as path from 'path';
config({ path: path.resolve(process.cwd(), '.env.local') });
interface UploadResult {
index: number;
imageUrl: string;
metadataUrl: string;
metadata: {
name: string;
[key: string]: any;
}
}
async function uploadItems() {
try {
const umi = baseCreateUmi(
process.env.NEXT_PUBLIC_SOLANA_RPC_URL || 'https://api.devnet.solana.com'
).use(mplCandyMachine());
const DEPLOY_WALLET_PATH = path.join(__dirname, 'deploy-wallet.json');
const secretKey = new Uint8Array(
JSON.parse(fs.readFileSync(DEPLOY_WALLET_PATH, 'utf-8'))
);
const keypair = umi.eddsa.createKeypairFromSecretKey(secretKey);
const signer = createSignerFromKeypair(umi, keypair);
umi.use(keypairIdentity(signer));
// get candy machine address that is saved by "deploy candy machine script
const candyMachineAddress = process.env.NEXT_PUBLIC_CANDY_MACHINE_ADDRESS;
if (!candyMachineAddress) {
throw new Error('Candy Machine address not found in .env.local');
}
// get Candy Machine instance
const candyMachine = await fetchCandyMachine(umi, publicKey(candyMachineAddress));
// read all JSON files in assets dir
const ASSETS_DIR = path.join(__dirname, 'assets');
const files = fs.readdirSync(ASSETS_DIR)
.filter(file => {
// exclude collection.json and collection.jpg
return file.endsWith('.json') &&
!file.startsWith('collection') &&
!isNaN(parseInt(file)) // make sure index is number
})
.sort((a, b) => parseInt(a) - parseInt(b));
// get result from upload-storage scripts, which give me real uri of metadata json files
const configLines = await Promise.all(
files.map(async (file) => {
const uploadResults = JSON.parse(
fs.readFileSync(path.join(__dirname, 'upload-results.json'), 'utf-8')
) as UploadResult[];
const index = parseInt(file);
const result = uploadResults.find((r: UploadResult) => r.index === index);
if (!result) {
throw new Error(`No upload result found for index ${index}`);
}
// nft name
const name = `xxxx #${index + 1}`;
// nft url
const uri = result.metadataUrl;
return { name, uri };
})
);
// batch
const batchSize = 5;
for (let i = 0; i < configLines.length; i += batchSize) {
const batch = configLines.slice(i, Math.min(i + batchSize, configLines.length));
console.log(`Uploading batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(configLines.length / batchSize)}`);
// add log
console.log('AddConfigLines content:', {
candyMachine: candyMachine.publicKey.toString(),
index: i,
configLines: batch.map(line => ({
name: line.name,
uri: line.uri,
fullUri: line.uri
}))
});
try {
await addConfigLines(umi, {
candyMachine: candyMachine.publicKey,
index: i,
configLines: batch
}).sendAndConfirm(umi);
console.log(`Uploaded items ${i + 1} to ${i + batch.length}`);
} catch (error) {
console.error(`Failed to upload batch ${Math.floor(i / batchSize) + 1}:`, error);
throw error;
}
}
console.log('All items uploaded successfully!');
} catch (error) {
console.error('Failed to upload items:', error);
throw error;
}
}
Upvotes: 0
Views: 24