Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions clients/js/test/setAuthority.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import {
fetchMetadata,
getAllocateInstruction,
getSetAuthorityInstruction,
getSetImmutableInstruction,
isProgramMetadataError,
PROGRAM_METADATA_ERROR__IMMUTABLE_METADATA_ACCOUNT,
} from '../src';
import {
createCanonicalMetadata,
Expand Down Expand Up @@ -392,3 +395,58 @@ test('the authority cannot remove itself on buffer accounts', async t => {
const error = await t.throwsAsync(promise);
t.true(isSolanaError(error.cause, SOLANA_ERROR__INSTRUCTION_ERROR__INVALID_ARGUMENT));
});

test('the authority cannot be changed on immutable metadata accounts', async t => {
// Given the following authorities and deployed program.
const client = createDefaultSolanaClient();
const [authority, explicitAuthority, anotherAuthority] = await Promise.all([
generateKeyPairSignerWithSol(client),
generateKeyPairSigner(),
generateKeyPairSigner(),
]);
const [program, programData] = await createDeployedProgram(client, authority);

// And the following initialized canonical metadata account.
const [metadata] = await createCanonicalMetadata(client, {
authority,
program,
programData,
seed: 'dummy',
data: getUtf8Encoder().encode('Hello, World!'),
});

// And given the explicit authority is set on the metadata account.
const setAuthorityIx = getSetAuthorityInstruction({
account: metadata,
authority,
program,
programData,
newAuthority: explicitAuthority.address,
});

// And the explicit authority sets the metadata account to be immutable.
const setImmutableIx = getSetImmutableInstruction({
metadata,
authority: explicitAuthority,
program,
programData,
});

// When the explicit authority attempts to set another authority on the
// metadata account after setting it to be immutable.
const setAnotherAuthorityIx = getSetAuthorityInstruction({
account: metadata,
authority,
program,
programData,
newAuthority: anotherAuthority.address,
});
const transactionMessage = pipe(await createDefaultTransaction(client, authority), tx =>
appendTransactionMessageInstructions([setAuthorityIx, setImmutableIx, setAnotherAuthorityIx], tx),
);
const promise = signAndSendTransaction(client, transactionMessage);

// Then we expect the transaction to fail.
const error = await t.throwsAsync(promise);
t.true(isProgramMetadataError(error.cause, transactionMessage, PROGRAM_METADATA_ERROR__IMMUTABLE_METADATA_ACCOUNT));
});
5 changes: 5 additions & 0 deletions program/src/processor/set_authority.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use pinocchio::{account::AccountView, error::ProgramError, Address, ProgramResult};

use crate::{
error::ProgramMetadataError,
processor::validate_authority,
state::{buffer::Buffer, header::Header, AccountDiscriminator, Zeroable},
};
Expand Down Expand Up @@ -61,6 +62,10 @@ pub fn set_authority(accounts: &mut [AccountView], instruction_data: &[u8]) -> P
return Err(ProgramError::InvalidAccountData);
}

if !header.mutable() {
return Err(ProgramMetadataError::ImmutableMetadataAccount.into());
}

validate_authority(header, authority, program, program_data)?;

header.authority = if *has_new_authority == 0 {
Expand Down
Loading