Skip to content
Draft
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
3 changes: 3 additions & 0 deletions Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ assign-permissionless-account = "yarn run tsx scripts/assignPermissionlessAccoun
create-v04-proposal = "yarn run tsx scripts/createV04Proposal.ts"
initialize-launch = "yarn run tsx scripts/initializeLaunch.ts"
test = "npx mocha --import=tsx --bail tests/main.test.ts"
execute-spending-limit = "yarn run tsx scripts/executeSpendingLimit.ts"
upgrade-dao = "yarn run tsx scripts/upgradeDao.ts"
convert = "yarn run tsx scripts/convertpk.ts"

[test]
startup_wait = 5000
Expand Down
2 changes: 2 additions & 0 deletions programs/autocrat/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ pub enum AutocratError {
QuestionMustBeBinary,
#[msg("Squads proposal must be in Draft status")]
InvalidSquadsProposalStatus,
#[msg("This Squads transaction should only contain calls to update spending limits")]
InvalidTransaction,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use super::*;

use anchor_lang::Discriminator;
use squads_multisig_program::program::SquadsMultisigProgram;

#[derive(Accounts)]
#[event_cpi]
pub struct ExecuteSpendingLimitChange<'info> {
#[account(
mut, has_one = dao, has_one = squads_proposal,
)]
pub proposal: Box<Account<'info, Proposal>>,
#[account(mut, has_one = squads_multisig)]
pub dao: Box<Account<'info, Dao>>,
/// CHECK: checked by squads multisig program
#[account(mut)]
pub squads_proposal: Account<'info, squads_multisig_program::Proposal>,
#[account(address = vault_transaction.multisig)]
pub squads_multisig: Account<'info, squads_multisig_program::Multisig>,
pub squads_multisig_program: Program<'info, SquadsMultisigProgram>,
pub vault_transaction: Account<'info, squads_multisig_program::VaultTransaction>,
}

impl<'info, 'c: 'info> ExecuteSpendingLimitChange<'info> {
pub fn validate(&self) -> Result<()> {
require_eq!(self.proposal.state, ProposalState::Passed);

Ok(())
}

pub fn handle(ctx: Context<'_, '_, 'c, 'info, Self>) -> Result<()> {
let Self {
proposal: _,
dao,
squads_proposal,
squads_multisig,
squads_multisig_program,
vault_transaction,
event_authority: _,
program: _,
} = ctx.accounts;

let message = &vault_transaction.message;

// it would be bad if we signed with the Dao and then they ran off and did something else
// with it. so we verify that the only instructions are calls to update spending limits on
// the squads program
let squads_program_key_index: u8 = message
.account_keys
.iter()
.position(|key| key == &squads_multisig_program.key())
.ok_or(error!(AutocratError::InvalidTransaction))?
.try_into()
.or_else(|_| Err(error!(AutocratError::InvalidTransaction)))?;

for instruction in message.instructions.iter() {
require_eq!(
instruction.program_id_index,
squads_program_key_index,
AutocratError::InvalidTransaction
);

let discriminator: [u8; 8] = instruction.data[0..8].try_into().unwrap();
if discriminator != squads_multisig_program::instruction::MultisigAddSpendingLimit::DISCRIMINATOR &&
discriminator != squads_multisig_program::instruction::MultisigRemoveSpendingLimit::DISCRIMINATOR {
return Err(error!(AutocratError::InvalidTransaction));
}
}

let dao_nonce = &dao.nonce.to_le_bytes();
let dao_creator_key = &dao.dao_creator.as_ref();
let dao_seeds = &[b"dao".as_ref(), dao_creator_key, dao_nonce, &[dao.pda_bump]];
let dao_signer = &[&dao_seeds[..]];

squads_multisig_program::cpi::vault_transaction_execute(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::VaultTransactionExecute {
multisig: squads_multisig.to_account_info(),
proposal: squads_proposal.to_account_info(),
transaction: vault_transaction.to_account_info(),
member: dao.to_account_info(),
},
dao_signer,
)
.with_remaining_accounts((&ctx.remaining_accounts).to_vec()),
)?;

Ok(())
}
}
109 changes: 109 additions & 0 deletions programs/autocrat/src/instructions/fix_omnipair_spending_limit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use super::*;

