Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: edit_controller_authority IX #337

Merged
merged 3 commits into from
Oct 8, 2024
Merged
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
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 @@ -19,6 +20,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_mint;
pub mod test_credix_lp_depository_rebalance_illiquid;
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(())
}
Loading