From 3469dd99d903de69a8a133d8868bc3f1ce74e940 Mon Sep 17 00:00:00 2001 From: blockiosaurus <90809591+blockiosaurus@users.noreply.github.com> Date: Sun, 22 Dec 2024 18:44:54 -0500 Subject: [PATCH] Fixing self-revoke (#157) * Adding resizing of metadata and edition accounts. * Integrating close of ownerless accounts. * Adding auth to fungibles. * Updating based on first round of feedback. * Minor tweaks and adding more thorough tests. * Finishing resize tests. * Fix formatting. * Fixing bad test function. * Minor fixes. * Adding ignores. * TS Ignore * Ignoring DeadlineExceeded tests. * Updating based on audit. * Update clients. * Add couple last tests * Fixing self revoke for metadata delegates. --------- Co-authored-by: Michael Danenberg <56533526+danenbm@users.noreply.github.com> --- clients/js/test/close/fungible.test.ts | 24 +++++----- clients/js/test/close/nonFungible.test.ts | 16 +++---- clients/js/test/revokeCollectionV1.test.ts | 38 +++++++++++++++ .../program/src/processor/delegate/revoke.rs | 16 +++---- .../program/tests/utils/metadata.rs | 48 +++++++++++++++++++ 5 files changed, 114 insertions(+), 28 deletions(-) diff --git a/clients/js/test/close/fungible.test.ts b/clients/js/test/close/fungible.test.ts index 77a42417..c7d66d6c 100644 --- a/clients/js/test/close/fungible.test.ts +++ b/clients/js/test/close/fungible.test.ts @@ -77,10 +77,10 @@ test.skip('it can close ownerless metadata for a fungible with zero supply and n t.deepEqual(await umi.rpc.getAccount(asset.metadata.publicKey), < MaybeRpcAccount - >{ - publicKey: asset.metadata.publicKey, - exists: false, - }); + >{ + publicKey: asset.metadata.publicKey, + exists: false, + }); t.deepEqual(await umi.rpc.getBalance(asset.metadata.publicKey), lamports(0)); const lamportsAfter = await umi.rpc.getBalance(closeDestination); @@ -139,10 +139,10 @@ test.skip('it can close ownerless metadata for a fungible with zero supply and m t.deepEqual(await umi.rpc.getAccount(asset.metadata.publicKey), < MaybeRpcAccount - >{ - publicKey: asset.metadata.publicKey, - exists: false, - }); + >{ + publicKey: asset.metadata.publicKey, + exists: false, + }); t.deepEqual(await umi.rpc.getBalance(asset.metadata.publicKey), lamports(0)); const lamportsAfter = await umi.rpc.getBalance(closeDestination); @@ -201,10 +201,10 @@ test.skip('it can close ownerless metadata for a fungible asset with zero supply t.deepEqual(await umi.rpc.getAccount(asset.metadata.publicKey), < MaybeRpcAccount - >{ - publicKey: asset.metadata.publicKey, - exists: false, - }); + >{ + publicKey: asset.metadata.publicKey, + exists: false, + }); t.deepEqual(await umi.rpc.getBalance(asset.metadata.publicKey), lamports(0)); const lamportsAfter = await umi.rpc.getBalance(closeDestination); diff --git a/clients/js/test/close/nonFungible.test.ts b/clients/js/test/close/nonFungible.test.ts index 2afdc2cd..ec2b7057 100644 --- a/clients/js/test/close/nonFungible.test.ts +++ b/clients/js/test/close/nonFungible.test.ts @@ -77,10 +77,10 @@ test.skip('it can close ownerless metadata for a non-fungible with zero supply', t.deepEqual(await umi.rpc.getAccount(asset.metadata.publicKey), < MaybeRpcAccount - >{ - publicKey: asset.metadata.publicKey, - exists: false, - }); + >{ + publicKey: asset.metadata.publicKey, + exists: false, + }); t.deepEqual(await umi.rpc.getBalance(asset.metadata.publicKey), lamports(0)); const lamportsAfter = await umi.rpc.getBalance(closeDestination); @@ -268,10 +268,10 @@ test.skip('it can close ownerless metadata for a non-fungible edition with zero t.deepEqual(await umi.rpc.getAccount(asset.metadata.publicKey), < MaybeRpcAccount - >{ - publicKey: asset.metadata.publicKey, - exists: false, - }); + >{ + publicKey: asset.metadata.publicKey, + exists: false, + }); t.deepEqual(await umi.rpc.getBalance(asset.metadata.publicKey), lamports(0)); const lamportsAfter = await umi.rpc.getBalance(closeDestination); diff --git a/clients/js/test/revokeCollectionV1.test.ts b/clients/js/test/revokeCollectionV1.test.ts index 5060c33d..7e7ba7d1 100644 --- a/clients/js/test/revokeCollectionV1.test.ts +++ b/clients/js/test/revokeCollectionV1.test.ts @@ -49,3 +49,41 @@ NON_EDITION_TOKEN_STANDARDS.forEach((tokenStandard) => { t.false(await umi.rpc.accountExists(metadataDelegateRecord)); }); }); + +NON_EDITION_TOKEN_STANDARDS.forEach((tokenStandard) => { + test(`it can self-revoke a collection delegate for a ${tokenStandard}`, async (t) => { + // Given an asset with an approved collection delegate. + const umi = await createUmi(); + const updateAuthority = generateSigner(umi); + const { publicKey: mint } = await createDigitalAssetWithToken(umi, { + authority: updateAuthority, + tokenStandard: TokenStandard[tokenStandard], + }); + const collectionDelegate = generateSigner(umi); + await delegateCollectionV1(umi, { + mint, + authority: updateAuthority, + delegate: collectionDelegate.publicKey, + tokenStandard: TokenStandard[tokenStandard], + }).sendAndConfirm(umi); + const [metadataDelegateRecord] = findMetadataDelegateRecordPda(umi, { + mint, + delegateRole: MetadataDelegateRole.Collection, + delegate: collectionDelegate.publicKey, + updateAuthority: updateAuthority.publicKey, + }); + t.true(await umi.rpc.accountExists(metadataDelegateRecord)); + + // When we revoke the collection delegate. + await revokeCollectionV1(umi, { + mint, + authority: collectionDelegate, + updateAuthority: updateAuthority.publicKey, + delegate: collectionDelegate.publicKey, + tokenStandard: TokenStandard[tokenStandard], + }).sendAndConfirm(umi); + + // Then the metadata delegate record was deleted. + t.false(await umi.rpc.accountExists(metadataDelegateRecord)); + }); +}); diff --git a/programs/token-metadata/program/src/processor/delegate/revoke.rs b/programs/token-metadata/program/src/processor/delegate/revoke.rs index c2fa5973..454c1ba0 100644 --- a/programs/token-metadata/program/src/processor/delegate/revoke.rs +++ b/programs/token-metadata/program/src/processor/delegate/revoke.rs @@ -195,21 +195,21 @@ fn get_delegate_record_update_authority( delegate_record_info: &AccountInfo, authority: &Pubkey, ) -> Result { - let delegate_record_update_authority = match delegate_scenario { + let (delegate, delegate_record_update_authority) = match delegate_scenario { DelegateScenario::Metadata(_) => { - MetadataDelegateRecord::from_account_info(delegate_record_info) - .map_err(|_| MetadataError::DelegateNotFound)? - .update_authority + let record = MetadataDelegateRecord::from_account_info(delegate_record_info) + .map_err(|_| MetadataError::DelegateNotFound)?; + (record.delegate, record.update_authority) } DelegateScenario::Holder(_) => { - HolderDelegateRecord::from_account_info(delegate_record_info) - .map_err(|_| MetadataError::DelegateNotFound)? - .update_authority + let record = HolderDelegateRecord::from_account_info(delegate_record_info) + .map_err(|_| MetadataError::DelegateNotFound)?; + (record.delegate, record.update_authority) } _ => return Err(MetadataError::InvalidDelegateRole.into()), }; - if cmp_pubkeys(&delegate_record_update_authority, authority) { + if cmp_pubkeys(&delegate, authority) { Ok(delegate_record_update_authority) } else { Err(MetadataError::InvalidDelegate.into()) diff --git a/programs/token-metadata/program/tests/utils/metadata.rs b/programs/token-metadata/program/tests/utils/metadata.rs index d7c34c22..e6b12369 100644 --- a/programs/token-metadata/program/tests/utils/metadata.rs +++ b/programs/token-metadata/program/tests/utils/metadata.rs @@ -240,6 +240,30 @@ impl Metadata { #[cfg(feature = "padded")] upsize_metadata(context, &self.pubkey).await; + #[cfg(feature = "resize")] + { + let tx = Transaction::new_signed_with_payer( + &[ResizeBuilder::new() + .metadata(self.pubkey) + .edition(find_master_edition_account(&self.mint.pubkey()).0) + .mint(self.mint.pubkey()) + .payer(context.payer.pubkey()) + .authority(context.payer.pubkey()) + .token(self.token.pubkey()) + .system_program(solana_program::system_program::ID) + .build() + .unwrap() + .instruction()], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + assert_before_metadata(context, self.pubkey).await; + context.banks_client.process_transaction(tx).await?; + assert_after_metadata(context, self.pubkey).await; + } + Ok(()) } @@ -316,6 +340,30 @@ impl Metadata { #[cfg(feature = "padded")] upsize_metadata(context, &self.pubkey).await; + #[cfg(feature = "resize")] + { + let tx = Transaction::new_signed_with_payer( + &[ResizeBuilder::new() + .metadata(self.pubkey) + .edition(find_master_edition_account(&self.mint.pubkey()).0) + .mint(self.mint.pubkey()) + .payer(context.payer.pubkey()) + .authority(context.payer.pubkey()) + .token(self.token.pubkey()) + .system_program(solana_program::system_program::ID) + .build() + .unwrap() + .instruction()], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + assert_before_metadata(context, self.pubkey).await; + context.banks_client.process_transaction(tx).await?; + assert_after_metadata(context, self.pubkey).await; + } + Ok(()) }