2021-12-02 07:28:12 -08:00
|
|
|
use crate::error::*;
|
2021-12-03 00:52:48 -08:00
|
|
|
use crate::state::*;
|
2021-12-02 07:28:12 -08:00
|
|
|
use anchor_lang::prelude::*;
|
2021-12-03 05:26:42 -08:00
|
|
|
use anchor_spl::token::{self, Token, TokenAccount};
|
2021-12-02 07:28:12 -08:00
|
|
|
|
|
|
|
#[derive(Accounts)]
|
2021-12-04 00:22:22 -08:00
|
|
|
pub struct Withdraw<'info> {
|
2021-12-08 23:22:22 -08:00
|
|
|
pub registrar: AccountLoader<'info, Registrar>,
|
2021-12-02 07:28:12 -08:00
|
|
|
|
2021-12-04 00:02:20 -08:00
|
|
|
// checking the PDA address it just an extra precaution,
|
|
|
|
// the other constraints must be exhaustive
|
|
|
|
#[account(
|
|
|
|
mut,
|
2021-12-04 00:22:22 -08:00
|
|
|
seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()],
|
2021-12-04 00:02:20 -08:00
|
|
|
bump = voter.load()?.voter_bump,
|
2021-12-04 01:54:33 -08:00
|
|
|
has_one = registrar,
|
|
|
|
has_one = voter_authority,
|
|
|
|
)]
|
2021-12-02 07:28:12 -08:00
|
|
|
pub voter: AccountLoader<'info, Voter>,
|
2021-12-04 01:54:33 -08:00
|
|
|
pub voter_authority: Signer<'info>,
|
2021-12-02 07:28:12 -08:00
|
|
|
|
2021-12-03 00:19:13 -08:00
|
|
|
/// The token_owner_record for the voter_authority. This is needed
|
|
|
|
/// to be able to forbid withdraws while the voter is engaged with
|
|
|
|
/// a vote or has an open proposal.
|
|
|
|
///
|
|
|
|
/// token_owner_record is validated in the instruction:
|
|
|
|
/// - owned by registrar.governance_program_id
|
|
|
|
/// - for the registrar.realm
|
|
|
|
/// - for the registrar.realm_governing_token_mint
|
|
|
|
/// - governing_token_owner is voter_authority
|
2021-12-02 07:28:12 -08:00
|
|
|
pub token_owner_record: UncheckedAccount<'info>,
|
|
|
|
|
2021-12-05 02:58:56 -08:00
|
|
|
/// Withdraws must update the voter weight record, to prevent a stale
|
|
|
|
/// record being used to vote after the withdraw.
|
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter_authority.key().as_ref()],
|
|
|
|
bump = voter.load()?.voter_weight_record_bump,
|
2021-12-08 23:22:22 -08:00
|
|
|
constraint = voter_weight_record.realm == registrar.load()?.realm,
|
2021-12-05 02:58:56 -08:00
|
|
|
constraint = voter_weight_record.governing_token_owner == voter.load()?.voter_authority,
|
2021-12-08 23:22:22 -08:00
|
|
|
constraint = voter_weight_record.governing_token_mint == registrar.load()?.realm_governing_token_mint,
|
2021-12-05 02:58:56 -08:00
|
|
|
)]
|
|
|
|
pub voter_weight_record: Account<'info, VoterWeightRecord>,
|
|
|
|
|
2021-12-02 07:28:12 -08:00
|
|
|
#[account(
|
|
|
|
mut,
|
|
|
|
associated_token::authority = registrar,
|
2021-12-03 05:26:42 -08:00
|
|
|
associated_token::mint = destination.mint,
|
2021-12-02 07:28:12 -08:00
|
|
|
)]
|
2021-12-02 10:30:41 -08:00
|
|
|
pub vault: Box<Account<'info, TokenAccount>>,
|
2021-12-02 07:28:12 -08:00
|
|
|
|
|
|
|
#[account(mut)]
|
|
|
|
pub destination: Box<Account<'info, TokenAccount>>,
|
|
|
|
|
|
|
|
pub token_program: Program<'info, Token>,
|
|
|
|
}
|
|
|
|
|
2021-12-04 00:22:22 -08:00
|
|
|
impl<'info> Withdraw<'info> {
|
2021-12-02 07:28:12 -08:00
|
|
|
pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> {
|
|
|
|
let program = self.token_program.to_account_info();
|
|
|
|
let accounts = token::Transfer {
|
2021-12-02 10:30:41 -08:00
|
|
|
from: self.vault.to_account_info(),
|
2021-12-02 07:28:12 -08:00
|
|
|
to: self.destination.to_account_info(),
|
|
|
|
authority: self.registrar.to_account_info(),
|
|
|
|
};
|
|
|
|
CpiContext::new(program, accounts)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Withdraws tokens from a deposit entry, if they are unlocked according
|
2021-12-03 00:19:13 -08:00
|
|
|
/// to the deposit's vesting schedule.
|
2021-12-02 07:28:12 -08:00
|
|
|
///
|
2021-12-03 00:19:13 -08:00
|
|
|
/// `deposit_entry_index`: The deposit entry to withdraw from.
|
2021-12-02 07:28:12 -08:00
|
|
|
/// `amount` is in units of the native currency being withdrawn.
|
2021-12-04 00:22:22 -08:00
|
|
|
pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) -> Result<()> {
|
2021-12-02 07:28:12 -08:00
|
|
|
// Load the accounts.
|
2021-12-08 23:22:22 -08:00
|
|
|
let registrar = &ctx.accounts.registrar.load()?;
|
2021-12-02 07:28:12 -08:00
|
|
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
|
|
|
|
|
|
|
// Governance may forbid withdraws, for example when engaged in a vote.
|
2021-12-03 03:54:20 -08:00
|
|
|
let token_owner_record = voter.load_token_owner_record(
|
|
|
|
&ctx.accounts.token_owner_record.to_account_info(),
|
|
|
|
registrar,
|
|
|
|
)?;
|
|
|
|
token_owner_record.assert_can_withdraw_governing_tokens()?;
|
2021-12-02 07:28:12 -08:00
|
|
|
|
|
|
|
// Get the deposit being withdrawn from.
|
|
|
|
let curr_ts = registrar.clock_unix_timestamp();
|
2021-12-03 03:46:37 -08:00
|
|
|
let deposit_entry = voter.active_deposit_mut(deposit_entry_index)?;
|
2021-12-02 07:28:12 -08:00
|
|
|
require!(
|
|
|
|
deposit_entry.amount_withdrawable(curr_ts) >= amount,
|
|
|
|
InsufficientVestedTokens
|
|
|
|
);
|
|
|
|
|
|
|
|
// Get the exchange rate for the token being withdrawn.
|
2021-12-03 11:36:42 -08:00
|
|
|
let mint_idx = registrar.voting_mint_config_index(ctx.accounts.destination.mint)?;
|
2021-12-02 07:28:12 -08:00
|
|
|
require!(
|
2021-12-03 11:36:42 -08:00
|
|
|
mint_idx == deposit_entry.voting_mint_config_idx as usize,
|
2021-12-02 07:28:12 -08:00
|
|
|
ErrorCode::InvalidMint
|
|
|
|
);
|
|
|
|
|
2021-12-03 12:13:32 -08:00
|
|
|
// Bookkeeping for withdrawn funds.
|
|
|
|
assert!(amount <= deposit_entry.amount_deposited_native);
|
2021-12-02 07:28:12 -08:00
|
|
|
deposit_entry.amount_deposited_native -= amount;
|
|
|
|
|
|
|
|
// Transfer the tokens to withdraw.
|
|
|
|
let registrar_seeds = registrar_seeds!(registrar);
|
|
|
|
token::transfer(
|
|
|
|
ctx.accounts.transfer_ctx().with_signer(&[registrar_seeds]),
|
|
|
|
amount,
|
|
|
|
)?;
|
|
|
|
|
2021-12-08 23:22:22 -08:00
|
|
|
let start_ts = deposit_entry.lockup.start_ts;
|
|
|
|
let end_ts = deposit_entry.lockup.end_ts;
|
2021-12-05 12:10:32 -08:00
|
|
|
msg!(
|
|
|
|
"Withdrew amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
|
|
|
amount,
|
|
|
|
deposit_entry_index,
|
|
|
|
deposit_entry.lockup.kind,
|
2021-12-08 23:22:22 -08:00
|
|
|
start_ts,
|
|
|
|
end_ts,
|
2021-12-05 12:10:32 -08:00
|
|
|
);
|
|
|
|
|
2021-12-05 02:58:56 -08:00
|
|
|
// Update the voter weight record
|
|
|
|
let record = &mut ctx.accounts.voter_weight_record;
|
|
|
|
record.voter_weight = voter.weight(®istrar)?;
|
|
|
|
record.voter_weight_expiry = Some(Clock::get()?.slot);
|
|
|
|
|
2021-12-02 07:28:12 -08:00
|
|
|
Ok(())
|
|
|
|
}
|