Skip to content

Commit

Permalink
feat(program-config): add ProgramConfig and instructions that work wi…
Browse files Browse the repository at this point in the history
…th it
  • Loading branch information
vovacodes committed Dec 2, 2023
1 parent e35bf30 commit 0671efe
Show file tree
Hide file tree
Showing 43 changed files with 2,955 additions and 360 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions programs/squads_multisig_program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ default = []
[dependencies]
anchor-lang = { version = "=0.29.0", features = ["allow-missing-optionals"] }
anchor-spl = { version="=0.29.0", features=["token"] }
solana-program = "1.17.4"
solana-security-txt = "1.1.1"
#
4 changes: 4 additions & 0 deletions programs/squads_multisig_program/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ pub use multisig_add_spending_limit::*;
pub use multisig_config::*;
pub use multisig_create::*;
pub use multisig_remove_spending_limit::*;
pub use program_config_init::*;
pub use program_config::*;
pub use proposal_activate::*;
pub use proposal_create::*;
pub use proposal_vote::*;
Expand All @@ -24,6 +26,8 @@ mod multisig_add_spending_limit;
mod multisig_config;
mod multisig_create;
mod multisig_remove_spending_limit;
mod program_config_init;
mod program_config;
mod proposal_activate;
mod proposal_create;
mod proposal_vote;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#![allow(deprecated)]
use anchor_lang::prelude::*;
use anchor_lang::system_program;
use solana_program::native_token::LAMPORTS_PER_SOL;

use crate::errors::MultisigError;
use crate::state::*;

#[derive(AnchorSerialize, AnchorDeserialize)]
Expand All @@ -20,6 +24,10 @@ pub struct MultisigCreateArgs {
pub memo: Option<String>,
}

#[deprecated(
since = "0.4.0",
note = "This instruction is deprecated and will be removed soon. Please use `multisig_create_v2` to ensure future compatibility."
)]
#[derive(Accounts)]
#[instruction(args: MultisigCreateArgs)]
pub struct MultisigCreate<'info> {
Expand All @@ -43,11 +51,84 @@ pub struct MultisigCreate<'info> {
pub system_program: Program<'info, System>,
}

#[allow(deprecated)]
impl MultisigCreate<'_> {
fn validate(&self) -> Result<()> {
Ok(())
}

/// Creates a multisig.
#[allow(deprecated)]
#[access_control(ctx.accounts.validate())]
pub fn multisig_create(ctx: Context<Self>, args: MultisigCreateArgs) -> Result<()> {
msg!("WARNING: This instruction is deprecated and will be removed soon. Please use `multisig_create_v2` to ensure future compatibility.");

// Sort the members by pubkey.
let mut members = args.members;
members.sort_by_key(|m| m.key);

// Initialize the multisig.
let multisig = &mut ctx.accounts.multisig;
multisig.config_authority = args.config_authority.unwrap_or_default();
multisig.threshold = args.threshold;
multisig.time_lock = args.time_lock;
multisig.transaction_index = 0;
multisig.stale_transaction_index = 0;
multisig.create_key = ctx.accounts.create_key.key();
multisig.bump = ctx.bumps.multisig;
multisig.members = members;
multisig.rent_collector = args.rent_collector;

multisig.invariant()?;

Ok(())
}
}

#[derive(Accounts)]
#[instruction(args: MultisigCreateArgs)]
pub struct MultisigCreateV2<'info> {
/// Global program config account.
pub program_config: Account<'info, ProgramConfig>,

/// The treasury where the creation fee is transferred to.
/// CHECK: validation is performed in the `MultisigCreate::validate()` method.
#[account(mut)]
pub treasury: AccountInfo<'info>,

#[account(
init,
payer = creator,
space = Multisig::size(args.members.len(), args.rent_collector.is_some()),
seeds = [SEED_PREFIX, SEED_MULTISIG, create_key.key().as_ref()],
bump
)]
pub multisig: Account<'info, Multisig>,

/// An ephemeral signer that is used as a seed for the Multisig PDA.
/// Must be a signer to prevent front-running attack by someone else but the original creator.
pub create_key: Signer<'info>,

/// The creator of the multisig.
#[account(mut)]
pub creator: Signer<'info>,

pub system_program: Program<'info, System>,
}

impl MultisigCreateV2<'_> {
fn validate(&self) -> Result<()> {
//region treasury
require_keys_eq!(
self.treasury.key(),
self.program_config.treasury,
MultisigError::InvalidAccount
);
//endregion

Ok(())
}

