Skip to content

Commit 1795a8d

Browse files
authored
Continuation fixes (#441)
* allow init of price_based performance package with min unlock start in past * ensure dao's CURRENT proposal duration in seconds is applied to launched proposal's duration * prevent initialization of launch that has multiple same spending limit members, or no spending limit members * handle single aggregator wrap in price based performance package unlocks * add proper wrapping arithmetic instead of saturating to TWAP aggregator calculations and projections * ppv2 - futarchy oracle - add check for at least one oracle update after twap_start_timestamp * mark squads vault as writable signer to match Squads SDK payer semantics * remove event_cpi * launchpad v6 & v7 - additional constraints * launchpad v6 - proper time boundary * ppv2 - proper staleness checks for change requests * remove proposer_type in favor of direct comparison * ppv2 - add test for stale change request (targets old PP) * remove unused accounts * remove mut from accounts that are not mutated * nit - update comment with correct amount of days * ppv2 - remove start_value from CliffLinear reward function * ppv2 - use specific require macros where possible * bump sdk, release * unstake_from_proposal should include init_if_needed for staker base account * bump sdk version
1 parent 7ab944a commit 1795a8d

54 files changed

Lines changed: 664 additions & 509 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

programs/bid_wall/src/instructions/close_bid_wall.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ pub struct CloseBidWall<'info> {
1919
)]
2020
pub bid_wall: Account<'info, BidWall>,
2121

22-
#[account(mut)]
23-
pub payer: Signer<'info>,
24-
2522
/// CHECK: used for constraints
2623
#[account(mut, address = bid_wall.authority)]
2724
pub authority: UncheckedAccount<'info>,

programs/bid_wall/src/instructions/collect_fees.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ pub struct CollectFees<'info> {
3434
pub quote_mint: Account<'info, Mint>,
3535

3636
pub token_program: Program<'info, Token>,
37-
pub system_program: Program<'info, System>,
3837
}
3938

4039
impl CollectFees<'_> {

programs/bid_wall/src/instructions/sell_tokens.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ pub struct SellTokens<'info> {
2020
#[account(mut, has_one = base_mint)]
2121
pub bid_wall: Account<'info, BidWall>,
2222

23-
#[account(mut)]
2423
pub user: Signer<'info>,
2524

2625
#[account(mut, associated_token::mint = base_mint, associated_token::authority = user)]
@@ -46,7 +45,6 @@ pub struct SellTokens<'info> {
4645
pub quote_mint: Account<'info, Mint>,
4746

4847
pub token_program: Program<'info, Token>,
49-
pub system_program: Program<'info, System>,
5048
}
5149

5250
// User sells tokens into the bid wall at the initial NAV per token, adjusted for treasury balance changes.

programs/futarchy/src/instructions/admin_cancel_proposal.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ pub struct AdminCancelProposal<'info> {
6464
#[account(mut, address = base_vault.underlying_token_account)]
6565
pub base_vault_underlying_token_account: Box<Account<'info, TokenAccount>>,
6666

67-
#[account(mut)]
6867
pub admin: Signer<'info>,
6968
}
7069

