Overview
CampaignStatusResponse.days_remaining (campaign/src/contract.rs lines 110–116, type in types.rs) returns an integer-day count computed by simple (end_time - now) / 86_400 truncation. Any deadline less than one full day away is reported as 0 ("less than a day remaining"), which a UI or dashboard is then forced to display either as "0 days" (confusing) or to re-engineer the precision client-side. Several test snapshots depend on this truncation, so the fix needs to either change the field semantics or layer in a separate sub-day view.
Evidence
// campaign/src/contract.rs (get_campaign_status)
let now = env.ledger().timestamp();
let days_remaining = if now < campaign.end_time {
((campaign.end_time - now) / 86_400) as i64 // truncates < 1 day to 0
} else {
-(((now - campaign.end_time) / 86_400) as i64)
};
CampaignStatusResponse { status, days_remaining }
Test snapshots in campaign/test_snapshots/get_campaign_status/ exercise values where this integer-day "0" is exactly the response — a fix that adds sub-day granularity will require concurrent snapshot updates. Adding hours_remaining alongside days_remaining is the only non-breaking option.
Impact
- Users checking "how long do I have until the deadline" see "0" for the entire final day, indistinguishable from "expired".
- UI layers that want sub-day urgency cannot read it from this API.
- Off-chain indexers that join
get_campaign_status to render timers must compute the granular value independently of the contract, defeating the point of the typed view.
Recommended Approach
Make the field sub-day-capable without breaking the existing typed i64 shape — add a parallel hours_remaining_since_days: u32 (clamped to [0, 23]) or, preferably, change the units so CampaignStatusResponse reports seconds_remaining: i64 and let UI floor-to-days. Second option requires a snapshot refresh; first is additive.
Option A (preferred, additive):
pub struct CampaignStatusResponse {
pub status: CampaignStatus,
pub days_remaining: i64, // existing semantics, retained for back-compat
pub hours_remaining: u32, // new: 0..=23 for positive deadlines; 0 for past deadlines
}
// campaign/src/contract.rs
let delta = (campaign.end_time as i64) - (now as i64);
let days_remaining = delta / 86_400;
let hours_remaining = if delta >= 0 { ((delta % 86_400) / 3_600) as u32 } else { 0 };
Option B (breaking replacement of days_remaining with seconds_remaining): cleaner long-term but requires updating all campaign/test_snapshots/ fixture files; track as a separate migration issue.
Acceptance Criteria
Affected Files
campaign/src/contract.rs
campaign/src/types.rs
campaign/test_snapshots/get_campaign_status/
docs/events.md (if event payload changes — likely not in Option A)
Overview
CampaignStatusResponse.days_remaining(campaign/src/contract.rs lines 110–116, type in types.rs) returns an integer-day count computed by simple(end_time - now) / 86_400truncation. Any deadline less than one full day away is reported as0("less than a day remaining"), which a UI or dashboard is then forced to display either as "0 days" (confusing) or to re-engineer the precision client-side. Several test snapshots depend on this truncation, so the fix needs to either change the field semantics or layer in a separate sub-day view.Evidence
Test snapshots in
campaign/test_snapshots/get_campaign_status/exercise values where this integer-day "0" is exactly the response — a fix that adds sub-day granularity will require concurrent snapshot updates. Addinghours_remainingalongsidedays_remainingis the only non-breaking option.Impact
get_campaign_statusto render timers must compute the granular value independently of the contract, defeating the point of the typed view.Recommended Approach
Make the field sub-day-capable without breaking the existing typed
i64shape — add a parallelhours_remaining_since_days: u32(clamped to [0, 23]) or, preferably, change the units soCampaignStatusResponsereportsseconds_remaining: i64and let UI floor-to-days. Second option requires a snapshot refresh; first is additive.Option A (preferred, additive):
Option B (breaking replacement of
days_remainingwithseconds_remaining): cleaner long-term but requires updating allcampaign/test_snapshots/fixture files; track as a separate migration issue.Acceptance Criteria
hours_remainingis added toCampaignStatusResponse, ordays_remainingis replaced withseconds_remaining(Option B)docs/events.mdand any UI references are updated to read the new fielddays_remainingstill distinguishable (deadline has passed) in both optionsAffected Files
campaign/src/contract.rscampaign/src/types.rscampaign/test_snapshots/get_campaign_status/docs/events.md(if event payload changes — likely not in Option A)