Skip to content
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
103 changes: 103 additions & 0 deletions programs/futarchy/src/instructions/admin_approve_multisig_proposal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use super::*;

mod admin {
use anchor_lang::prelude::declare_id;

// MetaDAO-controlled admin - cannot be a Squads signer because of reentrancy
declare_id!("CWGawadYU8CzRVBecnJymNw97H7E3ndDinV5sMzesgY2");
}

#[derive(Accounts)]
pub struct AdminApproveMultisigProposal<'info> {
#[account(mut, has_one = squads_multisig)]
pub dao: Account<'info, Dao>,
#[account(mut)]
pub admin: Signer<'info>,

#[account(
mut,
seeds = [
squads_multisig_program::SEED_PREFIX,
squads_multisig_program::SEED_MULTISIG,
dao.key().as_ref(),
],
bump,
seeds::program = squads_multisig_program
)]
pub squads_multisig: Account<'info, squads_multisig_program::Multisig>,

#[account(
mut,
seeds = [
squads_multisig_program::SEED_PREFIX,
squads_multisig.key().as_ref(),
squads_multisig_program::SEED_TRANSACTION,
squads_multisig_vault_transaction.index.to_le_bytes().as_ref(),
squads_multisig_program::SEED_PROPOSAL,
],
bump,
seeds::program = squads_multisig_program
)]
pub squads_multisig_proposal: Account<'info, squads_multisig_program::Proposal>,

#[account(
seeds = [
squads_multisig_program::SEED_PREFIX,
squads_multisig.key().as_ref(),
squads_multisig_program::SEED_TRANSACTION,
squads_multisig_vault_transaction.index.to_le_bytes().as_ref(),
],
bump,
seeds::program = squads_multisig_program
)]
pub squads_multisig_vault_transaction:
Account<'info, squads_multisig_program::VaultTransaction>,

pub squads_multisig_program:
Program<'info, squads_multisig_program::program::SquadsMultisigProgram>,
}

impl AdminApproveMultisigProposal<'_> {
pub fn validate(&self) -> Result<()> {
#[cfg(feature = "production")]
require_keys_eq!(self.admin.key(), admin::ID, FutarchyError::InvalidAdmin);

if !matches!(self.dao.amm.state, PoolState::Spot { .. }) {
return Err(FutarchyError::PoolNotInSpotState.into());
}

Ok(())
}

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

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

// Approve the proposal
squads_multisig_program::cpi::proposal_approve(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::ProposalVote {
proposal: squads_multisig_proposal.to_account_info(),
multisig: squads_multisig.to_account_info(),
member: dao.to_account_info(),
},
dao_signer,
),
squads_multisig_program::ProposalVoteArgs { memo: None },
)?;

Ok(())
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
use super::*;

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

// MetaDAO-controlled admin - cannot be a Squads signer because of reentrancy
declare_id!("CWGawadYU8CzRVBecnJymNw97H7E3ndDinV5sMzesgY2");
}

#[derive(Accounts)]
#[event_cpi]
pub struct AdminApproveExecuteMultisigProposal<'info> {
pub struct AdminExecuteMultisigProposal<'info> {
#[account(mut, has_one = squads_multisig)]
pub dao: Account<'info, Dao>,
#[account(mut)]
pub admin: Signer<'info>,

/// CHECK: checked by futarchy program
#[account(mut, seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig_program::SEED_MULTISIG, dao.key().as_ref()], bump, seeds::program = squads_multisig_program)]
#[account(
mut,
seeds = [
squads_multisig_program::SEED_PREFIX,
squads_multisig_program::SEED_MULTISIG,
dao.key().as_ref(),
],
bump,
seeds::program = squads_multisig_program
)]
pub squads_multisig: Account<'info, squads_multisig_program::Multisig>,
/// CHECK: squads proposal, initialized by squads multisig program, checked by squads multisig program

#[account(
mut,
seeds = [
Expand All @@ -32,7 +39,7 @@ pub struct AdminApproveExecuteMultisigProposal<'info> {
seeds::program = squads_multisig_program
)]
pub squads_multisig_proposal: Account<'info, squads_multisig_program::Proposal>,
/// CHECK: squads vault transaction, initialized by squads multisig program, checked by squads multisig program

#[account(
mut,
seeds = [
Expand All @@ -51,7 +58,7 @@ pub struct AdminApproveExecuteMultisigProposal<'info> {
Program<'info, squads_multisig_program::program::SquadsMultisigProgram>,
}

impl<'info, 'c: 'info> AdminApproveExecuteMultisigProposal<'info> {
impl<'info, 'c: 'info> AdminExecuteMultisigProposal<'info> {
pub fn validate(&self) -> Result<()> {
#[cfg(feature = "production")]
require_keys_eq!(self.admin.key(), admin::ID, FutarchyError::InvalidAdmin);
Expand All @@ -71,29 +78,13 @@ impl<'info, 'c: 'info> AdminApproveExecuteMultisigProposal<'info> {
squads_multisig_proposal,
squads_multisig_vault_transaction,
squads_multisig_program,
event_authority: _,
program: _,
} = ctx.accounts;

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

// Approve the proposal
squads_multisig_program::cpi::proposal_approve(
CpiContext::new_with_signer(
squads_multisig_program.to_account_info(),
squads_multisig_program::cpi::accounts::ProposalVote {
proposal: squads_multisig_proposal.to_account_info(),
multisig: squads_multisig.to_account_info(),
member: dao.to_account_info(),
},
dao_signer,
),
squads_multisig_program::ProposalVoteArgs { memo: None },
)?;

// Execute the vault transaction
squads_multisig_program::cpi::vault_transaction_execute(
CpiContext::new_with_signer(
Expand Down
6 changes: 4 additions & 2 deletions programs/futarchy/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::*;

pub mod admin_approve_execute_multisig_proposal;
pub mod admin_approve_multisig_proposal;
pub mod admin_cancel_proposal;
pub mod admin_execute_multisig_proposal;
pub mod admin_remove_proposal;
pub mod collect_fees;
pub mod collect_meteora_damm_fees;
Expand All @@ -19,8 +20,9 @@ pub mod unstake_from_proposal;
pub mod update_dao;
pub mod withdraw_liquidity;

pub use admin_approve_execute_multisig_proposal::*;
pub use admin_approve_multisig_proposal::*;
pub use admin_cancel_proposal::*;
pub use admin_execute_multisig_proposal::*;
pub use admin_remove_proposal::*;
pub use collect_fees::*;
pub use collect_meteora_damm_fees::*;
Expand Down
13 changes: 10 additions & 3 deletions programs/futarchy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,17 @@ pub mod futarchy {
}

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

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

#[access_control(ctx.accounts.validate())]
Expand Down
72 changes: 62 additions & 10 deletions sdk/src/v0.7/types/futarchy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@ export type Futarchy = {
args: [];
},
{
name: "adminApproveExecuteMultisigProposal";
name: "adminApproveMultisigProposal";
accounts: [
{
name: "dao";
Expand All @@ -1210,21 +1210,47 @@ export type Futarchy = {
},
{
name: "squadsMultisigVaultTransaction";
isMut: true;
isMut: false;
isSigner: false;
},
{
name: "squadsMultisigProgram";
isMut: false;
isSigner: false;
},
];
args: [];
},
{
name: "adminExecuteMultisigProposal";
accounts: [
{
name: "eventAuthority";
isMut: false;
name: "dao";
isMut: true;
isSigner: false;
},
{
name: "program";
name: "admin";
isMut: true;
isSigner: true;
},
{
name: "squadsMultisig";
isMut: true;
isSigner: false;
},
{
name: "squadsMultisigProposal";
isMut: true;
isSigner: false;
},
{
name: "squadsMultisigVaultTransaction";
isMut: true;
isSigner: false;
},
{
name: "squadsMultisigProgram";
isMut: false;
isSigner: false;
},
Expand Down Expand Up @@ -4449,7 +4475,7 @@ export const IDL: Futarchy = {
args: [],
},
{
name: "adminApproveExecuteMultisigProposal",
name: "adminApproveMultisigProposal",
accounts: [
{
name: "dao",
Expand All @@ -4473,21 +4499,47 @@ export const IDL: Futarchy = {
},
{
name: "squadsMultisigVaultTransaction",
isMut: true,
isMut: false,
isSigner: false,
},
{
name: "squadsMultisigProgram",
isMut: false,
isSigner: false,
},
],
args: [],
},
{
name: "adminExecuteMultisigProposal",
accounts: [
{
name: "eventAuthority",
isMut: false,
name: "dao",
isMut: true,
isSigner: false,
},
{
name: "program",
name: "admin",
isMut: true,
isSigner: true,
},
{
name: "squadsMultisig",
isMut: true,
isSigner: false,
},
{
name: "squadsMultisigProposal",
isMut: true,
isSigner: false,
},
{
name: "squadsMultisigVaultTransaction",
isMut: true,
isSigner: false,
},
{
name: "squadsMultisigProgram",
isMut: false,
isSigner: false,
},
Expand Down
6 changes: 4 additions & 2 deletions tests/futarchy/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import provideLiquidity from "./unit/provideLiquidity.test.js";
import executeSpendingLimitChange from "./unit/executeSpendingLimitChange.test.js";

import collectMeteoraDammFees from "./unit/collectMeteoraDammFees.test.js";
import adminApproveProposal from "./unit/adminApproveExecuteMultisigProposal.test.js";
import adminApproveMultisigProposal from "./unit/adminApproveMultisigProposal.test.js";
import adminExecuteMultisigProposal from "./unit/adminExecuteMultisigProposal.test.js";
import adminCancelProposal from "./unit/adminCancelProposal.test.js";
import adminRemoveProposal from "./unit/adminRemoveProposal.test.js";

Expand Down Expand Up @@ -60,7 +61,8 @@ export default function suite() {

describe("#collect_meteora_damm_fees", collectMeteoraDammFees);

describe("#admin_approve_proposal", adminApproveProposal);
describe("#admin_approve_multisig_proposal", adminApproveMultisigProposal);
describe("#admin_execute_multisig_proposal", adminExecuteMultisigProposal);
describe("#admin_cancel_proposal", adminCancelProposal);
describe("#admin_remove_proposal", adminRemoveProposal);
// describe("full proposal", fullProposal);
Expand Down
Loading
Loading