programs/futarchy/src/instructions/collect_meteora_damm_fees.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ pub struct CollectMeteoraDammFees<'info> {
4242
#[account(mut, seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig_program::SEED_MULTISIG, dao.key().as_ref()], bump, seeds::program = squads_program)]
4343
pub squads_multisig: Account<'info, squads_multisig_program::Multisig>,
4444
/// CHECK: signer for the squads transaction, checked by squads program
45-
#[account(seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig.key().as_ref(), squads_multisig_program::SEED_VAULT, 0_u8.to_le_bytes().as_ref()], bump, seeds::program = squads_program)]
45+
#[account(mut, seeds = [squads_multisig_program::SEED_PREFIX, squads_multisig.key().as_ref(), squads_multisig_program::SEED_VAULT, 0_u8.to_le_bytes().as_ref()], bump, seeds::program = squads_program)]
4646
pub squads_multisig_vault: UncheckedAccount<'info>,
4747
/// CHECK: squads transaction, initialized by squads multisig program, checked by squads multisig program
4848
#[account(mut)]
@@ -411,9 +411,8 @@ fn compile_transaction_message(
411411
// Track account metadata: (is_signer, is_writable)
412412
let mut key_meta_map: BTreeMap<Pubkey, (bool, bool)> = BTreeMap::new();
413413

414-
// Add vault as a signer (it will sign the vault transaction)
415-
// Writability is determined by whether it appears as writable in instruction accounts
416-
key_meta_map.insert(*vault_key, (true, false));
414+
// Add vault as a writable signer (matches Squads SDK payer semantics)
415+
key_meta_map.insert(*vault_key, (true, true));
417416

418417
// Collect all accounts from instructions, merging their flags with OR
419418
for ix in instructions {
@@ -435,14 +434,14 @@ fn compile_transaction_message(
435434

436435
for (pubkey, (is_signer, is_writable)) in &key_meta_map {
437436
if *is_signer && *is_writable {
438-
writable_signers.push(*pubkey);
439-
} else if *is_signer {
440-
// Vault key should be first among readonly signers
437+
// Vault key should be first among writable signers (matches Squads SDK)
441438
if *pubkey == *vault_key {
442-
readonly_signers.insert(0, *pubkey);
439+
writable_signers.insert(0, *pubkey);
443440
} else {
444-
readonly_signers.push(*pubkey);
441+
writable_signers.push(*pubkey);
445442
}
443+
} else if *is_signer {
444+
readonly_signers.push(*pubkey);
446445
} else if *is_writable {
447446
writable_non_signers.push(*pubkey);
448447
} else {

programs/futarchy/src/instructions/execute_spending_limit_change.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use anchor_lang::Discriminator;
44
use squads_multisig_program::program::SquadsMultisigProgram;
55

66
#[derive(Accounts)]
7-
#[event_cpi]
87
pub struct ExecuteSpendingLimitChange<'info> {
98
#[account(
109
mut, has_one = dao, has_one = squads_proposal,
@@ -40,8 +39,6 @@ impl<'info, 'c: 'info> ExecuteSpendingLimitChange<'info> {
4039
squads_multisig,
4140
squads_multisig_program,
4241
vault_transaction,
43-
event_authority: _,
44-
program: _,
4542
} = ctx.accounts;
4643

4744
let message = &vault_transaction.message;

programs/futarchy/src/instructions/launch_proposal.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ impl LaunchProposal<'_> {
157157
// Update proposal state to Pending and set timestamp enqueued
158158
proposal.state = ProposalState::Pending;
159159
proposal.timestamp_enqueued = clock.unix_timestamp;
160+
// Additionally, set the duration once more in case it was updated since the proposal was created
161+
proposal.duration_in_seconds = dao.seconds_per_proposal;
160162

161163
dao.seq_num += 1;
162164

programs/futarchy/src/instructions/stake_to_proposal.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ pub struct StakeToProposal<'info> {
3737
#[account(mut)]
3838
pub payer: Signer<'info>,
3939
pub token_program: Program<'info, Token>,
40-
pub associated_token_program: Program<'info, AssociatedToken>,
4140
pub system_program: Program<'info, System>,
4241
}
4342

@@ -71,7 +70,6 @@ impl StakeToProposal<'_> {
7170
staker,
7271
payer: _,
7372
token_program,
74-
associated_token_program: _,
7573
system_program: _,
7674
event_authority: _,
7775
program: _,

programs/futarchy/src/instructions/unstake_from_proposal.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,26 @@ pub struct UnstakeFromProposalParams {
99
#[instruction(args: UnstakeFromProposalParams)]
1010
#[event_cpi]
1111
pub struct UnstakeFromProposal<'info> {
12-
#[account(mut)]
12+
#[account(
13+
mut,
14+
has_one = dao,
15+
)]
1316
pub proposal: Box<Account<'info, Proposal>>,
14-
#[account(mut)]
15-
pub dao: Box<Account<'info, Dao>>,
1617
#[account(
1718
mut,
18-
associated_token::mint = dao.base_mint,
19+
has_one = base_mint,
20+
)]
21+
pub dao: Box<Account<'info, Dao>>,
22+
#[account(
23+
init_if_needed,
24+
payer = staker,
25+
associated_token::mint = base_mint,
1926
associated_token::authority = staker,
2027
)]
2128
pub staker_base_account: Box<Account<'info, TokenAccount>>,
2229
#[account(
2330
mut,
24-
associated_token::mint = dao.base_mint,
31+
associated_token::mint = base_mint,
2532
associated_token::authority = proposal,
2633
)]
2734
pub proposal_base_account: Box<Account<'info, TokenAccount>>,
@@ -31,8 +38,12 @@ pub struct UnstakeFromProposal<'info> {
3138
bump = stake_account.bump,
3239
)]
3340
pub stake_account: Box<Account<'info, StakeAccount>>,
41+
#[account(address = dao.base_mint)]
42+
pub base_mint: Account<'info, Mint>,
43+
#[account(mut)]
3444
pub staker: Signer<'info>,
3545
pub token_program: Program<'info, Token>,
46+
pub system_program: Program<'info, System>,
3647
pub associated_token_program: Program<'info, AssociatedToken>,
3748
}
3849

@@ -61,9 +72,11 @@ impl UnstakeFromProposal<'_> {
6172
stake_account,
6273
staker,
6374
token_program,
64-
associated_token_program: _,
6575
event_authority: _,
6676
program: _,
77+
system_program: _,
78+
associated_token_program: _,
79+
base_mint: _,
6780
} = ctx.accounts;
6881

6982
let UnstakeFromProposalParams { amount } = params;

programs/futarchy/src/state/futarchy_amm.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ pub struct TwapOracle {
272272
///
273273
/// Assuming latest observations are as big as possible (u64::MAX * 1e12),
274274
/// we can store 18 million seconds worth of observations, which turns out to
275-
/// be ~208 days.
275+
/// be ~213 days.
276276
///
277277
/// Assuming that latest observations are 100x smaller than they could theoretically
278278
/// be, we can store ~57 years worth of them. Even this is a very
@@ -404,9 +404,9 @@ impl Pool {
404404
.try_into()
405405
.unwrap();
406406

407-
// if this saturates, the aggregator will wrap back to 0, so this value doesn't
408-
// really matter. we just can't panic.
409-
let weighted_observation = last_observation.saturating_mul(time_difference);
407+
// wrapping_mul ensures we don't panic in case of overflow
408+
// Theoretically, wrapping can occur, but it's astronomically unlikely
409+
let weighted_observation = last_observation.wrapping_mul(time_difference);
410410

411411
oracle.aggregator.wrapping_add(weighted_observation)
412412
};
@@ -462,7 +462,9 @@ impl Pool {
462462

463463
// include the final interval that hasn't been accumulated yet
464464
let final_interval = (current_timestamp - self.oracle.last_updated_timestamp) as u128;
465-
let final_contribution = self.oracle.last_observation.saturating_mul(final_interval);
465+
// wrapping_mul ensures we don't panic in case of overflow
466+
// Theoretically, wrapping can occur, but it's astronomically unlikely
467+
let final_contribution = self.oracle.last_observation.wrapping_mul(final_interval);
466468
let total_aggregator = self.oracle.aggregator.wrapping_add(final_contribution);
467469

468470
Ok(total_aggregator / seconds_passed)

0 commit comments

Comments
 (0)