# Working with time

**URL:** <https://forum.midnight.network/t/working-with-time/698>\
**Category:** Technical Questions\
**Tags:** compact\
**Created:** [November 26, 2025, 1:13pm UTC](https://forum.midnight.network/t/working-with-time/698 "2025-11-26T13:13:58Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![A\_Kyras](https://avatars.discourse-cdn.com/v4/letter/a/73ab20/32.png) [@A\_Kyras](https://forum.midnight.network/u/A_Kyras)\
**Post date:** [November 26, 2025, 1:13pm UTC](https://forum.midnight.network/t/working-with-time/698/1 "2025-11-26T13:13:58Z")

</div>

For a smart contract, I need to know the current time. In compact documentation, I saw only circuits that compares current block time with some value.

#### Attempt 1: Witness-less ledger shredder

Based on that, I tried to create a `witness`-less implementation of `now` circuit, something like this:

```auto
pragma language_version >= 0.18.0 && < 0.19.0;

module Time {
  import CompactStandardLibrary;

  circuit powersOfTwo(): Vector<64, Uint<64>> {
    return [
      1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288,
      1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824,
      2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944,
      549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832,
      70368744177664, 140737488355328, 281474976710656, 562949953421312, 1125899906842624, 2251799813685248,
      4503599627370496, 9007199254740992, 18014398509481984, 36028797018963968, 72057594037927936, 144115188075855872,
      288230376151711744, 576460752303423488, 1152921504606846976, 2305843009213693952, 4611686018427387904,
      9223372036854775808
    ];
  }

  circuit reverseIndexes(): Vector<64, Uint<0..63>> {
    return [
      63, 62, 61, 60, 59, 58, 57, 56,
      55, 54, 53, 52, 51, 50, 49, 48,
      47, 46, 45, 44, 43, 42, 41, 40,
      39, 38, 37, 36, 35, 34, 33, 32,
      31, 30, 29, 28, 27, 26, 25, 24,
      23, 22, 21, 20, 19, 18, 17, 16,
      15, 14, 13, 12, 11, 10, 9, 8,
      7, 6, 5, 4, 3, 2, 1, 0
     ];
  }

  export circuit now(): Uint<64> {
    const pows = powersOfTwo();

    return fold((currentNow, i) => {
      const candidateNow = (currentNow + pows[i]) as Uint<64>;
      if (blockTimeGte(candidateNow)) {
        return candidateNow;
      } else {
        return currentNow;
      }
    }, 0 as Uint<64>, reverseIndexes());
  }
}

import CompactStandardLibrary;
import Time prefix Time_;

export ledger lastNow: Maybe<Uint<64>>;

constructor() {
  lastNow = none<Uint<64>>();
}

export circuit getLastNow(): Maybe<Uint<64>> {
  return lastNow;
}

export circuit getNow(): Uint<64> {
  const now = Time_now();
  lastNow = some<Uint<64>>(now);
  return now;
}

```

This contract behaves extremely weirdly. Calling circuit `getNow` **bricks** the deployed smart contract. The TX calling this circuit fails with this error:

```auto
{
  “circuitId”: “getNow”,
  “tx”: {“__wbg_ptr”: 1516976},
  “status”: “FailEntirely”,
  “txId”: “00000000f58378d4d3d06667a2d100b52cb1546bf4f89c20364c2b5f70a0400c70a4e61b”,
  “txHash”: “1691da3c5a4a457e00dc98b73c906d6657591a92de3608dc37d0fb718d44f556”,
  “blockHeight”: 1506,
  “blockHash”: “156f6ff6b3dd8356d9d4236df157eecfb4ab6aee3f4861403e9194dd9b7c621e”
}
    at submitCallTx (./node_modules/@midnight-ntwrk/midnight-js-contracts/src/submit-call-tx.ts:67:11)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Object.interact (./apps/contract-cli/src/commands/interact.ts:41:14)

```

And any subsequent interaction (like calling `getLastNow`) with deployed contract fails on error:

```auto
Unexpected length of input
    at __wbindgen_error_new (file:///./node_modules/@midnight-ntwrk/ledger/midnight_ledger_wasm_bg.js:7821:17)
    at wasm://wasm/0152566a:wasm-function[4615]:0x4fea6e
    at wasm://wasm/0152566a:wasm-function[2684]:0x421cc8
    at wasm://wasm/0152566a:wasm-function[4285]:0x4c1604
    at ZswapChainState.deserialize (file:///./node_modules/@midnight-ntwrk/ledger/midnight_ledger_wasm_bg.js:7183:26)
    at deserializeZswapState (./node_modules/@midnight-ntwrk/midnight-js-indexer-public-data-provider/src/indexer-public-data-provider.ts:93:19)
    at Object.queryZSwapAndContractState (./node_modules/@midnight-ntwrk/midnight-js-indexer-public-data-provider/src/indexer-public-data-provider.ts:385:13)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async getPublicStates (./node_modules/@midnight-ntwrk/midnight-js-contracts/src/get-states.ts:48:5)
    at async getStates (./node_modules/@midnight-ntwrk/midnight-js-contracts/src/get-states.ts:73:32)

```

#### Attempt 2: Witness mess

This did not work, so I tried using a witness to get time from a user, and validating it with the comparison methods. I got such module

```auto
pragma language_version >= 0.18.0 && < 0.19.0;

module Time {
  import CompactStandardLibrary;

  ledger timePrecision: Uint<64>;

  /**
  * Witness from client, producing current timestamp. This is not validated in any way, and should not be trusted.
  * For this reason use `now`.
  */
  export witness secondsFromUnixEpoch_unsafe(): Uint<64>;

  /**
  * Initialization for the module. This *must* be called in contract constructor, which want to use the Time module.
  */
  export circuit init(timePrecision_: Uint<64>): [] {
    timePrecision = disclose(timePrecision_);
  }

  /**
  * Get current timestamps, represented as number of seconds from unix epoch. This method is validating and disclosing
  * value of `secondsFromUnixEpoch`, and is safe to use in other contracts.
  */
  export circuit now(): Uint<64> {
    const ts = disclose(secondsFromUnixEpoch_unsafe());

    if (ts + timePrecision > 18446744073709551615) {
      if (timePrecision > ts) {
        assertCurrentTimeInRange(ts, ts);
      } else {
        assertCurrentTimeInRange(ts - timePrecision, ts);
      }
    } else {
      if (timePrecision > ts) {
        assertCurrentTimeInRange(ts, (ts + timePrecision) as Uint<64>);
      } else {
        assertCurrentTimeInRange(ts - timePrecision, (ts + timePrecision) as Uint<64>);
      }
    }

    return ts;
  }

  //===
  // Helpers
  circuit assertCurrentTimeInRange(lowerBound: Uint<64>, upperBound: Uint<64>): [] {
    assert(blockTimeGte(lowerBound), "Time: Provided timestamp is older than set precision in comparison to current timestamp");
    assert(blockTimeLte(upperBound), "Time: Provided timestamp is newer than set precision in comparison to current timestamp");
  }
}

```

Used JS witness:

```typescript
{
  secondsFromUnixEpoch_unsafe(context: WitnessContext<any, any>): [any, bigint] {
    return [context.privateState, BigInt(Math.ceil(new Date().getTime() / 1000))];
  }
}

```

As documentation says, that `blockTime*` methods are expecting ‘Returns true if the current block time is \* than the given value.’, does not says what the current value format is supposed to be 🤷 . Following docs, I found `kernel.blockTimeGreaterThan` method, which specifies that this expect value of seconds from the unix epoch, so I expect this to be also true for the circuits from `CompactStandardLibrary`. But no matter what, the transactions using these methods, always fails to prove, as time is never precise (used precision up to 8 hours long).

Can someone shed some light what am I doing wrong or whats might be going on?

---

<div class="post-metadata">

**Author:** ![A\_Kyras](https://avatars.discourse-cdn.com/v4/letter/a/73ab20/32.png) [@A\_Kyras](https://forum.midnight.network/u/A_Kyras)\
**Post date:** [November 27, 2025, 1:26pm UTC](https://forum.midnight.network/t/working-with-time/698/2 "2025-11-27T13:26:31Z")

</div>

# ⚠ Contract Bricking ⚠

After further experimentation, I pin-pointed, that the bug that causes the contract to brick up, is in the `kernel.blockTimeLessThan`. If the provided time is too close to the actual time, the bricking happens.

---

<div class="post-metadata">

**Author:** ![A\_Kyras](https://avatars.discourse-cdn.com/v4/letter/a/73ab20/32.png) [@A\_Kyras](https://forum.midnight.network/u/A_Kyras)\
**Post date:** [November 28, 2025, 10:57am UTC](https://forum.midnight.network/t/working-with-time/698/3 "2025-11-28T10:57:30Z")

</div>

# ⚠ The contracts are destructive ⚠

After further investigation. It is highly possible, that the bricking happening due to the kernel circuit is at chain state level or it is bug in a node. I setup script to print out the raw zswap chainState. This is how it looks like before the destructive call:

```auto
{
  zswapState: '00040003003d0000000000000000000000230000000220b97e12e9b1581600aab70f86b1e79d62097ff42e1011799ff65659e970a63953060000000023000000022054ed210db750224a2f052bc88c55752edd77920b579fb91d1f007fcb877c375605000000002300000002205ffda519e042cbbf8520574bc75398f3bab0c7544c81407d38108b1c67bcf96802000000000200000001020200000020000000ba60c72d5f49e54dce7765cf0432408521774d60a7dd18cd51341038e96c95d52000000081540ddc172ee212bc36b7e75f7b49cbd5feaf8686065cca7a3b0a49b7fa75c5230000000320e495a4df8ea5fac581c0dfe71d2eddbfe89ad54dcc8ea26243f75e509471864d0300000000020000000103020000002000000015474177f747d5c2a21e2adfdd853593afeff4959abbf61e9408c18de32c15702000000017aaf21c90c61e4a00dcb2918d7f041954dcdab4518289cf004a28992d6914522300000003201b1c80194d697509f7c0aae9ec50e6f6a5fb83e10eb21aa2d32d6c0342bd822904000000000200000001040200000020000000731ad8054f7f5753fbdf6ef913db72f9f0661c617b4e9f44aeab2e1b772c012820000000dd941aaa6ccb9e720039dab35be518871223333fb202ebc5d9f52cbdbe5d4d6923000000032039957e85473ad8d047acd585d09873066bb379f4acc336032b5a6227f07e611b05020000002000000032908e4d379ed8fa4df61217c3d6105df80b725d838962c4267c70e926683c192000000018503237379d0523d09691931fe171fbc99d34ae283353ea59f51d7645ae455e230000000320dece7f825adcd65e21e2688062c29212433ff9417e0975eb252a1f344d39fb430602000000200000000522ecfe6f90140ad8b2ff0d754538acacdfae190e1c3a029fd1feb2d618813f20000000d621206f4dabb6f2b88703ae5f30a367afadc0085fa7251cbf078a89fc6c93b223000000032099bddbcccbb228a74156d5f46588b96d08303719b7052ce09175b1f17727ce3c070000000002000000010702000000200000009d55faf5731d786b2bdac69fd12ae754e643e8ae1926afb44d3ca3f8aea2023c20000000bca9fa41ce356548f89d4c640a876ebd3dad078757b4ca470602361f0398bb2c23000000032027bcc8f13b5de539245907be245aab03f19b46876667f0a4f6bdd4b311aa835b08000000000200000001080200000020000000908d101e4c952d51fcdb11ef4f8ad4f78f89aadc3c2c4827862c0e7b358497a220000000ee2f1397df461f01d4dcd0bdf25c8578cc122a333c5dcfeb739e19f9003acdc4230000000320ec9095add657cf496a30c9b770ddecf9451b1fa209c40192e060552cf6d43d4c09000000000200000001090200000020000000cb2a9499f892cf9c2621ec284e56bc0d23ad7dc4df2ac8f76197f49c7b9680d520000000dc4271fb15a60a2f1b22560103a4da49a33d418e5d6c1282b86119bca6ae00f6230000000320fb83a945f0de5f2c421cff04013cd585405a17c68385dd5e407f6004136be3550a0000000002000000010a0200000020000000d88b4f1f4db716d5b1865f67d92c18562b0f22e32ee9340a4c3fa462e5aaf1a0200000002b5c2739f14f754263856759eee457022cb247d1ea9c07d568bf86902d5252f8230000000320e3f7646e16d8c9de5b98adc90c15cc6175451d0f7f8e38e1ff4760f9bec3a5010b0000000002000000010b0200000020000000d6e2a692017c4ad407d7cda0a39225625aeb13c6c0cbd62339ac169251f458b6200000009c067552d1eadd2689db8e49ceffc43e5460e0a31aacc39e23e8268e4c0ab52f230000000320201376ae53862d89dce5acbae0c79062836087f8eaeab30128ce7956e4353d2d0c0000000002000000010c020000002000000052e4b6f57cb6e6808f0986bc00ffae119ef5ba9c84871b514dd418f90f1972f4200000008b82106b530fc5c269b4b1cbcc7969e887bea081d3058b84ed688edbfd3930e6230000000320be23e39f76e36d27514c614376fb61d14ec03def0ab4ebd2faae4cec6054a6540d0000000002000000010d0200000020000000ec3dccb62dc1936a11535b3d3a08cb898180ba5750e38f9503acf0cdf187a83820000000e0e80869246080e5c667d4070cc1d1077643e90bdfcc8064608c0b8f5235338823000000032002c3debba80cbea0483eb77889e598753f5b5966d598f8c68603bca26b905e680e0000000002000000010e020000002000000055d7e1b2f3a1f596b53fb9e10d8bb316c0520e602c1359e1da4989d7b2ed25e220000000e423a843a7a250cedacb59a49c9085acdd1dd2f370327b5b84c1cd1acd915034230000000320b0c243c799289ca915763b0d9fec66832d66e7dc5ec900aae29441c8830a48650f0000000002000000010f020000002000000095c054e9704dc2bc19d60cd963f146743a03e6ce4c4815067d6f9a679eb6d4ea20000000ddc1b3cf242ca9397fff0b4e1675137ad620b44ee57b65771806e06bbc5ed98d23000000032037e57e68cc88f86875f0f3b5d979ea3d6ecd8dffd25638d31f26dc7a61e4c52110000000000200000001100200000020000000d0dd90ad6cb9802f86bed11a0151f109ba1ef89d3fcd8e7baf23eeeb6259e33120000000c2f1431314e3aa568965deda70850fe3c0dc481e25a6de230cc01d483024bc792300000003208ca7384332f949bedea00aae861a03849d9ea9e9c952f08949b869628c73f60311000000000200000001110200000020000000069be279fb1ef4255298b3e9b53fb1e26cd7d08164b91728df6ad842a61c144320000000e3a899273c6a1ba652c820ca016036dcdb935f2b77c9072e5d062dd26867c406230000000320269bc0a75b8b551090cc8be8fb26d78389d3ab0a722f0a6a38a7208c1569883f12000000000200000001120200000020000000840881b819b1c4f8adafa3f10422f037ce9d835a2a0ad67eee7188cc7eb59f0f20000000579a7a03f9e2a61dc7fdb55bfd70e8cfe294643aea9cfcb249573bece7deb9da230000000320bda976cd33bd6af267ea0aa21e1ae3922cf75f75ee4d91fe86f576bb4f0e8d3313000000000200000001130200000020000000201dec481fc8f1cef5bd2a12aaccdd97e0c60fed2b82f90972794a1a7c310876200000009e0bca7965a27d2041319bcf45e60b0b6e6fa1697ec627dc1ae7e18d85302b3c230000000320fefeb88c4118fce4f713e019ff02cd573c5d2b427513d88e36140c82120ab650140000000002000000011402000000200000005bf1bb0c0b4033033386c6250a57e069060950a2ccbc08c93f456f8eb3e34b4820000000f7bbb80e6123ee311dd47d32f2c6190b481ee756d84ff6182f955ff69dbef813230000000320e3f365867fe185ab75c0a03a5ce20ba9c3326695221c29dfaa05ded0d4102b47150000000002000000011502000000200000002b7ffc620aa84374b50b0b145d975f4ec82e1466be4b98af4f6ab3501a8d9091200000009678187f3f5b9f344579a2c32e8e8f16f5fa83c2e50078c3c2612a22ae119d6d23000000032052dc408468c79c82fbcb2611cbbe851db78cbcd38c5ca12c9515bcf3e8e6da66160000000002000000011602000000200000003bc6aba1a1c5aaa814091e2a6960b7f7390798b8e9a80c7dfd1cc3036a0a3dd92000000049a19ad7d1ede7401d582459923a9d30277703889ef14c9326b90939ec1fe293230000000320e0655ca2b76cf71ee3282bb76466f8192eec0227d97a0f73d2ff6a23f981dd0b17000000000200000001170200000020000000802117e07a8b36dc7fdae5a68fef60f1dee26f7b00e0caeb2252ef944ea8f26420000000d6fce1c378a689820db92774fa8612ff880910aab47232e2db0c1742eaa0f12623000000032095345b8d04b22767fbb08530267bbb8cf3c11101efbb98041026dfd5d9402e6918000000000200000001180200000020000000c32b0cbb7c231b6714657da7d0f65cfc262d906d5c0c5681be01eac9c8fae5dd20000000e765678d4a674e065b2b5b3e23a6d20ffb06ebe678aea84f585638d59e845101230000000320a34e20444b6e663cf192db991566f94a252e2ba181f57cea7d6f626bb69bfd2a19000000000200000001190200000020000000dddaf5dcb0e18e1338b79139d89ff575e994d170814b930591ca6e36b4cd81f4200000009d62169a6dbc22827b20be2691d81479ee3ff93966425ae0c6968d72caec48162300000003200fce729a2784eb872147af143437b4bd0aa16bfa1f1183411dbf50b8dd38fa031a0000000002000000011a0200000020000000b529d68bf2fb4895fc0df32feb9bc0ae0d5efd5fc37a6ae7e0f698fd9f9983f020000000a806b851939a1892d5e5c69eab4d3fa575bf23d82fbce9254a70559af7dc1309230000000320f1e37ee4aa6f61d20fe4f0138f90f2296acf75c2aa5351c5e672d91e0a9da14a1b0000000002000000011b020000002000000057e722488a59d50421b358c213c9157271b12c40b77d90b4b6f1c8ef765a63d8200000002ea05e45264f4caabb5440925d996f11a5e40e24d973c9fb218bee45bc40584e230000000320f4bd53be0a972d7b2e3fe825a5256a5f0c165f87020fd106d125cfec00b3ed251c0000000002000000011c02000000200000005b3edd39210d9893e9cb82bde8292a87769dca36b4bc9d23d2653c848e6e7dc2200000008c55ac946a4dd4b07cd2a50dcd70b0f00d4c15bee38965f2c3cd0093ecaff82c2300000003202bbaba595f513040346be9635705355b0757320718a1c0a721fcf9a99ab32a641d0000000002000000011d0200000020000000d76c5501c8c0322bf275b77c399748999b7e18d2c984a791eaf62a140868498420000000aadfd86a63a3890ade3a50f947d1d79e78e16d4fcf532d8f47890e67f3e7463623000000032047c064db3b87e571944a2e90ce1f31487ba5ac530af62f3c2c5753970aaffd671e0000000002000000011e0200000020000000baa863cd6d27e51286c3625f8b4223ac44df48fdff5dd74fd72a95a44023d7532000000034cb913006d36fa19bb58f4b59b3b824195994b1c0e8cba60a436cc1b365b3b5230000000320c62bd179a001d3a67b0efaf1541049019efa63b488f1c04105a73c3897f5e24b1f0000000002000000011f02000000200000004e37a609c0eefb0fe43cf8fc6cae99e5eab0f8e47b4ac169288f63dd6d0a549220000000dce3c1b054d9741da8a091f1f56a96d1f59f575272f8050f50e06db81b83974d230000000320ea89c85441d345c5f286e2efbc963175e801677df943d118bf33829b9ddd4554200101010000000000000000000000010000000000000000000000000101010000000000000000000000010000000001010100000000000000000000000100000000',
  contractState: 'ContractState (Array(5) [<[05]: b8>, Map {}, Map {}, <[-]: b8>, <[-]: b32>]{claim: <verifier key>, vestmentInfo: <verifier key>, createVestment: <verifier key>}ContractState )'
}

```

This is how it looks like after:

```auto
{
  zswapState: '',
  contractState: 'ContractState (Array(5) [<[05]: b8>, Map {}, Map {}, <[-]: b8>, <[-]: b32>]{claim: <verifier key>, vestmentInfo: <verifier key>, createVestment: <verifier key>}ContractState )'
}

```

I checked if it is not a problem with indexer. But after recreating the indexer container and waiting for it to catch up, it produces same data.
