From 36dfe2c3fd2e1cb3d9aadc5dfde396389e2768a7 Mon Sep 17 00:00:00 2001 From: Michael Danenberg <56533526+danenbm@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:28:21 -0700 Subject: [PATCH] Add SDK helper to choose compression programs (#117) * Add SDK helper to choose compression programs * Change variable name --- clients/js/src/getCompressionPrograms.ts | 49 ++++++++++++++++++++++++ clients/js/src/index.ts | 1 + clients/js/test/createTree.test.ts | 21 +++++----- clients/js/test/transfer.test.ts | 39 ++++++++++--------- 4 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 clients/js/src/getCompressionPrograms.ts diff --git a/clients/js/src/getCompressionPrograms.ts b/clients/js/src/getCompressionPrograms.ts new file mode 100644 index 00000000..450366fe --- /dev/null +++ b/clients/js/src/getCompressionPrograms.ts @@ -0,0 +1,49 @@ +import { Context, PublicKey } from '@metaplex-foundation/umi'; +import { + SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + SPL_NOOP_PROGRAM_ID, +} from './generated'; + +export const MPL_ACCOUNT_COMPRESSION_PROGRAM_ID = + 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' as PublicKey<'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW'>; + +export const MPL_NOOP_PROGRAM_ID = + 'mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3' as PublicKey<'mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'>; + +// Constants for known genesis blockhashes on Solana. +const SOLANA_MAINNET_GENESIS_HASH = + '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d'; +const SOLANA_DEVNET_GENESIS_HASH = + 'EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG'; +const SOLANA_TESTNET_GENESIS_HASH = + '4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY'; + +export type CompressionPrograms = { + logWrapper: PublicKey; + compressionProgram: PublicKey; +}; + +export async function getCompressionPrograms( + context: Pick +): Promise { + const genesisHash = await context.rpc.call('getGenesisHash'); + + // Determine if the genesis hash matches known clusters. + const isSolanaCluster = [ + SOLANA_MAINNET_GENESIS_HASH, + SOLANA_DEVNET_GENESIS_HASH, + SOLANA_TESTNET_GENESIS_HASH, + ].includes(genesisHash); + + // Return appropriate program IDs based on the cluster. + if (isSolanaCluster) { + return { + logWrapper: SPL_NOOP_PROGRAM_ID, + compressionProgram: SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + }; + } + return { + logWrapper: MPL_NOOP_PROGRAM_ID, + compressionProgram: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + }; +} diff --git a/clients/js/src/index.ts b/clients/js/src/index.ts index 3e7b0435..7828d437 100644 --- a/clients/js/src/index.ts +++ b/clients/js/src/index.ts @@ -7,3 +7,4 @@ export * from './hooked'; export * from './leafAssetId'; export * from './merkle'; export * from './plugin'; +export * from './getCompressionPrograms'; diff --git a/clients/js/test/createTree.test.ts b/clients/js/test/createTree.test.ts index 7eaccc56..9845eb34 100644 --- a/clients/js/test/createTree.test.ts +++ b/clients/js/test/createTree.test.ts @@ -17,6 +17,9 @@ import { safeFetchTreeConfigFromSeeds, getMerkleTreeSize, SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + getCompressionPrograms, + MPL_NOOP_PROGRAM_ID, + MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, } from '../src'; import { createUmi } from './_setup'; @@ -133,15 +136,17 @@ test('it can create a Bubblegum tree using mpl-account-compression and mpl-noop' const umi = await createUmi(); const merkleTree = generateSigner(umi); + // For these tests, make sure `getCompressionPrograms` doesn't return spl programs. + const { logWrapper, compressionProgram } = await getCompressionPrograms(umi); + t.is(logWrapper, MPL_NOOP_PROGRAM_ID); + t.is(compressionProgram, MPL_ACCOUNT_COMPRESSION_PROGRAM_ID); + // When we create a tree at this address. const builder = await createTree(umi, { merkleTree, maxDepth: 14, maxBufferSize: 64, - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + ...(await getCompressionPrograms(umi)), }); await builder.sendAndConfirm(umi); @@ -200,9 +205,7 @@ test('it cannot create a Bubblegum tree using invalid logWrapper with mpl-accoun maxDepth: 14, maxBufferSize: 64, logWrapper: generateSigner(umi).publicKey, - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + compressionProgram: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, }); const promise = builder.sendAndConfirm(umi); @@ -262,9 +265,9 @@ test('it cannot create a Bubblegum tree when compression program does not match merkleTree, maxDepth: 14, maxBufferSize: 64, - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), + logWrapper: MPL_NOOP_PROGRAM_ID, compressionProgram: generateSigner(umi).publicKey, - merkleTreeOwner: publicKey('mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW'), + merkleTreeOwner: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, }); const promise = builder.sendAndConfirm(umi); diff --git a/clients/js/test/transfer.test.ts b/clients/js/test/transfer.test.ts index 4f8366c2..1666ff5a 100644 --- a/clients/js/test/transfer.test.ts +++ b/clients/js/test/transfer.test.ts @@ -12,6 +12,9 @@ import { hashMetadataData, transfer, verifyLeaf, + getCompressionPrograms, + MPL_NOOP_PROGRAM_ID, + MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, } from '../src'; import { createTree, createUmi, mint } from './_setup'; import { @@ -58,21 +61,21 @@ test('it can transfer a compressed NFT', async (t) => { test('it can transfer a compressed NFT using mpl-account-compression and mpl-noop', async (t) => { // Given a tree with a minted NFT owned by leafOwnerA. const umi = await createUmi(); + + // For these tests, make sure `getCompressionPrograms` doesn't return spl programs. + const { logWrapper, compressionProgram } = await getCompressionPrograms(umi); + t.is(logWrapper, MPL_NOOP_PROGRAM_ID); + t.is(compressionProgram, MPL_ACCOUNT_COMPRESSION_PROGRAM_ID); + const merkleTree = await createTree(umi, { - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + ...(await getCompressionPrograms(umi)), }); let merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); const leafOwnerA = generateSigner(umi); const { metadata, leafIndex } = await mint(umi, { merkleTree, leafOwner: leafOwnerA.publicKey, - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + ...(await getCompressionPrograms(umi)), }); // When leafOwnerA transfers the NFT to leafOwnerB. @@ -87,10 +90,7 @@ test('it can transfer a compressed NFT using mpl-account-compression and mpl-noo nonce: leafIndex, index: leafIndex, proof: [], - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + ...(await getCompressionPrograms(umi)), }).sendAndConfirm(umi); // Then the leaf was updated in the merkle tree. @@ -107,6 +107,12 @@ test('it can transfer a compressed NFT using mpl-account-compression and mpl-noo test('it cannot transfer a compressed NFT owned by spl-account-compression using mpl programs', async (t) => { // Given a tree with a minted NFT owned by leafOwnerA. const umi = await createUmi(); + + // For these tests, make sure `getCompressionPrograms` doesn't return spl programs. + const { logWrapper, compressionProgram } = await getCompressionPrograms(umi); + t.is(logWrapper, MPL_NOOP_PROGRAM_ID); + t.is(compressionProgram, MPL_ACCOUNT_COMPRESSION_PROGRAM_ID); + const merkleTree = await createTree(umi); let merkleTreeAccount = await fetchMerkleTree(umi, merkleTree); const leafOwnerA = generateSigner(umi); @@ -127,10 +133,7 @@ test('it cannot transfer a compressed NFT owned by spl-account-compression using nonce: leafIndex, index: leafIndex, proof: [], - logWrapper: publicKey('mnoopTCrg4p8ry25e4bcWA9XZjbNjMTfgYVGGEdRsf3'), - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + ...(await getCompressionPrograms(umi)), }).sendAndConfirm(umi); // Then we expect a program error. @@ -170,9 +173,7 @@ test('it cannot transfer a compressed NFT owned by spl-account-compression using nonce: leafIndex, index: leafIndex, proof: [], - compressionProgram: publicKey( - 'mcmt6YrQEMKw8Mw43FmpRLmf7BqRnFMKmAcbxE3xkAW' - ), + compressionProgram: MPL_ACCOUNT_COMPRESSION_PROGRAM_ID, }).sendAndConfirm(umi); // Then we expect a program error.