|
| 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 | +} |
0 commit comments