Skip to content

Commit

Permalink
feat: edit_controller_authority IX (#337)
Browse files Browse the repository at this point in the history
## Summary

In order to fully migrate the UXD-Program to the new redemption DAO, we
need to be able to migrate its authority pubkey. This PR provides a
simple implementation for the authority to be transfered to a different
pubkey.
  • Loading branch information
crypto-vincent authored Oct 8, 2024
1 parent 7d31b06 commit b860eaa
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 1 deletion.
9 changes: 8 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,5 +70,12 @@
"WSOLATA"
],
"editor.formatOnSave": true,
"rust-analyzer.linkedProjects": ["./programs/uxd/Cargo.toml"]
"rust-analyzer.linkedProjects": [
"./programs/uxd/Cargo.toml"
],
"workbench.colorCustomizations": {
"activityBar.background": "#161D82",
"titleBar.activeBackground": "#1F28B6",
"titleBar.activeForeground": "#FCFCFF"
}
}
32 changes: 32 additions & 0 deletions programs/uxd/src/instructions/edit_controller_authority.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use crate::error::UxdError;
use crate::validate_is_program_frozen;
use crate::Controller;
use crate::CONTROLLER_NAMESPACE;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct EditControllerAuthority<'info> {
/// #1 Authored call accessible only to the signer matching Controller.authority
pub authority: Signer<'info>,
/// #2 The top level UXDProgram on chain account managing the redeemable mint
#[account(
mut,
seeds = [CONTROLLER_NAMESPACE],
bump = controller.load()?.bump,
has_one = authority @UxdError::InvalidAuthority,
)]
pub controller: AccountLoader<'info, Controller>,
}

pub(crate) fn handler(ctx: Context<EditControllerAuthority>, authority: &Pubkey) -> Result<()> {
let controller = &mut ctx.accounts.controller.load_mut()?;
controller.authority = *authority;
Ok(())
}

impl<'info> EditControllerAuthority<'info> {
pub(crate) fn validate(&self, _authority: &Pubkey) -> Result<()> {
validate_is_program_frozen(self.controller.load()?)?;
Ok(())
}
}
2 changes: 2 additions & 0 deletions programs/uxd/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod credix_lp;
pub mod edit_controller;
pub mod edit_controller_authority;
pub mod edit_identity_depository;
pub mod edit_mercurial_vault_depository;
pub mod freeze_program;
Expand All @@ -14,6 +15,7 @@ pub mod register_mercurial_vault_depository;

pub use credix_lp::*;
pub use edit_controller::*;
pub use edit_controller_authority::*;
pub use edit_identity_depository::*;
pub use edit_mercurial_vault_depository::*;
pub use freeze_program::*;
Expand Down
8 changes: 8 additions & 0 deletions programs/uxd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ pub mod uxd {
instructions::edit_controller::handler(ctx, &fields)
}

#[access_control(ctx.accounts.validate(&authority))]
pub fn edit_controller_authority(
ctx: Context<EditControllerAuthority>,
authority: Pubkey,
) -> Result<()> {
instructions::edit_controller_authority::handler(ctx, &authority)
}