use squads_multisig_program::{program::SquadsMultisigProgram, Member, Multisig, MultisigAddSpendingLimitArgs, MultisigRemoveSpendingLimitArgs, Period, Permission, Permissions, SpendingLimit};

pub mod kollan_address {
use anchor_lang::prelude::declare_id;
declare_id!("tSTp6B6kE9o6ZaTmHm2ZwnJBBtgd3x112tapxFhmBEQ");
}

pub mod omnpair_dao {
use anchor_lang::prelude::declare_id;

declare_id!("B3AufDZCDtQN8JxZgJ5bSDZaiKCF4vtw7ynN9tuR9pXN");
}

pub mod omnipair_spending_limit {
use anchor_lang::prelude::declare_id;

declare_id!("5gn6mBnPqp4AQUXyanadwcse8eVqUNFxnc2zrKJV8kCn");
}

pub mod rakka_address {
use anchor_lang::prelude::declare_id;

declare_id!("5jRqFejxKHWMfR69dbYF2A9TnpnBPjz7iaRQS44imcMi");
}

#[derive(Accounts)]
pub struct FixOmnipairSpendingLimit<'info> {
#[account(mut, has_one = squads_multisig, address = omnpair_dao::id())]
pub dao: Box<Account<'info, Dao>>,
#[account(mut)]
pub squads_multisig: Account<'info, Multisig>,
pub squads_multisig_program: Program<'info, SquadsMultisigProgram>,
#[account(mut)]
pub rent_payer: Signer<'info>,
pub kollan: Signer<'info>,
#[account(mut, address = omnipair_spending_limit::id())]
pub omnipair_spending_limit: Account<'info, SpendingLimit>,
pub system_program: Program<'info, System>,
}

impl FixOmnipairSpendingLimit<'_> {
pub fn validate(&self) -> Result<()> {
// #[cfg(feature = "production")]
require_eq!(self.kollan.key(), kollan_address::id());

Ok(())
}

pub fn handle(ctx: Context<Self>) -> Result<()> {
let Self {
dao,
squads_multisig,
squads_multisig_program,
rent_payer,
kollan: _,
system_program,
omnipair_spending_limit,
} = ctx.accounts;

let dao_nonce = &dao.nonce.to_le_bytes();
let dao_creator_key = &dao.dao_creator.as_ref();
let dao_seeds = &[b"dao".as_ref(), dao_creator_key, dao_nonce, &[dao.pda_bump]];
let dao_signer = &[&dao_seeds[..]];

squads_multisig_program::cpi::multisig_remove_spending_limit(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigRemoveSpendingLimit {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
spending_limit: omnipair_spending_limit.to_account_info(),
rent_collector: rent_payer.to_account_info(),
},
dao_signer,
),
MultisigRemoveSpendingLimitArgs {
memo: None,
}
)?;

squads_multisig_program::cpi::multisig_add_spending_limit(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigAddSpendingLimit {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
spending_limit: omnipair_spending_limit.to_account_info(),
rent_payer: rent_payer.to_account_info(),
system_program: system_program.to_account_info(),
},
dao_signer,
),
MultisigAddSpendingLimitArgs {
memo: None,
create_key: dao.key(),
vault_index: 0,
mint: dao.quote_mint,
amount: 50_000 * 1_000_000,
period: Period::Month,
members: vec![rakka_address::id()],
destinations: vec![],
}
)?;

Ok(())
}
}
6 changes: 6 additions & 0 deletions programs/autocrat/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,14 @@ pub mod finalize_proposal;
pub mod initialize_dao;
pub mod initialize_proposal;
pub mod update_dao;
pub mod execute_spending_limit_change;
pub mod upgrade_multisig_dao;
pub mod fix_omnipair_spending_limit;

pub use finalize_proposal::*;
pub use initialize_dao::*;
pub use initialize_proposal::*;
pub use update_dao::*;
pub use execute_spending_limit_change::*;
pub use upgrade_multisig_dao::*;
pub use fix_omnipair_spending_limit::*;
122 changes: 122 additions & 0 deletions programs/autocrat/src/instructions/upgrade_multisig_dao.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
use super::*;

