@@ -12,10 +12,11 @@ import {
1212import {
1313 SPL_ACCOUNT_COMPRESSION_PROGRAM_ID ,
1414 SPL_NOOP_PROGRAM_ID ,
15- changeLogEventV1Beet ,
15+ deserializeChangeLogEventV1 ,
1616} from '@solana/spl-account-compression' ;
1717import { PublicKey } from '@solana/web3.js' ;
1818import { BN } from 'bn.js' ;
19+ import base58 from 'bs58' ;
1920import { SendAndConfirmTransactionResponse } from '../../rpcModule' ;
2021import { assertNft , Nft } from '../models' ;
2122import { Option , TransactionBuilder , TransactionBuilderOptions } from '@/utils' ;
@@ -194,11 +195,20 @@ export type CreateCompressedNftOutput = {
194195 /** The blockchain response from sending and confirming the transaction. */
195196 response : SendAndConfirmTransactionResponse ;
196197
197- /** The newly created SFT and, potentially, its associated token. */
198+ /** The newly created NFT and, potentially, its associated token. */
198199 nft : Nft ;
199200
200- /** The asset id of the leaf . */
201+ /** The mint address is the compressed NFT's assetId . */
201202 mintAddress : PublicKey ;
203+
204+ /** The metadata address is the compressed NFT's assetId. */
205+ metadataAddress : PublicKey ;
206+
207+ /** The master edition address is the compressed NFT's assetId. */
208+ masterEditionAddress : PublicKey ;
209+
210+ /** The token address is the compressed NFT's assetId. */
211+ tokenAddress : PublicKey ;
202212} ;
203213
204214/**
@@ -226,29 +236,64 @@ export const createCompressedNftOperationHandler: OperationHandler<CreateCompres
226236 const output = await builder . sendAndConfirm ( metaplex , confirmOptions ) ;
227237 scope . throwIfCanceled ( ) ;
228238
229- const {
230- response : { signature } ,
231- } = output ;
232- const txInfo = await metaplex . connection . getTransaction ( signature , {
233- maxSupportedTransactionVersion : 0 ,
239+ const txInfo = await metaplex . connection . getTransaction (
240+ output . response . signature ,
241+ {
242+ maxSupportedTransactionVersion : 0 ,
243+ }
244+ ) ;
245+ scope . throwIfCanceled ( ) ;
246+
247+ // find the index of the bubblegum instruction
248+ const relevantIndex =
249+ txInfo ! . transaction . message . compiledInstructions . findIndex (
250+ ( instruction ) => {
251+ return (
252+ txInfo ?. transaction . message . staticAccountKeys [
253+ instruction . programIdIndex
254+ ] . toBase58 ( ) === 'BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY'
255+ ) ;
256+ }
257+ ) ;
258+
259+ // locate the no-op inner instructions called via cpi from bubblegum
260+ const relevantInnerIxs = txInfo ! . meta ?. innerInstructions ?. [
261+ relevantIndex
262+ ] . instructions . filter ( ( instruction ) => {
263+ return (
264+ txInfo ?. transaction . message . staticAccountKeys [
265+ instruction . programIdIndex
266+ ] . toBase58 ( ) === 'noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV'
267+ ) ;
234268 } ) ;
235- const relevantIx = txInfo ! . transaction . message . compiledInstructions . find (
236- ( instruction ) => {
237- return (
238- txInfo ! . transaction . message . staticAccountKeys [
239- instruction . programIdIndex
240- ] . toBase58 ( ) === 'noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV'
269+
270+ // when no valid noop instructions are found, throw an error
271+ if ( ! relevantInnerIxs || relevantInnerIxs . length == 0 )
272+ throw Error ( 'Unable to locate valid noop instructions' ) ;
273+
274+ // locate the asset index by attempting to locate and parse the correct `relevantInnerIx`
275+ let assetIndex : number | undefined = undefined ;
276+ // note: the `assetIndex` is expected to be at position `1`, and normally expect only 2 `relevantInnerIx`
277+ for ( let i = relevantInnerIxs . length - 1 ; i > 0 ; i -- ) {
278+ try {
279+ const changeLogEvent = deserializeChangeLogEventV1 (
280+ Buffer . from ( base58 . decode ( relevantInnerIxs [ i ] ?. data ! ) )
241281 ) ;
282+
283+ // extract a successful changelog index
284+ assetIndex = changeLogEvent ?. index ;
285+ } catch ( __ ) {
286+ // do nothing, invalid data is handled just after the for loop
242287 }
243- ) ;
288+ }
244289
245- const [ changeLog ] = changeLogEventV1Beet . deserialize (
246- Buffer . from ( relevantIx ! . data )
247- ) ;
290+ // when no `assetIndex` was found, throw an error
291+ if ( typeof assetIndex == 'undefined' )
292+ throw Error ( 'Unable to locate the newly minted assetId ' ) ;
248293
249294 const assetId = await getLeafAssetId (
250295 operation . input . tree ,
251- new BN ( changeLog . index )
296+ new BN ( assetIndex )
252297 ) ;
253298
254299 const nft = await metaplex . nfts ( ) . findByAssetId (
@@ -260,7 +305,19 @@ export const createCompressedNftOperationHandler: OperationHandler<CreateCompres
260305 scope . throwIfCanceled ( ) ;
261306
262307 assertNft ( nft ) ;
263- return { ...output , nft } ;
308+
309+ return {
310+ ...output ,
311+ nft,
312+ /**
313+ * the assetId is impossible to know before the compressed nft is minted
314+ * all these addresses are derived from, or are, the `assetId`
315+ */
316+ mintAddress : assetId ,
317+ tokenAddress : assetId ,
318+ metadataAddress : nft . metadataAddress ,
319+ masterEditionAddress : nft . edition . address ,
320+ } ;
264321 } ,
265322 } ;
266323
@@ -316,7 +373,7 @@ export type CreateCompressedNftBuilderContext = Omit<
316373> ;
317374
318375/**
319- * Creates a new SFT .
376+ * Creates a new compressed NFT .
320377 *
321378 * ```ts
322379 * const transactionBuilder = await metaplex
0 commit comments