#[access_control(ctx.accounts.validate())]
pub fn edit_mercurial_vault_depository(
ctx: Context<EditMercurialVaultDepository>,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod process_collect_profits_of_mercurial_vault_depository;
pub mod process_edit_controller;
pub mod process_edit_controller_authority;
pub mod process_edit_credix_lp_depository;
pub mod process_edit_identity_depository;
pub mod process_edit_mercurial_vault_depository;
Expand All @@ -20,6 +21,7 @@ pub mod process_register_mercurial_vault_depository;

pub use process_collect_profits_of_mercurial_vault_depository::*;
pub use process_edit_controller::*;
pub use process_edit_controller_authority::*;
pub use process_edit_credix_lp_depository::*;
pub use process_edit_identity_depository::*;
pub use process_edit_mercurial_vault_depository::*;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
use anchor_lang::InstructionData;
use anchor_lang::ToAccountMetas;
use solana_sdk::instruction::Instruction;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;
use solana_sdk::signer::Signer;

use uxd::state::Controller;

use crate::integration_tests::api::program_context;
use crate::integration_tests::api::program_uxd;

pub async fn process_edit_controller_authority(
program_context: &mut Box<dyn program_context::ProgramContext>,
payer: &Keypair,
authority: &Keypair,
new_authority: &Pubkey,
) -> Result<(), program_context::ProgramError> {
// Find needed accounts
let controller = program_uxd::accounts::find_controller_pda().0;

// Read state before
let controller_before =
program_context::read_account_anchor::<Controller>(program_context, &controller).await?;

// Execute IX
let accounts = uxd::accounts::EditControllerAuthority {
authority: authority.pubkey(),
controller,
};
let payload = uxd::instruction::EditControllerAuthority {
authority: *new_authority,
};
let instruction = Instruction {
program_id: uxd::id(),
accounts: accounts.to_account_metas(None),
data: payload.data(),
};
program_context::process_instruction_with_signer(
program_context,
instruction,
payer,
authority,
)
.await?;

// Read state after
let controller_after =
program_context::read_account_anchor::<Controller>(program_context, &controller).await?;

// Check behaviour
let controller_authority_before = controller_before.authority;
assert_eq!(controller_authority_before, authority.pubkey());

let controller_authority_after = controller_after.authority;
assert_eq!(controller_authority_after, *new_authority);

// Done
Ok(())
}
1 change: 1 addition & 0 deletions programs/uxd/tests/integration_tests/suites/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod test_controller_edit;
pub mod test_controller_edit_authority;
pub mod test_credix_lp_depository_edit;
pub mod test_credix_lp_depository_exchange_liquidity;
pub mod test_credix_lp_depository_mint;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
use solana_program_test::tokio;
use solana_sdk::signer::keypair::Keypair;
use solana_sdk::signer::Signer;
use uxd::instructions::EditControllerFields;

use crate::integration_tests::api::program_context;
use crate::integration_tests::api::program_uxd;

#[tokio::test]
async fn test_controller_edit_authority() -> Result<(), program_context::ProgramError> {
// ---------------------------------------------------------------------
// -- Phase 1
// -- Setup basic context and accounts needed for this test suite
// ---------------------------------------------------------------------

let mut program_context: Box<dyn program_context::ProgramContext> =
Box::new(program_context::create_program_test_context().await);

// Fund payer
let payer = Keypair::new();
program_context
.process_airdrop(&payer.pubkey(), 1_000_000_000_000)
.await?;

// Hardcode mints decimals
let collateral_mint_decimals = 6;
let redeemable_mint_decimals = 6;

// Important account keys
let authority = Keypair::new();
let collateral_mint = Keypair::new();
let mercurial_vault_lp_mint = Keypair::new();
let credix_multisig = Keypair::new();

// Initialize basic UXD program state
program_uxd::procedures::process_deploy_program(
&mut program_context,
&payer,
&authority,
&collateral_mint,
&mercurial_vault_lp_mint,
&credix_multisig,
collateral_mint_decimals,
redeemable_mint_decimals,
)
.await?;

// ---------------------------------------------------------------------
// -- Phase 2
// -- Change the controller authority back and forth
// ---------------------------------------------------------------------

let old_authority = authority;
let new_authority = Keypair::new();

// Using the wrong authority should fail
assert!(
program_uxd::instructions::process_edit_controller_authority(
&mut program_context,
&payer,
&payer,
&new_authority.pubkey(),
)
.await
.is_err()
);

// Using the correct authority should succeed
program_uxd::instructions::process_edit_controller_authority(
&mut program_context,
&payer,
&old_authority,
&new_authority.pubkey(),
)
.await?;

// After changing the authority we cant use it again
assert!(
program_uxd::instructions::process_edit_controller_authority(
&mut program_context,
&payer,
&old_authority,
&new_authority.pubkey(),
)
.await
.is_err()
);

// The new authority can use it now
program_uxd::instructions::process_edit_controller_authority(
&mut program_context,
&payer,
&new_authority,
&new_authority.pubkey(),
)
.await?;

// The old authority can not use the program anymore, but the new one can
assert!(program_uxd::instructions::process_edit_controller(
&mut program_context,
&payer,
&old_authority,
&EditControllerFields {
redeemable_global_supply_cap: None,
depositories_routing_weight_bps: None,
router_depositories: None,
outflow_limit_per_epoch_amount: None,
outflow_limit_per_epoch_bps: None,
slots_per_epoch: None,
},
)
.await
.is_err());
program_uxd::instructions::process_edit_controller(
&mut program_context,
&payer,
&new_authority,
&EditControllerFields {
redeemable_global_supply_cap: None,
depositories_routing_weight_bps: None,
router_depositories: None,
outflow_limit_per_epoch_amount: None,
outflow_limit_per_epoch_bps: None,
slots_per_epoch: None,
},
)
.await?;

// The new authority can send the authority again back to the old one
program_uxd::instructions::process_edit_controller_authority(
&mut program_context,
&payer,
&new_authority,
&old_authority.pubkey(),
)
.await?;

// The new authority can not use the program anymore, but the old one can
assert!(program_uxd::instructions::process_edit_controller(
&mut program_context,
&payer,
&new_authority,
&EditControllerFields {
redeemable_global_supply_cap: None,
depositories_routing_weight_bps: None,
router_depositories: None,
outflow_limit_per_epoch_amount: None,
outflow_limit_per_epoch_bps: None,
slots_per_epoch: None,
},
)
.await
.is_err());
program_uxd::instructions::process_edit_controller(
&mut program_context,
&payer,
&old_authority,
&EditControllerFields {
redeemable_global_supply_cap: None,
depositories_routing_weight_bps: None,
router_depositories: None,
outflow_limit_per_epoch_amount: None,
outflow_limit_per_epoch_bps: None,
slots_per_epoch: None,
},
)
.await?;

// Done
Ok(())
}

0 comments on commit b860eaa

Please sign in to comment.