diff --git a/programs/cardinal-token-manager/src/instructions/invalidate.rs b/programs/cardinal-token-manager/src/instructions/invalidate.rs index c9f066b52..40c10daba 100644 --- a/programs/cardinal-token-manager/src/instructions/invalidate.rs +++ b/programs/cardinal-token-manager/src/instructions/invalidate.rs @@ -55,13 +55,40 @@ pub struct InvalidateCtx<'info> { pub fn handler<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, 'remaining, 'info, InvalidateCtx<'info>>) -> Result<()> { let token_manager = &mut ctx.accounts.token_manager; - let remaining_accs = &mut ctx.remaining_accounts.iter(); + let remaining_accs = &mut ctx.remaining_accounts.iter().peekable(); // get PDA seeds to sign with let mint = token_manager.mint; let token_manager_seeds = &[TOKEN_MANAGER_SEED.as_bytes(), mint.as_ref(), &[token_manager.bump]]; let token_manager_signer = &[&token_manager_seeds[..]]; + if token_manager.kind != TokenManagerKind::Programmable as u8 { + // look at next account + if let Some(next_account) = remaining_accs.peek() { + match assert_derivation( + &mpl_token_metadata::id(), + &next_account.to_account_info(), + &[mpl_token_metadata::state::PREFIX.as_bytes(), mpl_token_metadata::id().as_ref(), mint.as_ref()], + ) { + // migrated pnft + Ok(_) => { + let mint_metadata_data = next_account.try_borrow_mut_data().expect("Failed to borrow data"); + let metadata = Metadata::deserialize(&mut mint_metadata_data.as_ref()).expect("Failed to deserialize metadata"); + match metadata.token_standard { + Some(TokenStandard::ProgrammableNonFungible) => { + // pop this account and update type + next_account_info(remaining_accs)?; + token_manager.kind = TokenManagerKind::Programmable as u8; + } + _ => return Err(error!(ErrorCode::InvalidTokenManagerKind)), + } + } + // regular edition + _ => {} + } + } + } + if token_manager.state == TokenManagerState::Claimed as u8 { match token_manager.kind { k if k == TokenManagerKind::Unmanaged as u8 => {} @@ -89,47 +116,27 @@ pub fn handler<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, } k if k == TokenManagerKind::Edition as u8 => { let edition_info = next_account_info(remaining_accs)?; - match assert_derivation( - &mpl_token_metadata::id(), - &edition_info.to_account_info(), - &[mpl_token_metadata::state::PREFIX.as_bytes(), mpl_token_metadata::id().as_ref(), ctx.accounts.mint.key().as_ref()], - ) { - // migrated pnft - Ok(_) => { - let mint_metadata_data = edition_info.try_borrow_mut_data().expect("Failed to borrow data"); - let metadata = Metadata::deserialize(&mut mint_metadata_data.as_ref()).expect("Failed to deserialize metadata"); - match metadata.token_standard { - Some(TokenStandard::ProgrammableNonFungible) => { - token_manager.kind = TokenManagerKind::Programmable as u8; - } - _ => return Err(error!(ErrorCode::InvalidTokenManagerKind)), - } - } - // regular edition - _ => { - let metadata_program = next_account_info(remaining_accs)?; - // edition will be validated by metadata_program - if metadata_program.key() != mpl_token_metadata::id() { - return Err(error!(ErrorCode::InvalidMetadataProgramId)); - } - invoke_signed( - &thaw_delegated_account( - *metadata_program.key, - token_manager.key(), - ctx.accounts.recipient_token_account.key(), - *edition_info.key, - ctx.accounts.mint.key(), - ), - &[ - token_manager.to_account_info(), - ctx.accounts.recipient_token_account.to_account_info(), - edition_info.to_account_info(), - ctx.accounts.mint.to_account_info(), - ], - &[token_manager_seeds], - )?; - } + let metadata_program = next_account_info(remaining_accs)?; + // edition will be validated by metadata_program + if metadata_program.key() != mpl_token_metadata::id() { + return Err(error!(ErrorCode::InvalidMetadataProgramId)); } + invoke_signed( + &thaw_delegated_account( + *metadata_program.key, + token_manager.key(), + ctx.accounts.recipient_token_account.key(), + *edition_info.key, + ctx.accounts.mint.key(), + ), + &[ + token_manager.to_account_info(), + ctx.accounts.recipient_token_account.to_account_info(), + edition_info.to_account_info(), + ctx.accounts.mint.to_account_info(), + ], + &[token_manager_seeds], + )?; } k if k == TokenManagerKind::Programmable as u8 => {} _ => return Err(error!(ErrorCode::InvalidTokenManagerKind)), diff --git a/programs/cardinal-token-manager/src/instructions/unissue.rs b/programs/cardinal-token-manager/src/instructions/unissue.rs index c8cc9c284..7b3252600 100644 --- a/programs/cardinal-token-manager/src/instructions/unissue.rs +++ b/programs/cardinal-token-manager/src/instructions/unissue.rs @@ -32,7 +32,7 @@ pub struct UnissueCtx<'info> { } pub fn handler<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, 'remaining, 'info, UnissueCtx<'info>>) -> Result<()> { - let remaining_accs = &mut ctx.remaining_accounts.iter(); + let remaining_accs = &mut ctx.remaining_accounts.iter().peekable(); let token_manager = &mut ctx.accounts.token_manager; // get PDA seeds to sign with @@ -40,26 +40,30 @@ pub fn handler<'key, 'accounts, 'remaining, 'info>(ctx: Context<'key, 'accounts, let token_manager_seeds = &[TOKEN_MANAGER_SEED.as_bytes(), mint.as_ref(), &[token_manager.bump]]; let token_manager_signer = &[&token_manager_seeds[..]]; - if token_manager.kind == TokenManagerKind::Edition as u8 { - let edition_info = next_account_info(remaining_accs)?; - match assert_derivation( - &mpl_token_metadata::id(), - &edition_info.to_account_info(), - &[mpl_token_metadata::state::PREFIX.as_bytes(), mpl_token_metadata::id().as_ref(), mint.as_ref()], - ) { - // migrated pnft - Ok(_) => { - let mint_metadata_data = edition_info.try_borrow_mut_data().expect("Failed to borrow data"); - let metadata = Metadata::deserialize(&mut mint_metadata_data.as_ref()).expect("Failed to deserialize metadata"); - match metadata.token_standard { - Some(TokenStandard::ProgrammableNonFungible) => { - token_manager.kind = TokenManagerKind::Programmable as u8; + if token_manager.kind != TokenManagerKind::Programmable as u8 { + // look at next account + if let Some(next_account) = remaining_accs.peek() { + match assert_derivation( + &mpl_token_metadata::id(), + &next_account.to_account_info(), + &[mpl_token_metadata::state::PREFIX.as_bytes(), mpl_token_metadata::id().as_ref(), mint.as_ref()], + ) { + // migrated pnft + Ok(_) => { + let mint_metadata_data = next_account.try_borrow_mut_data().expect("Failed to borrow data"); + let metadata = Metadata::deserialize(&mut mint_metadata_data.as_ref()).expect("Failed to deserialize metadata"); + match metadata.token_standard { + Some(TokenStandard::ProgrammableNonFungible) => { + // pop this account and update type + next_account_info(remaining_accs)?; + token_manager.kind = TokenManagerKind::Programmable as u8; + } + _ => return Err(error!(ErrorCode::InvalidTokenManagerKind)), } - _ => return Err(error!(ErrorCode::InvalidTokenManagerKind)), } + // regular edition + _ => {} } - // regular edition - _ => {} } } diff --git a/src/programs/tokenManager/utils.ts b/src/programs/tokenManager/utils.ts index 45dc33098..92cc42879 100644 --- a/src/programs/tokenManager/utils.ts +++ b/src/programs/tokenManager/utils.ts @@ -51,19 +51,17 @@ export const getRemainingAccountsForKind = ( tokenManagerKind === TokenManagerKind.Managed || tokenManagerKind === TokenManagerKind.Permissioned ) { - const mintManagerId = findMintManagerId(mintId); return [ { - pubkey: mintManagerId, + pubkey: findMintManagerId(mintId), isSigner: false, isWritable: true, }, ]; } else if (tokenManagerKind === TokenManagerKind.Edition) { - const editionId = findMintEditionId(mintId); return [ { - pubkey: editionId, + pubkey: findMintEditionId(mintId), isSigner: false, isWritable: false, }, @@ -85,7 +83,7 @@ export const getRemainingAccountsForUnissue = ( ): AccountMeta[] => { const remainingAccounts: AccountMeta[] = []; if ( - tokenManagerData.kind === TokenManagerKind.Edition && + tokenManagerData.kind !== TokenManagerKind.Programmable && metadata?.tokenStandard === TokenStandard.ProgrammableNonFungible ) { remainingAccounts.push({ @@ -169,22 +167,25 @@ export const withRemainingAccountsForInvalidate = async ( metadata: Metadata | null ): Promise => { const remainingAccounts: AccountMeta[] = []; + if ( + tokenManagerData.parsed.kind !== TokenManagerKind.Programmable && + metadata?.tokenStandard === TokenStandard.ProgrammableNonFungible + ) { + // update kind + tokenManagerData.parsed.kind = TokenManagerKind.Programmable; + remainingAccounts.push({ + pubkey: findMintMetadataId(mintId), + isSigner: false, + isWritable: false, + }); + } + if (tokenManagerData.parsed.state === TokenManagerState.Claimed) { - if ( - tokenManagerData.parsed.kind === TokenManagerKind.Edition && - metadata?.tokenStandard === TokenStandard.ProgrammableNonFungible - ) { - remainingAccounts.push({ - pubkey: findMintMetadataId(mintId), - isSigner: false, - isWritable: false, - }); - } else { - remainingAccounts.push( - ...getRemainingAccountsForKind(mintId, tokenManagerData.parsed.kind) - ); - } + remainingAccounts.push( + ...getRemainingAccountsForKind(mintId, tokenManagerData.parsed.kind) + ); } + if ( tokenManagerData.parsed.invalidationType === InvalidationType.Release && tokenManagerData.parsed.kind === TokenManagerKind.Programmable @@ -209,7 +210,6 @@ export const withRemainingAccountsForInvalidate = async ( ); remainingAccounts.push(...returnAccounts); } - return remainingAccounts; };