/// Creates a multisig.
#[access_control(ctx.accounts.validate())]
pub fn multisig_create(ctx: Context<Self>, args: MultisigCreateArgs) -> Result<()> {
Expand All @@ -69,6 +150,22 @@ impl MultisigCreate<'_> {

multisig.invariant()?;

let creation_fee = ctx.accounts.program_config.multisig_creation_fee;

if creation_fee > 0 {
system_program::transfer(
CpiContext::new(
ctx.accounts.system_program.to_account_info(),
system_program::Transfer {
from: ctx.accounts.creator.to_account_info(),
to: ctx.accounts.treasury.to_account_info(),
},
),
creation_fee,
)?;
msg!("Creation fee: {}", creation_fee / LAMPORTS_PER_SOL);
}

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use anchor_lang::prelude::*;

use crate::errors::MultisigError;

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ProgramConfigSetAuthorityArgs {
pub new_authority: Pubkey,
}

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ProgramConfigSetMultisigCreationFeeArgs {
pub new_multisig_creation_fee: u64,
}

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ProgramConfigSetTreasuryArgs {
pub new_treasury: Pubkey,
}

#[derive(Accounts)]
pub struct ProgramConfig<'info> {
#[account(mut)]
pub program_config: Account<'info, crate::state::ProgramConfig>,

pub authority: Signer<'info>,
}

impl ProgramConfig<'_> {
fn validate(&self) -> Result<()> {
let Self {
program_config,
authority,
} = self;

// authority
require_keys_eq!(
program_config.authority,
authority.key(),
MultisigError::Unauthorized
);

Ok(())
}

#[access_control(ctx.accounts.validate())]
pub fn program_config_set_authority(
ctx: Context<Self>,
args: ProgramConfigSetAuthorityArgs,
) -> Result<()> {
let program_config = &mut ctx.accounts.program_config;

program_config.authority = args.new_authority;

Ok(())
}

#[access_control(ctx.accounts.validate())]
pub fn program_config_set_multisig_creation_fee(
ctx: Context<Self>,
args: ProgramConfigSetMultisigCreationFeeArgs,
) -> Result<()> {
let program_config = &mut ctx.accounts.program_config;

program_config.multisig_creation_fee = args.new_multisig_creation_fee;

Ok(())
}

#[access_control(ctx.accounts.validate())]
pub fn program_config_set_treasury(
ctx: Context<Self>,
args: ProgramConfigSetTreasuryArgs,
) -> Result<()> {
let program_config = &mut ctx.accounts.program_config;

program_config.treasury = args.new_treasury;

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::errors::MultisigError;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::pubkey;

use crate::state::*;

/// This is a key controlled by the Squads team and is intended to use for the single
/// transaction that initializes the global program config. It is not used for anything else.
#[cfg(not(feature = "testing"))]
const INITIALIZER: Pubkey = pubkey!("HM5y4mz3Bt9JY9mr1hkyhnvqxSH4H2u2451j7Hc2dtvK");

#[cfg(feature = "testing")]
const INITIALIZER: Pubkey = pubkey!("BrQAbGdWQ9YUHmWWgKFdFe4miTURH71jkYFPXfaosqDv");

#[derive(AnchorSerialize, AnchorDeserialize)]
pub struct ProgramConfigInitArgs {
/// The authority that can configure the program config: change the treasury, etc.
pub authority: Pubkey,
/// The fee that is charged for creating a new multisig.
pub multisig_creation_fee: u64,
/// The treasury where the creation fee is transferred to.
pub treasury: Pubkey,
}

#[derive(Accounts)]
pub struct ProgramConfigInit<'info> {
#[account(
init,
payer = initializer,
space = 8 + ProgramConfig::INIT_SPACE,
seeds = [SEED_PREFIX, SEED_PROGRAM_CONFIG],
bump
)]
pub program_config: Account<'info, ProgramConfig>,

/// The hard-coded account that is used to initialize the program config once.
#[account(
mut,
address = INITIALIZER @ MultisigError::Unauthorized
)]
pub initializer: Signer<'info>,

pub system_program: Program<'info, System>,
}

impl ProgramConfigInit<'_> {
/// A one-time instruction that initializes the global program config.
pub fn program_config_init(ctx: Context<Self>, args: ProgramConfigInitArgs) -> Result<()> {
let program_config = &mut ctx.accounts.program_config;

program_config.authority = args.authority;
program_config.multisig_creation_fee = args.multisig_creation_fee;
program_config.treasury = args.treasury;

program_config.invariant()?;

Ok(())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl ConfigTransactionAccountsClose<'_> {
// Has to be either stale or in a terminal state.
let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;

#[allow(deprecated)]
let can_close = match proposal.status {
// Draft proposals can only be closed if stale,
// so they can't be activated anymore.
Expand Down Expand Up @@ -188,6 +189,7 @@ impl VaultTransactionAccountsClose<'_> {

let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;

#[allow(deprecated)]
let can_close = match proposal.status {
// Draft proposals can only be closed if stale,
// so they can't be activated anymore.
Expand Down Expand Up @@ -351,6 +353,7 @@ impl VaultBatchTransactionAccountClose<'_> {

// Batch transactions that are marked as executed within the batch can be closed,
// otherwise we need to check the proposal status.
#[allow(deprecated)]
let can_close = is_batch_transaction_executed
|| match proposal.status {
// Transactions of Draft proposals can only be closed if stale,
Expand Down Expand Up @@ -467,6 +470,7 @@ impl BatchAccountsClose<'_> {

let is_stale = proposal.transaction_index <= multisig.stale_transaction_index;

#[allow(deprecated)]
let can_close = match proposal.status {
// Draft proposals can only be closed if stale,
// so they can't be activated anymore.
Expand Down
Loading

0 comments on commit 0671efe

Please sign in to comment.