Withdraw: Never block withdraws of tokens without voting power

Previously being engaged in a proposal would prohibit these withdraws.
This commit is contained in:
Christian Kamm 2022-02-09 09:45:57 +01:00
parent 997767a06e
commit 929c38f512
16 changed files with 97 additions and 8 deletions

View File

@ -87,12 +87,18 @@ pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) ->
let registrar = &ctx.accounts.registrar.load()?;
let voter = &mut ctx.accounts.voter.load_mut()?;
// Get the exchange rate for the token being withdrawn.
let mint_idx = registrar.voting_mint_config_index(ctx.accounts.destination.mint)?;
// Governance may forbid withdraws, for example when engaged in a vote.
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()?;
// Not applicable for tokens that don't contribute to voting power.
if registrar.voting_mints[mint_idx].grants_vote_weight() {
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()?;
}
// Get the deposit being withdrawn from.
let curr_ts = registrar.clock_unix_timestamp();
@ -101,9 +107,6 @@ pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) ->
deposit_entry.amount_unlocked(curr_ts) >= amount,
InsufficientUnlockedTokens
);
// Get the exchange rate for the token being withdrawn.
let mint_idx = registrar.voting_mint_config_index(ctx.accounts.destination.mint)?;
require!(
mint_idx == deposit_entry.voting_mint_config_idx as usize,
ErrorCode::InvalidMint

View File

@ -85,6 +85,15 @@ impl VotingMintConfig {
pub fn in_use(&self) -> bool {
self.mint != Pubkey::default()
}
/// Do tokens of this mint contribute to voting weight?
///
/// DAOs may configure mints without any vote weight contributions if they
/// want to use the grant / vesting / clawback functionality for non-voting
/// tokens like USDC.
pub fn grants_vote_weight(&self) -> bool {
self.unlocked_scaled_factor > 0 || self.lockup_scaled_factor > 0
}
}
unsafe impl Zeroable for VotingMintConfig {}

View File

@ -103,6 +103,7 @@ impl AddinCookie {
lockup_scaled_factor: f64,
lockup_saturation_secs: u64,
grant_authority: Option<Pubkey>,
other_mints: Option<&[Pubkey]>,
) -> VotingMintConfigCookie {
let deposit_mint = mint.pubkey.unwrap();
@ -129,6 +130,11 @@ impl AddinCookie {
deposit_mint,
false,
));
for mint in other_mints.unwrap_or(&[]) {
accounts.push(anchor_lang::prelude::AccountMeta::new_readonly(
*mint, false,
));
}
let instructions = vec![Instruction {
program_id: self.program_id,

View File

@ -46,6 +46,7 @@ async fn test_all_deposits() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -47,6 +47,7 @@ async fn test_basic() -> Result<(), TransportError> {
0.0,
1,
None,
None,
)
.await;
let mngo_voting_mint = context
@ -62,6 +63,7 @@ async fn test_basic() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -54,6 +54,7 @@ async fn test_clawback() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -79,6 +79,7 @@ async fn test_deposit_cliff() -> Result<(), TransportError> {
1.0,
2 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -79,6 +79,7 @@ async fn test_deposit_constant() -> Result<(), TransportError> {
1.0,
2 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -80,6 +80,7 @@ async fn test_deposit_daily_vesting() -> Result<(), TransportError> {
0.5,
60 * 60 * 60, // 60h / 2.5d
None,
None,
)
.await;

View File

@ -79,6 +79,7 @@ async fn test_deposit_monthly_vesting() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -85,6 +85,7 @@ async fn test_deposit_no_locking() -> Result<(), TransportError> {
10.0, // no locking, so has no effect
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -50,6 +50,7 @@ async fn test_grants() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
Some(grant_authority.pubkey()),
None,
)
.await;

View File

@ -70,6 +70,7 @@ async fn test_internal_transfer() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -89,6 +89,7 @@ async fn test_log_voter_info() -> Result<(), TransportError> {
1.0,
365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -70,6 +70,7 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;

View File

@ -28,6 +28,7 @@ async fn test_voting() -> Result<(), TransportError> {
let voter_authority = &context.users[1].key;
let voter2_authority = &context.users[2].key;
let voter_mngo = context.users[1].token_accounts[0];
let voter_usdc = context.users[1].token_accounts[1];
let token_owner_record = realm
.create_token_owner_record(voter_authority.pubkey(), &payer)
.await;
@ -50,6 +51,22 @@ async fn test_voting() -> Result<(), TransportError> {
0.0,
5 * 365 * 24 * 60 * 60,
None,
None,
)
.await;
let usdc_voting_mint = addin
.configure_voting_mint(
&registrar,
&realm_authority,
payer,
1,
&context.mints[1],
0,
0.0,
0.0,
5 * 365 * 24 * 60 * 60,
None,
Some(&[context.mints[0].pubkey.unwrap()]),
)
.await;
@ -177,6 +194,33 @@ async fn test_voting() -> Result<(), TransportError> {
.await
.unwrap();
addin
.create_deposit_entry(
&registrar,
&voter2,
voter2_authority,
&usdc_voting_mint,
1,
LockupKind::None,
None,
0,
false,
)
.await
.unwrap();
addin
.deposit(
&registrar,
&voter2,
&usdc_voting_mint,
voter_authority,
voter_usdc,
1,
1000,
)
.await
.unwrap();
realm
.cast_vote(
mint_governance.address,
@ -211,6 +255,20 @@ async fn test_voting() -> Result<(), TransportError> {
.await
.expect_err("could not withdraw");
// but can withdraw USDC
addin
.withdraw(
&registrar,
&voter2,
&usdc_voting_mint,
&voter2_authority,
voter_usdc,
1,
1,
)
.await
.unwrap();
realm
.relinquish_vote(
mint_governance.address,