andromeda_std/common/
expiration.rs

1use cosmwasm_std::{ensure, BlockInfo, Env, Timestamp};
2use cw_utils::Expiration;
3
4use crate::error::ContractError;
5
6use super::Milliseconds;
7
8pub const MILLISECONDS_TO_NANOSECONDS_RATIO: u64 = 1_000_000;
9
10/// Creates a CosmWasm Expiration struct given a time in milliseconds
11/// # Arguments
12///
13/// * `time` - The expiration time in milliseconds since the Epoch
14///
15/// Returns a `cw_utils::Expiration::AtTime` struct with the given expiration time
16pub fn expiration_from_milliseconds(time: Milliseconds) -> Result<Expiration, ContractError> {
17    // Make sure that multiplying by above ratio does not exceed u64 limit
18    ensure!(
19        time.milliseconds() <= u64::MAX / MILLISECONDS_TO_NANOSECONDS_RATIO,
20        ContractError::InvalidExpirationTime {}
21    );
22
23    Ok(Expiration::AtTime(Timestamp::from_nanos(time.nanos())))
24}
25
26pub fn block_to_expiration(block: &BlockInfo, model: Expiration) -> Option<Expiration> {
27    match model {
28        Expiration::AtTime(_) => Some(Expiration::AtTime(block.time)),
29        Expiration::AtHeight(_) => Some(Expiration::AtHeight(block.height)),
30        Expiration::Never {} => None,
31    }
32}
33
34pub fn get_and_validate_start_time(
35    env: &Env,
36    start_time: Option<Milliseconds>,
37) -> Result<(Expiration, Milliseconds), ContractError> {
38    let current_time = Milliseconds::from_nanos(env.block.time.nanos()).milliseconds();
39
40    let start_expiration = if let Some(start_time) = start_time {
41        expiration_from_milliseconds(start_time)?
42    } else {
43        // Set as current time + 1 so that it isn't expired from the very start
44        expiration_from_milliseconds(Milliseconds(current_time + 1))?
45    };
46
47    // Validate start time
48    let block_time = block_to_expiration(&env.block, start_expiration).unwrap();
49    ensure!(
50        start_expiration.gt(&block_time),
51        ContractError::StartTimeInThePast {
52            current_time,
53            current_block: env.block.height,
54        }
55    );
56
57    Ok((start_expiration, Milliseconds(current_time)))
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_expiration_from_milliseconds() {
66        let time = u64::MAX;
67        let result = expiration_from_milliseconds(Milliseconds(time)).unwrap_err();
68        assert_eq!(result, ContractError::InvalidExpirationTime {});
69
70        let valid_time = 100;
71        let result = expiration_from_milliseconds(Milliseconds(valid_time)).unwrap();
72        assert_eq!(
73            Expiration::AtTime(Timestamp::from_nanos(100000000u64)),
74            result
75        )
76    }
77}