From 4c69e4cf3f70e86b6314d22fcd3110057880079f Mon Sep 17 00:00:00 2001 From: brooksjeremy7 <92278656+brooksjeremy7@users.noreply.github.com> Date: Wed, 25 Jun 2025 11:47:02 -0700 Subject: [PATCH 1/4] fix: require proposal slots be greater than twap delay --- programs/autocrat/src/error.rs | 2 ++ programs/autocrat/src/instructions/initialize_dao.rs | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/programs/autocrat/src/error.rs b/programs/autocrat/src/error.rs index 87649abda..df8571d16 100644 --- a/programs/autocrat/src/error.rs +++ b/programs/autocrat/src/error.rs @@ -28,4 +28,6 @@ pub enum AutocratError { InsufficientLpTokenBalance, #[msg("The LP tokens passed in have less liquidity than the DAO's `min_quote_futarchic_liquidity` or `min_base_futachic_liquidity`")] InsufficientLpTokenLock, + #[msg("Proposal duration must be longer than TWAP start delay")] + ProposalDurationTooShort, } diff --git a/programs/autocrat/src/instructions/initialize_dao.rs b/programs/autocrat/src/instructions/initialize_dao.rs index ae741733f..51878de0c 100644 --- a/programs/autocrat/src/instructions/initialize_dao.rs +++ b/programs/autocrat/src/instructions/initialize_dao.rs @@ -46,6 +46,13 @@ impl InitializeDao<'_> { let (treasury, treasury_pda_bump) = Pubkey::find_program_address(&[dao.key().as_ref()], ctx.program_id); + let final_slots_per_proposal = slots_per_proposal.unwrap_or(THREE_DAYS_IN_SLOTS); + + require!( + final_slots_per_proposal > twap_start_delay_slots, + AutocratError::ProposalDurationTooShort + ); + dao.set_inner(Dao { token_mint: ctx.accounts.token_mint.key(), usdc_mint: ctx.accounts.usdc_mint.key(), From 79d2f348987d0a381aa11134e4b0399048b77a8a Mon Sep 17 00:00:00 2001 From: brooksjeremy7 <92278656+brooksjeremy7@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:14:42 -0700 Subject: [PATCH 2/4] fix: cleaned up naming of variables --- programs/autocrat/src/instructions/initialize_dao.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/autocrat/src/instructions/initialize_dao.rs b/programs/autocrat/src/instructions/initialize_dao.rs index 51878de0c..65031a647 100644 --- a/programs/autocrat/src/instructions/initialize_dao.rs +++ b/programs/autocrat/src/instructions/initialize_dao.rs @@ -46,10 +46,10 @@ impl InitializeDao<'_> { let (treasury, treasury_pda_bump) = Pubkey::find_program_address(&[dao.key().as_ref()], ctx.program_id); - let final_slots_per_proposal = slots_per_proposal.unwrap_or(THREE_DAYS_IN_SLOTS); + let slots_per_proposal = slots_per_proposal.unwrap_or(THREE_DAYS_IN_SLOTS); require!( - final_slots_per_proposal > twap_start_delay_slots, + slots_per_proposal > twap_start_delay_slots, AutocratError::ProposalDurationTooShort ); @@ -60,7 +60,7 @@ impl InitializeDao<'_> { treasury, proposal_count: 0, pass_threshold_bps: pass_threshold_bps.unwrap_or(DEFAULT_PASS_THRESHOLD_BPS), - slots_per_proposal: slots_per_proposal.unwrap_or(THREE_DAYS_IN_SLOTS), + slots_per_proposal, twap_initial_observation, twap_max_observation_change_per_update, twap_start_delay_slots, From 2b99fedff1a73ffed9c311004400ea0eded9c3d7 Mon Sep 17 00:00:00 2001 From: brooksjeremy7 <92278656+brooksjeremy7@users.noreply.github.com> Date: Wed, 25 Jun 2025 12:16:08 -0700 Subject: [PATCH 3/4] fix: cleaned up naming of variables --- programs/autocrat/src/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/autocrat/src/error.rs b/programs/autocrat/src/error.rs index df8571d16..7a7465358 100644 --- a/programs/autocrat/src/error.rs +++ b/programs/autocrat/src/error.rs @@ -30,4 +30,6 @@ pub enum AutocratError { InsufficientLpTokenLock, #[msg("Proposal duration must be longer than TWAP start delay")] ProposalDurationTooShort, + #[msg("Question must have exactly 2 outcomes for binary futarchy")] + QuestionMustBeBinary, } From 7726b676f99cce742e6d337518e38bca2fa7631f Mon Sep 17 00:00:00 2001 From: brooksjeremy7 <92278656+brooksjeremy7@users.noreply.github.com> Date: Wed, 25 Jun 2025 13:45:22 -0700 Subject: [PATCH 4/4] fix: added a test to check for the desired behavior --- sdk/src/v0.4/types/autocrat.ts | 20 ++++++++++++++++++ sdk/src/v0.4/types/conditional_vault.ts | 10 +++++++++ tests/autocrat/autocrat.ts | 28 +++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/sdk/src/v0.4/types/autocrat.ts b/sdk/src/v0.4/types/autocrat.ts index c250e7ac0..2b1b6e3dd 100644 --- a/sdk/src/v0.4/types/autocrat.ts +++ b/sdk/src/v0.4/types/autocrat.ts @@ -1001,6 +1001,16 @@ export type Autocrat = { code: 6011; name: "InsufficientLpTokenLock"; msg: "The LP tokens passed in have less liquidity than the DAO's `min_quote_futarchic_liquidity` or `min_base_futachic_liquidity`"; + }, + { + code: 6012; + name: "ProposalDurationTooShort"; + msg: "Proposal duration must be longer than TWAP start delay"; + }, + { + code: 6013; + name: "QuestionMustBeBinary"; + msg: "Question must have exactly 2 outcomes for binary futarchy"; } ]; }; @@ -2009,5 +2019,15 @@ export const IDL: Autocrat = { name: "InsufficientLpTokenLock", msg: "The LP tokens passed in have less liquidity than the DAO's `min_quote_futarchic_liquidity` or `min_base_futachic_liquidity`", }, + { + code: 6012, + name: "ProposalDurationTooShort", + msg: "Proposal duration must be longer than TWAP start delay", + }, + { + code: 6013, + name: "QuestionMustBeBinary", + msg: "Question must have exactly 2 outcomes for binary futarchy", + }, ], }; diff --git a/sdk/src/v0.4/types/conditional_vault.ts b/sdk/src/v0.4/types/conditional_vault.ts index a4c563cd3..fd83c4bf3 100644 --- a/sdk/src/v0.4/types/conditional_vault.ts +++ b/sdk/src/v0.4/types/conditional_vault.ts @@ -914,6 +914,11 @@ export type ConditionalVault = { code: 6015; name: "ConditionalTokenMetadataAlreadySet"; msg: "Conditional token metadata already set"; + }, + { + code: 6016; + name: "UnauthorizedConditionalTokenAccount"; + msg: "Conditional token account is not owned by the authority"; } ]; }; @@ -1835,5 +1840,10 @@ export const IDL: ConditionalVault = { name: "ConditionalTokenMetadataAlreadySet", msg: "Conditional token metadata already set", }, + { + code: 6016, + name: "UnauthorizedConditionalTokenAccount", + msg: "Conditional token account is not owned by the authority", + }, ], }; diff --git a/tests/autocrat/autocrat.ts b/tests/autocrat/autocrat.ts index d849414d6..15c13ab10 100644 --- a/tests/autocrat/autocrat.ts +++ b/tests/autocrat/autocrat.ts @@ -236,6 +236,34 @@ export default function suite() { }); }); + it("doesn't allow DAOs with proposal duration less than TWAP start delay", async function () { + const callbacks = expectError( + "ProposalDurationTooShort", + "DAO initialized despite slots_per_proposal being less than twap_start_delay_slots" + ); + + const daoKeypair = Keypair.generate(); + + await autocratClient + .initializeDaoIx( + daoKeypair, + META, + { + twapInitialObservation: new BN(1), + twapMaxObservationChangePerUpdate: new BN(1000), + twapStartDelaySlots: new BN(10000), + minQuoteFutarchicLiquidity: new BN(5), + minBaseFutarchicLiquidity: new BN(5000), + passThresholdBps: 300, + slotsPerProposal: new BN(5000), + }, + USDC + ) + .signers([payer]) + .rpc() + .then(callbacks[0], callbacks[1]); + }); + describe("#initialize_proposal", async function () { it("initializes proposals", async function () { const accounts = [