diff --git a/programs/autocrat/src/error.rs b/programs/autocrat/src/error.rs index 973d3450c..7a7465358 100644 --- a/programs/autocrat/src/error.rs +++ b/programs/autocrat/src/error.rs @@ -28,6 +28,8 @@ 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, #[msg("Question must have exactly 2 outcomes for binary futarchy")] QuestionMustBeBinary, } diff --git a/programs/autocrat/src/instructions/initialize_dao.rs b/programs/autocrat/src/instructions/initialize_dao.rs index ae741733f..65031a647 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 slots_per_proposal = slots_per_proposal.unwrap_or(THREE_DAYS_IN_SLOTS); + + require!( + 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(), @@ -53,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, 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 = [