Skip to content

Instantly share code, notes, and snippets.

@myyellowshoe
Last active June 27, 2023 14:30
Show Gist options
  • Save myyellowshoe/f4df7963431a95fe208b5ee6dcf07373 to your computer and use it in GitHub Desktop.
Save myyellowshoe/f4df7963431a95fe208b5ee6dcf07373 to your computer and use it in GitHub Desktop.
Calculating NFT Print Edition Supply Manually
/*
Print editions are awesome but tricky, 2 big issues:
1. Printing in quick succession.
Multiple people click a button to print at the same time, same edition will try and get printed multiple times.
2. Burned nfts mess up on chain supply reference.
We can solve for number #2, and drastically reduce #1 window for any errors.
This is largely a port of the code here:
https://github.com/samuelvanderwaal/metaboss/blob/b662001ee929f5b34f3660c227136aab9697de27/src/mint.rs#L366
How this works:
1. A set of Edions have there own PDA called an EditionMarker, so we derive this.
2. Grab the account related to the PDA and deseralize the data.
3. Using findFirstZeroBit function find missing editions that are not printed.
4. Returns most up to date supply.
5. Were using confirmed status when grabbing the account to ensure this is speedy.
6. Pass this supply number to the print script.
Todo:
1. To really fix for printing in quick succession in the long term we'll need a queue system of some kind.
But this will work a long as multiple folks are not clicking the print button within 1-2 seconds, aka the time it takes to confirm a transaction on solana.
2. Need more testing with larger print editions > 250 to ensure the it iterate to the next EditionMarker
*/
// Get Mint Info
const masterEditionPublicKey = new PublicKey(masterMintAddress);
const nftToMint = (await metaplex.nfts().findByMint({
mintAddress: masterEditionPublicKey,
})) as Nft;
// Old way of getting supply
const supplyFromNFT = (nftToMint.edition as NftOriginalEdition).supply.toString()
// New way of calculating supply
// Get a recent blockhash to include in the transaction
const { blockhash } = await connection.getLatestBlockhash();
function findFirstZeroBit(arr, first_marker) {
// First edition marker starts at 1 so the first bit is zero and needs to be skipped.
for (let i = 0; i < arr.length; i++) {
const byte = arr[i];
if (byte !== 0xff) {
// There's at least one zero bit in this byte
for (let bit = 7; bit >= 0; bit--) {
if ((byte & (1 << bit)) === 0) {
if (first_marker && i === 0 && bit === 7) {
continue;
}
return [i, 7 - bit];
}
}
}
}
return null;
}
// Get current edition number
// Loop through edition marker accounts and look for any 0s in the ledger data.
// Use the first found 0 as the next edition number.
let calculatedEditionSupply = 1;
let edition = toBigNumber(calculatedEditionSupply);
while (true) {
const editionMarker = metaplex
.nfts()
.pdas()
.editionMarker({ mint: masterEditionPublicKey, edition });
// If the edition marker doesn't exist then the next edition is the first edition for that marker.
const account = await connection
.getAccountInfo(editionMarker, "confirmed")
.catch((err) => {
console.log(err);
return;
});
// Account doesn't exist
if (!account) {
break;
}
const [marker, markerNumber] = EditionMarker.deserialize(account.data);
const prettyMarker = marker.pretty();
const [index, bit] = findFirstZeroBit(
prettyMarker.ledger,
calculatedEditionSupply,
);
console.log(index, bit);
if (index !== null && bit !== null) {
calculatedEditionSupply = index * 8 + bit - 1;
break;
} else {
// Need more testing to verify large print collections
calculatedEditionSupply += 248;
}
}
// Create print transaction
await metaplex
.nfts()
.printNewEdition({
originalSupply: toBigNumber(calculatedEditionSupply),
newOwner: payerPublicKey,
originalMint: new PublicKey(masterMintAddress),
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment