Skip to content

Commit 5473f3a

Browse files
authored
cancel proposal ix (#420)
1 parent 8058fbd commit 5473f3a

7 files changed

Lines changed: 922 additions & 0 deletions

File tree

programs/futarchy/src/events.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,15 @@ pub struct RemoveProposalEvent {
199199
pub admin: Pubkey,
200200
}
201201

202+
#[event]
203+
pub struct AdminCancelProposalEvent {
204+
pub common: CommonFields,
205+
pub proposal: Pubkey,
206+
pub dao: Pubkey,
207+
pub admin: Pubkey,
208+
pub post_amm_state: FutarchyAmm,
209+
}
210+
202211
#[event]
203212
pub struct CollectMeteoraDammFeesEvent {
204213
pub common: CommonFields,
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
use super::*;
2+
3+
use conditional_vault::{cpi::accounts::ResolveQuestion, ResolveQuestionArgs};
4+
use squads_multisig_program::program::SquadsMultisigProgram;
5+
6+
pub mod admin {
7+
use anchor_lang::prelude::declare_id;
8+
declare_id!("CWGawadYU8CzRVBecnJymNw97H7E3ndDinV5sMzesgY2");
9+
}
10+
11+
#[derive(Accounts)]
12+
#[event_cpi]
13+
pub struct AdminCancelProposal<'info> {
14+
#[account(
15+
mut, has_one = question, has_one = dao, has_one = squads_proposal,
16+
has_one = base_vault, has_one = quote_vault,
17+
has_one = pass_base_mint, has_one = pass_quote_mint,
18+
has_one = fail_base_mint, has_one = fail_quote_mint
19+
)]
20+
pub proposal: Box<Account<'info, Proposal>>,
21+
#[account(mut, has_one = squads_multisig)]
22+
pub dao: Box<Account<'info, Dao>>,
23+
#[account(mut)]
24+
pub question: Box<Account<'info, Question>>,
25+
/// CHECK: checked by squads multisig program
26+
#[account(mut)]
27+
pub squads_proposal: UncheckedAccount<'info>,
28+
/// CHECK: checked by squads multisig program
29+
pub squads_multisig: UncheckedAccount<'info>,
30+
pub squads_multisig_program: Program<'info, SquadsMultisigProgram>,
31+
32+
#[account(mut, associated_token::mint = proposal.pass_base_mint, associated_token::authority = dao)]
33+
pub amm_pass_base_vault: Box<Account<'info, TokenAccount>>,
34+
#[account(mut, associated_token::mint = proposal.pass_quote_mint, associated_token::authority = dao)]
35+
pub amm_pass_quote_vault: Box<Account<'info, TokenAccount>>,
36+
#[account(mut, associated_token::mint = proposal.fail_base_mint, associated_token::authority = dao)]
37+
pub amm_fail_base_vault: Box<Account<'info, TokenAccount>>,
38+
#[account(mut, associated_token::mint = proposal.fail_quote_mint, associated_token::authority = dao)]
39+
pub amm_fail_quote_vault: Box<Account<'info, TokenAccount>>,
40+
41+
#[account(mut, associated_token::mint = dao.base_mint, associated_token::authority = dao)]
42+
pub amm_base_vault: Account<'info, TokenAccount>,
43+
#[account(mut, associated_token::mint = dao.quote_mint, associated_token::authority = dao)]
44+
pub amm_quote_vault: Account<'info, TokenAccount>,
45+
46+
pub vault_program: Program<'info, ConditionalVaultProgram>,
47+
/// CHECK: checked by vault program
48+
pub vault_event_authority: UncheckedAccount<'info>,
49+
pub token_program: Program<'info, Token>,
50+
#[account(mut)]
51+
pub quote_vault: Box<Account<'info, ConditionalVault>>,
52+
#[account(mut, address = quote_vault.underlying_token_account)]
53+
pub quote_vault_underlying_token_account: Box<Account<'info, TokenAccount>>,
54+
#[account(mut)]
55+
pub pass_quote_mint: Box<Account<'info, Mint>>,
56+
#[account(mut)]
57+
pub fail_quote_mint: Box<Account<'info, Mint>>,
58+
#[account(mut)]
59+
pub pass_base_mint: Box<Account<'info, Mint>>,
60+
#[account(mut)]
61+
pub fail_base_mint: Box<Account<'info, Mint>>,
62+
#[account(mut)]
63+
pub base_vault: Box<Account<'info, ConditionalVault>>,
64+
#[account(mut, address = base_vault.underlying_token_account)]
65+
pub base_vault_underlying_token_account: Box<Account<'info, TokenAccount>>,
66+
67+
#[account(mut)]
68+
pub admin: Signer<'info>,
69+
}
70+
71+
impl AdminCancelProposal<'_> {
72+
pub fn validate(&self) -> Result<()> {
73+
#[cfg(feature = "production")]
74+
require_keys_eq!(self.admin.key(), admin::ID, FutarchyError::InvalidAdmin);
75+
76+
require!(
77+
self.proposal.state == ProposalState::Pending,
78+
FutarchyError::ProposalNotActive
79+
);
80+
81+
Ok(())
82+
}
83+
84+
pub fn handle(ctx: Context<Self>) -> Result<()> {
85+
let Self {
86+
proposal,
87+
dao,
88+
question,
89+
squads_proposal,
90+
squads_multisig,
91+
squads_multisig_program,
92+
vault_program,
93+
quote_vault,
94+
token_program,
95+
event_authority: _,
96+
vault_event_authority,
97+
program: _,
98+
quote_vault_underlying_token_account,
99+
pass_quote_mint,
100+
fail_quote_mint,
101+
amm_pass_quote_vault,
102+
amm_fail_quote_vault,
103+
pass_base_mint,
104+
fail_base_mint,
105+
amm_quote_vault,
106+
amm_pass_base_vault,
107+
amm_fail_base_vault,
108+
amm_base_vault,
109+
base_vault,
110+
base_vault_underlying_token_account,
111+
admin: _,
112+
} = ctx.accounts;
113+
114+
let squads_proposal_key = squads_proposal.key();
115+
let proposal_seeds = &[
116+
b"proposal",
117+
squads_proposal_key.as_ref(),
118+
&[proposal.pda_bump],
119+
];
120+
let proposal_signer = &[&proposal_seeds[..]];
121+
122+
let PoolState::Futarchy { fail, mut spot, .. } = dao.amm.state.to_owned() else {
123+
unreachable!();
124+
};
125+
126+
proposal.state = ProposalState::Failed;
127+
128+
let cpi_accounts = ResolveQuestion {
129+
question: question.to_account_info(),
130+
oracle: proposal.to_account_info(),
131+
event_authority: vault_event_authority.to_account_info(),
132+
program: vault_program.to_account_info(),
133+
};
134+
let cpi_ctx = CpiContext::new(vault_program.to_account_info(), cpi_accounts)
135+
.with_signer(proposal_signer);
136+
conditional_vault::cpi::resolve_question(
137+
cpi_ctx,
138+
ResolveQuestionArgs {
139+
payout_numerators: vec![1, 0],
140+
},
141+
)?;
142+
143+
let dao_nonce = &dao.nonce.to_le_bytes();
144+
let dao_creator_key = &dao.dao_creator.as_ref();
145+
let dao_seeds = &[b"dao".as_ref(), dao_creator_key, dao_nonce, &[dao.pda_bump]];
146+
let dao_signer = &[&dao_seeds[..]];
147+
148+
squads_multisig_program::cpi::proposal_reject(
149+
CpiContext::new_with_signer(
150+
squads_multisig_program.to_account_info(),
151+
squads_multisig_program::cpi::accounts::ProposalVote {
152+
proposal: squads_proposal.to_account_info(),
153+
multisig: squads_multisig.to_account_info(),
154+
member: dao.to_account_info(),
155+
},
156+
dao_signer,
157+
),
158+
squads_multisig_program::ProposalVoteArgs { memo: None },
159+
)?;
160+
161+
spot.base_reserves += fail.base_reserves;
162+
spot.quote_reserves += fail.quote_reserves;
163+
spot.base_protocol_fee_balance += fail.base_protocol_fee_balance;
164+
spot.quote_protocol_fee_balance += fail.quote_protocol_fee_balance;
165+
166+
let quote_cpi_context = CpiContext::new_with_signer(
167+
vault_program.to_account_info(),
168+
conditional_vault::cpi::accounts::InteractWithVault {
169+
question: question.to_account_info(),
170+
vault: quote_vault.to_account_info(),
171+
vault_underlying_token_account: quote_vault_underlying_token_account
172+
.to_account_info(),
173+
authority: dao.to_account_info(),
174+
user_underlying_token_account: amm_quote_vault.to_account_info(),
175+
event_authority: vault_event_authority.to_account_info(),
176+
program: vault_program.to_account_info(),
177+
token_program: token_program.to_account_info(),
178+
},
179+
dao_signer,
180+
)
181+
.with_remaining_accounts(vec![
182+
fail_quote_mint.to_account_info(),
183+
pass_quote_mint.to_account_info(),
184+
amm_fail_quote_vault.to_account_info(),
185+
amm_pass_quote_vault.to_account_info(),
186+
]);
187+
188+
conditional_vault::cpi::redeem_tokens(quote_cpi_context)?;
189+
190+
let base_cpi_context = CpiContext::new_with_signer(
191+
vault_program.to_account_info(),
192+
conditional_vault::cpi::accounts::InteractWithVault {
193+
question: question.to_account_info(),
194+
vault: base_vault.to_account_info(),
195+
vault_underlying_token_account: base_vault_underlying_token_account
196+
.to_account_info(),
197+
authority: dao.to_account_info(),
198+
user_underlying_token_account: amm_base_vault.to_account_info(),
199+
event_authority: vault_event_authority.to_account_info(),
200+
program: vault_program.to_account_info(),
201+
token_program: token_program.to_account_info(),
202+
},
203+
dao_signer,
204+
)
205+
.with_remaining_accounts(vec![
206+
fail_base_mint.to_account_info(),
207+
pass_base_mint.to_account_info(),
208+
amm_fail_base_vault.to_account_info(),
209+
amm_pass_base_vault.to_account_info(),
210+
]);
211+
212+
conditional_vault::cpi::redeem_tokens(base_cpi_context)?;
213+
214+
dao.amm.state = PoolState::Spot { spot };
215+
216+
dao.seq_num += 1;
217+
218+
let clock = Clock::get()?;
219+
220+
emit_cpi!(AdminCancelProposalEvent {
221+
common: CommonFields::new(&clock, dao.seq_num),
222+
proposal: proposal.key(),
223+
dao: dao.key(),
224+
admin: ctx.accounts.admin.key(),
225+
post_amm_state: dao.amm.clone(),
226+
});
227+
228+
Ok(())
229+
}
230+
}