use squads_multisig_program::{program::SquadsMultisigProgram, Member, MultisigAddMemberArgs, MultisigRemoveMemberArgs, Permission, Permissions, Multisig};

pub mod kollan_address {
use anchor_lang::prelude::declare_id;
declare_id!("tSTp6B6kE9o6ZaTmHm2ZwnJBBtgd3x112tapxFhmBEQ");
}

#[derive(Accounts)]
pub struct UpgradeMultisigDao<'info> {
#[account(mut, has_one = squads_multisig)]
pub dao: Box<Account<'info, Dao>>,
#[account(mut)]
pub squads_multisig: Account<'info, Multisig>,
pub squads_multisig_program: Program<'info, SquadsMultisigProgram>,
#[account(mut)]
pub rent_payer: Signer<'info>,
pub system_program: Program<'info, System>,
pub kollan: Signer<'info>,
}

impl UpgradeMultisigDao<'_> {
pub fn validate(&self) -> Result<()> {
// #[cfg(feature = "production")]
require_eq!(self.kollan.key(), kollan_address::id());

Ok(())
}

pub fn handle(ctx: Context<Self>) -> Result<()> {
let Self {
dao,
squads_multisig,
squads_multisig_program,
rent_payer,
system_program,
kollan: _,
} = ctx.accounts;

let dao_nonce = &dao.nonce.to_le_bytes();
let dao_creator_key = &dao.dao_creator.as_ref();
let dao_seeds = &[b"dao".as_ref(), dao_creator_key, dao_nonce, &[dao.pda_bump]];
let dao_signer = &[&dao_seeds[..]];

squads_multisig_program::cpi::multisig_add_member(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigConfig {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
rent_payer: Some(rent_payer.to_account_info()),
system_program: Some(system_program.to_account_info()),
},
dao_signer,
),
MultisigAddMemberArgs {
new_member: Member {
key: system_program.key(),
permissions: Permissions::from_vec(&[Permission::Vote, Permission::Execute]),
},
memo: None,
}
)?;

squads_multisig_program::cpi::multisig_remove_member(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigConfig {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
rent_payer: Some(rent_payer.to_account_info()),
system_program: Some(system_program.to_account_info()),
},
dao_signer,
),
MultisigRemoveMemberArgs {
old_member: dao.key(),
memo: None,
}
)?;

squads_multisig_program::cpi::multisig_add_member(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigConfig {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
rent_payer: Some(rent_payer.to_account_info()),
system_program: Some(system_program.to_account_info()),
},
dao_signer,
),
MultisigAddMemberArgs {
new_member: Member {
key: dao.key(),
permissions: Permissions::from_vec(&[Permission::Vote, Permission::Execute]),
},
memo: None,
}
)?;

squads_multisig_program::cpi::multisig_remove_member(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::MultisigConfig {
multisig: squads_multisig.to_account_info(),
config_authority: dao.to_account_info(),
rent_payer: Some(rent_payer.to_account_info()),
system_program: Some(system_program.to_account_info()),
},
dao_signer,
),
MultisigRemoveMemberArgs {
old_member: system_program.key(),
memo: None,
}
)?;

Ok(())
}
}
17 changes: 17 additions & 0 deletions programs/autocrat/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,21 @@ pub mod autocrat {
pub fn update_dao(ctx: Context<UpdateDao>, dao_params: UpdateDaoParams) -> Result<()> {
UpdateDao::handle(ctx, dao_params)
}

#[access_control(ctx.accounts.validate())]
pub fn execute_spending_limit_change<'c: 'info, 'info>(
ctx: Context<'_, '_, 'c, 'info, ExecuteSpendingLimitChange<'info>>,
) -> Result<()> {
ExecuteSpendingLimitChange::handle(ctx)
}

#[access_control(ctx.accounts.validate())]
pub fn upgrade_multisig_dao(ctx: Context<UpgradeMultisigDao>) -> Result<()> {
UpgradeMultisigDao::handle(ctx)
}

#[access_control(ctx.accounts.validate())]
pub fn fix_omnipair_spending_limit(ctx: Context<FixOmnipairSpendingLimit>) -> Result<()> {
FixOmnipairSpendingLimit::handle(ctx)
}
}
Loading