diff --git a/crates/aldabra-dao/src/builder/proposal_create.rs b/crates/aldabra-dao/src/builder/proposal_create.rs index d569e8c..ca583c6 100644 --- a/crates/aldabra-dao/src/builder/proposal_create.rs +++ b/crates/aldabra-dao/src/builder/proposal_create.rs @@ -610,8 +610,20 @@ pub fn build_unsigned_proposal_create( // `pvalidateProposalStartingTime` is satisfied because // `starting_time_slot ∈ [valid_from, invalid_after - 1]` by // construction. - staging = staging.valid_from_slot(args.starting_time_slot); - staging = staging.invalid_from_slot(args.starting_time_slot + max_width_slots); + // 2026-05-08: CENTER `starting_time_slot` inside the validity range + // (rather than putting it at the lower bound). Tiny test DAOs run on + // a 30-second create_proposal_time_range_max_width, and koios's tip + // endpoint lag vs. the actual node can swing ±60s. With + // valid_from = starting_time, the window only spans [now, now+30]. + // If chain is even slightly past `now` when the tx lands, the tx + // expires. Centering gives [now-15, now+15] of slack — same width, + // same validator-bound, but the chain-now-at-block-time can drift + // ±15s without missing the window. + let half_width_slots = max_width_slots / 2; + let valid_from = args.starting_time_slot.saturating_sub(half_width_slots); + let invalid_from = valid_from + max_width_slots; + staging = staging.valid_from_slot(valid_from); + staging = staging.invalid_from_slot(invalid_from); let proposer_pkh_arr: [u8; 28] = args .proposer_pkh