programs/futarchy/src/instructions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use super::*;
22

33
pub mod admin_approve_execute_multisig_proposal;
4+
pub mod admin_cancel_proposal;
45
pub mod admin_remove_proposal;
56
pub mod collect_fees;
67
pub mod collect_meteora_damm_fees;
@@ -19,6 +20,7 @@ pub mod update_dao;
1920
pub mod withdraw_liquidity;
2021

2122
pub use admin_approve_execute_multisig_proposal::*;
23+
pub use admin_cancel_proposal::*;
2224
pub use admin_remove_proposal::*;
2325
pub use collect_fees::*;
2426
pub use collect_meteora_damm_fees::*;

programs/futarchy/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,11 @@ pub mod futarchy {
155155
AdminApproveExecuteMultisigProposal::handle(ctx)
156156
}
157157

158+
#[access_control(ctx.accounts.validate())]
159+
pub fn admin_cancel_proposal(ctx: Context<AdminCancelProposal>) -> Result<()> {
160+
AdminCancelProposal::handle(ctx)
161+
}
162+
158163
#[access_control(ctx.accounts.validate())]
159164
pub fn admin_remove_proposal(ctx: Context<AdminRemoveProposal>) -> Result<()> {
160165
AdminRemoveProposal::handle(ctx)

0 commit comments

Comments
 (0)