Governance: Transfer freeze and close authorities (#2799)
* fix: transfer mint freeze authority for mint governance * chore: update transfer_mint_authorities comments * feat: transfer close_authority when creating token account governance * chore: update spl-token function names * chore: remove redundant set_spl_token_mint_authority
This commit is contained in:
parent
6d62ea6cc7
commit
56953b2286
|
@ -107,6 +107,7 @@ impl GovernanceChatProgramTest {
|
|||
.create_mint(
|
||||
&governing_token_mint_keypair,
|
||||
&governing_token_mint_authority.pubkey(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
|
|
@ -337,7 +337,7 @@ pub enum GovernanceInstruction {
|
|||
/// 0. `[]` Realm account the created Governance belongs to
|
||||
/// 1. `[writable]` Mint Governance account. PDA seeds: ['mint-governance', realm, governed_mint]
|
||||
/// 2. `[writable]` Mint governed by this Governance account
|
||||
/// 3. `[signer]` Current Mint Authority
|
||||
/// 3. `[signer]` Current Mint authority (MintTokens and optionally FreezeAccount)
|
||||
/// 4. `[]` Governing TokenOwnerRecord account
|
||||
/// 5. `[signer]` Payer
|
||||
/// 6. `[]` SPL Token program
|
||||
|
@ -352,10 +352,10 @@ pub enum GovernanceInstruction {
|
|||
config: GovernanceConfig,
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Indicates whether Mint's authority should be transferred to the Governance PDA
|
||||
/// Indicates whether Mint's authorities (MintTokens, FreezeAccount) should be transferred to the Governance PDA
|
||||
/// If it's set to false then it can be done at a later time
|
||||
/// However the instruction would validate the current mint authority signed the transaction nonetheless
|
||||
transfer_mint_authority: bool,
|
||||
transfer_mint_authorities: bool,
|
||||
},
|
||||
|
||||
/// Creates Token Governance account which governs a token account
|
||||
|
@ -363,7 +363,7 @@ pub enum GovernanceInstruction {
|
|||
/// 0. `[]` Realm account the created Governance belongs to
|
||||
/// 1. `[writable]` Token Governance account. PDA seeds: ['token-governance', realm, governed_token]
|
||||
/// 2. `[writable]` Token account governed by this Governance account
|
||||
/// 3. `[signer]` Current Token account
|
||||
/// 3. `[signer]` Current token account authority (AccountOwner and optionally CloseAccount)
|
||||
/// 4. `[]` Governing TokenOwnerRecord account
|
||||
/// 5. `[signer]` Payer
|
||||
/// 6. `[]` SPL Token program
|
||||
|
@ -378,10 +378,10 @@ pub enum GovernanceInstruction {
|
|||
config: GovernanceConfig,
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Indicates whether token owner should be transferred to the Governance PDA
|
||||
/// Indicates whether the token account authorities (AccountOwner and optionally CloseAccount) should be transferred to the Governance PDA
|
||||
/// If it's set to false then it can be done at a later time
|
||||
/// However the instruction would validate the current token owner signed the transaction nonetheless
|
||||
transfer_token_owner: bool,
|
||||
transfer_account_authorities: bool,
|
||||
},
|
||||
|
||||
/// Sets GovernanceConfig for a Governance
|
||||
|
@ -756,7 +756,7 @@ pub fn create_mint_governance(
|
|||
voter_weight_record: Option<Pubkey>,
|
||||
// Args
|
||||
config: GovernanceConfig,
|
||||
transfer_mint_authority: bool,
|
||||
transfer_mint_authorities: bool,
|
||||
) -> Instruction {
|
||||
let mint_governance_address = get_mint_governance_address(program_id, realm, governed_mint);
|
||||
|
||||
|
@ -777,7 +777,7 @@ pub fn create_mint_governance(
|
|||
|
||||
let instruction = GovernanceInstruction::CreateMintGovernance {
|
||||
config,
|
||||
transfer_mint_authority,
|
||||
transfer_mint_authorities,
|
||||
};
|
||||
|
||||
Instruction {
|
||||
|
@ -801,7 +801,7 @@ pub fn create_token_governance(
|
|||
voter_weight_record: Option<Pubkey>,
|
||||
// Args
|
||||
config: GovernanceConfig,
|
||||
transfer_token_owner: bool,
|
||||
transfer_account_authorities: bool,
|
||||
) -> Instruction {
|
||||
let token_governance_address = get_token_governance_address(program_id, realm, governed_token);
|
||||
|
||||
|
@ -822,7 +822,7 @@ pub fn create_token_governance(
|
|||
|
||||
let instruction = GovernanceInstruction::CreateTokenGovernance {
|
||||
config,
|
||||
transfer_token_owner,
|
||||
transfer_account_authorities,
|
||||
};
|
||||
|
||||
Instruction {
|
||||
|
|
|
@ -119,13 +119,20 @@ pub fn process_instruction(
|
|||
|
||||
GovernanceInstruction::CreateMintGovernance {
|
||||
config,
|
||||
transfer_mint_authority,
|
||||
} => process_create_mint_governance(program_id, accounts, config, transfer_mint_authority),
|
||||
transfer_mint_authorities,
|
||||
} => {
|
||||
process_create_mint_governance(program_id, accounts, config, transfer_mint_authorities)
|
||||
}
|
||||
|
||||
GovernanceInstruction::CreateTokenGovernance {
|
||||
config,
|
||||
transfer_token_owner,
|
||||
} => process_create_token_governance(program_id, accounts, config, transfer_token_owner),
|
||||
transfer_account_authorities,
|
||||
} => process_create_token_governance(
|
||||
program_id,
|
||||
accounts,
|
||||
config,
|
||||
transfer_account_authorities,
|
||||
),
|
||||
|
||||
GovernanceInstruction::CreateAccountGovernance { config } => {
|
||||
process_create_account_governance(program_id, accounts, config)
|
||||
|
|
|
@ -10,23 +10,27 @@ use crate::{
|
|||
realm::get_realm_data,
|
||||
token_owner_record::get_token_owner_record_data_for_realm,
|
||||
},
|
||||
tools::spl_token::{assert_spl_token_mint_authority_is_signer, set_spl_token_mint_authority},
|
||||
tools::spl_token::{
|
||||
assert_spl_token_mint_authority_is_signer, set_spl_token_account_authority,
|
||||
},
|
||||
};
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
sysvar::Sysvar,
|
||||
};
|
||||
use spl_governance_tools::account::create_and_serialize_account_signed;
|
||||
use spl_token::{instruction::AuthorityType, state::Mint};
|
||||
|
||||
/// Processes CreateMintGovernance instruction
|
||||
pub fn process_create_mint_governance(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
config: GovernanceConfig,
|
||||
transfer_mint_authority: bool,
|
||||
transfer_mint_authorities: bool,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
||||
|
@ -84,13 +88,28 @@ pub fn process_create_mint_governance(
|
|||
rent,
|
||||
)?;
|
||||
|
||||
if transfer_mint_authority {
|
||||
set_spl_token_mint_authority(
|
||||
if transfer_mint_authorities {
|
||||
set_spl_token_account_authority(
|
||||
governed_mint_info,
|
||||
governed_mint_authority_info,
|
||||
mint_governance_info.key,
|
||||
AuthorityType::MintTokens,
|
||||
spl_token_info,
|
||||
)?;
|
||||
|
||||
// If the mint has freeze_authority then transfer it as well
|
||||
let mint_data = Mint::unpack(&governed_mint_info.data.borrow())?;
|
||||
// Note: The code assumes mint_authority==freeze_authority
|
||||
// If this is not the case then the caller should set freeze_authority accordingly before making the transfer
|
||||
if mint_data.freeze_authority.is_some() {
|
||||
set_spl_token_account_authority(
|
||||
governed_mint_info,
|
||||
governed_mint_authority_info,
|
||||
mint_governance_info.key,
|
||||
AuthorityType::FreezeAccount,
|
||||
spl_token_info,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
assert_spl_token_mint_authority_is_signer(
|
||||
governed_mint_info,
|
||||
|
|
|
@ -10,23 +10,25 @@ use crate::{
|
|||
realm::get_realm_data,
|
||||
token_owner_record::get_token_owner_record_data_for_realm,
|
||||
},
|
||||
tools::spl_token::{assert_spl_token_owner_is_signer, set_spl_token_owner},
|
||||
tools::spl_token::{assert_spl_token_owner_is_signer, set_spl_token_account_authority},
|
||||
};
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
program_pack::Pack,
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
sysvar::Sysvar,
|
||||
};
|
||||
use spl_governance_tools::account::create_and_serialize_account_signed;
|
||||
use spl_token::{instruction::AuthorityType, state::Account};
|
||||
|
||||
/// Processes CreateTokenGovernance instruction
|
||||
pub fn process_create_token_governance(
|
||||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
config: GovernanceConfig,
|
||||
transfer_token_owner: bool,
|
||||
transfer_account_authorities: bool,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
||||
|
@ -84,13 +86,28 @@ pub fn process_create_token_governance(
|
|||
rent,
|
||||
)?;
|
||||
|
||||
if transfer_token_owner {
|
||||
set_spl_token_owner(
|
||||
if transfer_account_authorities {
|
||||
set_spl_token_account_authority(
|
||||
governed_token_info,
|
||||
governed_token_owner_info,
|
||||
token_governance_info.key,
|
||||
AuthorityType::AccountOwner,
|
||||
spl_token_info,
|
||||
)?;
|
||||
|
||||
// If the token account has close_authority then transfer it as well
|
||||
let token_account_data = Account::unpack(&governed_token_info.data.borrow())?;
|
||||
// Note: The code assumes owner==close_authority
|
||||
// If this is not the case then the caller should set close_authority accordingly before making the transfer
|
||||
if token_account_data.close_authority.is_some() {
|
||||
set_spl_token_account_authority(
|
||||
governed_token_info,
|
||||
governed_token_owner_info,
|
||||
token_governance_info.key,
|
||||
AuthorityType::CloseAccount,
|
||||
spl_token_info,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
assert_spl_token_owner_is_signer(governed_token_info, governed_token_owner_info)?;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use solana_program::{
|
|||
system_instruction,
|
||||
};
|
||||
use spl_token::{
|
||||
instruction::set_authority,
|
||||
instruction::{set_authority, AuthorityType},
|
||||
state::{Account, Mint},
|
||||
};
|
||||
|
||||
|
@ -287,34 +287,6 @@ pub fn assert_spl_token_mint_authority_is_signer(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets new mint authority
|
||||
pub fn set_spl_token_mint_authority<'a>(
|
||||
mint_info: &AccountInfo<'a>,
|
||||
mint_authority: &AccountInfo<'a>,
|
||||
new_mint_authority: &Pubkey,
|
||||
spl_token_info: &AccountInfo<'a>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let set_authority_ix = set_authority(
|
||||
&spl_token::id(),
|
||||
mint_info.key,
|
||||
Some(new_mint_authority),
|
||||
spl_token::instruction::AuthorityType::MintTokens,
|
||||
mint_authority.key,
|
||||
&[],
|
||||
)?;
|
||||
|
||||
invoke(
|
||||
&set_authority_ix,
|
||||
&[
|
||||
mint_info.clone(),
|
||||
mint_authority.clone(),
|
||||
spl_token_info.clone(),
|
||||
],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Asserts current token owner matches the given owner and it's signer of the transaction
|
||||
pub fn assert_spl_token_owner_is_signer(
|
||||
token_info: &AccountInfo,
|
||||
|
@ -333,27 +305,28 @@ pub fn assert_spl_token_owner_is_signer(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Sets new token account owner
|
||||
pub fn set_spl_token_owner<'a>(
|
||||
token_info: &AccountInfo<'a>,
|
||||
token_owner: &AccountInfo<'a>,
|
||||
new_token_owner: &Pubkey,
|
||||
/// Sets spl-token account (Mint or TokenAccount) authority
|
||||
pub fn set_spl_token_account_authority<'a>(
|
||||
account_info: &AccountInfo<'a>,
|
||||
account_authority: &AccountInfo<'a>,
|
||||
new_account_authority: &Pubkey,
|
||||
authority_type: AuthorityType,
|
||||
spl_token_info: &AccountInfo<'a>,
|
||||
) -> Result<(), ProgramError> {
|
||||
let set_authority_ix = set_authority(
|
||||
&spl_token::id(),
|
||||
token_info.key,
|
||||
Some(new_token_owner),
|
||||
spl_token::instruction::AuthorityType::AccountOwner,
|
||||
token_owner.key,
|
||||
account_info.key,
|
||||
Some(new_account_authority),
|
||||
authority_type,
|
||||
account_authority.key,
|
||||
&[],
|
||||
)?;
|
||||
|
||||
invoke(
|
||||
&set_authority_ix,
|
||||
&[
|
||||
token_info.clone(),
|
||||
token_owner.clone(),
|
||||
account_info.clone(),
|
||||
account_authority.clone(),
|
||||
spl_token_info.clone(),
|
||||
],
|
||||
)?;
|
||||
|
|
|
@ -226,3 +226,48 @@ async fn test_create_mint_governance_with_invalid_realm_error() {
|
|||
// Assert
|
||||
assert_eq!(err, GovernanceToolsError::InvalidAccountType.into());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_mint_governance_with_freeze_authority_transfer() {
|
||||
// Arrange
|
||||
let mut governance_test = GovernanceProgramTest::start_new().await;
|
||||
|
||||
let realm_cookie = governance_test.with_realm().await;
|
||||
let governed_mint_cookie = governance_test.with_freezable_governed_mint().await;
|
||||
|
||||
let token_owner_record_cookie = governance_test
|
||||
.with_community_token_deposit(&realm_cookie)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Act
|
||||
let mint_governance_cookie = governance_test
|
||||
.with_mint_governance(
|
||||
&realm_cookie,
|
||||
&governed_mint_cookie,
|
||||
&token_owner_record_cookie,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// // Assert
|
||||
let mint_governance_account = governance_test
|
||||
.get_governance_account(&mint_governance_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(mint_governance_cookie.account, mint_governance_account);
|
||||
|
||||
let mint_account = governance_test
|
||||
.get_mint_account(&governed_mint_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
mint_governance_cookie.address,
|
||||
mint_account.mint_authority.unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
mint_governance_cookie.address,
|
||||
mint_account.freeze_authority.unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use solana_sdk::{signature::Keypair, signer::Signer};
|
|||
use spl_governance::error::GovernanceError;
|
||||
use spl_governance_tools::error::GovernanceToolsError;
|
||||
|
||||
use spl_token::error::TokenError;
|
||||
use spl_token::{error::TokenError, instruction::AuthorityType};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_token_governance() {
|
||||
|
@ -225,3 +225,54 @@ async fn test_create_token_governance_with_invalid_realm_error() {
|
|||
// Assert
|
||||
assert_eq!(err, GovernanceToolsError::InvalidAccountType.into());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_token_governance_with_close_authority_transfer() {
|
||||
// Arrange
|
||||
let mut governance_test = GovernanceProgramTest::start_new().await;
|
||||
|
||||
let realm_cookie = governance_test.with_realm().await;
|
||||
let governed_token_cookie = governance_test.with_governed_token().await;
|
||||
|
||||
governance_test
|
||||
.bench
|
||||
.set_spl_token_account_authority(
|
||||
&governed_token_cookie.address,
|
||||
&governed_token_cookie.token_owner,
|
||||
Some(&governed_token_cookie.token_owner.pubkey()),
|
||||
AuthorityType::CloseAccount,
|
||||
)
|
||||
.await;
|
||||
|
||||
let token_owner_record_cookie = governance_test
|
||||
.with_community_token_deposit(&realm_cookie)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Act
|
||||
let token_governance_cookie = governance_test
|
||||
.with_token_governance(
|
||||
&realm_cookie,
|
||||
&governed_token_cookie,
|
||||
&token_owner_record_cookie,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Assert
|
||||
let token_governance_account = governance_test
|
||||
.get_governance_account(&token_governance_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(token_governance_cookie.account, token_governance_account);
|
||||
|
||||
let token_account = governance_test
|
||||
.get_token_account(&governed_token_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(token_governance_cookie.address, token_account.owner);
|
||||
assert_eq!(
|
||||
token_governance_cookie.address,
|
||||
token_account.close_authority.unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -176,6 +176,7 @@ impl GovernanceProgramTest {
|
|||
.create_mint(
|
||||
&community_token_mint_keypair,
|
||||
&community_token_mint_authority.pubkey(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
@ -197,6 +198,7 @@ impl GovernanceProgramTest {
|
|||
.create_mint(
|
||||
&council_token_mint_keypair,
|
||||
&council_token_mint_authority.pubkey(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
|
@ -962,16 +964,34 @@ impl GovernanceProgramTest {
|
|||
|
||||
#[allow(dead_code)]
|
||||
pub async fn with_governed_mint(&mut self) -> GovernedMintCookie {
|
||||
let mint_keypair = Keypair::new();
|
||||
let mint_authority = Keypair::new();
|
||||
|
||||
self.with_governed_mint_impl(&mint_authority, None).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn with_freezable_governed_mint(&mut self) -> GovernedMintCookie {
|
||||
let mint_authority = Keypair::new();
|
||||
|
||||
self.with_governed_mint_impl(&mint_authority, Some(&mint_authority.pubkey()))
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn with_governed_mint_impl(
|
||||
&mut self,
|
||||
mint_authority: &Keypair,
|
||||
freeze_authority: Option<&Pubkey>,
|
||||
) -> GovernedMintCookie {
|
||||
let mint_keypair = Keypair::new();
|
||||
|
||||
self.bench
|
||||
.create_mint(&mint_keypair, &mint_authority.pubkey())
|
||||
.create_mint(&mint_keypair, &mint_authority.pubkey(), freeze_authority)
|
||||
.await;
|
||||
|
||||
GovernedMintCookie {
|
||||
address: mint_keypair.pubkey(),
|
||||
mint_authority,
|
||||
mint_authority: clone_keypair(mint_authority),
|
||||
transfer_mint_authority: true,
|
||||
}
|
||||
}
|
||||
|
@ -982,7 +1002,7 @@ impl GovernanceProgramTest {
|
|||
let mint_authority = Keypair::new();
|
||||
|
||||
self.bench
|
||||
.create_mint(&mint_keypair, &mint_authority.pubkey())
|
||||
.create_mint(&mint_keypair, &mint_authority.pubkey(), None)
|
||||
.await;
|
||||
|
||||
let token_keypair = Keypair::new();
|
||||
|
|
|
@ -12,6 +12,7 @@ use solana_sdk::{account::Account, signature::Keypair, signer::Signer, transacti
|
|||
|
||||
use bincode::deserialize;
|
||||
|
||||
use spl_token::instruction::{set_authority, AuthorityType};
|
||||
use tools::clone_keypair;
|
||||
|
||||
use crate::tools::map_transaction_error;
|
||||
|
@ -113,7 +114,12 @@ impl ProgramTestBench {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn create_mint(&mut self, mint_keypair: &Keypair, mint_authority: &Pubkey) {
|
||||
pub async fn create_mint(
|
||||
&mut self,
|
||||
mint_keypair: &Keypair,
|
||||
mint_authority: &Pubkey,
|
||||
freeze_authority: Option<&Pubkey>,
|
||||
) {
|
||||
let mint_rent = self.rent.minimum_balance(spl_token::state::Mint::LEN);
|
||||
|
||||
let instructions = [
|
||||
|
@ -128,7 +134,7 @@ impl ProgramTestBench {
|
|||
&spl_token::id(),
|
||||
&mint_keypair.pubkey(),
|
||||
mint_authority,
|
||||
None,
|
||||
freeze_authority,
|
||||
0,
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -139,6 +145,29 @@ impl ProgramTestBench {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
/// Sets spl-token program account (Mint or TokenAccount) authority
|
||||
pub async fn set_spl_token_account_authority(
|
||||
&mut self,
|
||||
account: &Pubkey,
|
||||
account_authority: &Keypair,
|
||||
new_authority: Option<&Pubkey>,
|
||||
authority_type: AuthorityType,
|
||||
) {
|
||||
let set_authority_ix = set_authority(
|
||||
&spl_token::id(),
|
||||
account,
|
||||
new_authority,
|
||||
authority_type,
|
||||
&account_authority.pubkey(),
|
||||
&[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
self.process_transaction(&[set_authority_ix], Some(&[account_authority]))
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn create_empty_token_account(
|
||||
&mut self,
|
||||
|
|
Loading…
Reference in New Issue