diff --git a/docs/developers/best-practices/prepare-sc-supernova.md b/docs/developers/best-practices/prepare-sc-supernova.md new file mode 100644 index 000000000..a16415337 --- /dev/null +++ b/docs/developers/best-practices/prepare-sc-supernova.md @@ -0,0 +1,178 @@ +--- +id: prepare-sc-supernova +title: Preparing SCs for Supernova +--- + + +The MultiversX Supernova upgrade reduces block time from **6 seconds to 0.6 seconds**, enabling sub-second blocks. While this is a major improvement, it can impact existing smart contracts, especially those relying on assumptions about timestamp behavior. + +This guide explains how to prepare your contracts for Supernova safely. + + + +[comment]: # (mx-context-auto) + +## Understand What Changes — and What Doesn’t + +All existing timestamp APIs continue returning **seconds** unless you explicitly call the millisecond versions. Nothing changes silently in the VM, the framework, or deployed contracts. + +Obviously, contracts that have the 6 seconds between blocks hardcoded will have to change. But more importantly, block times expressed in seconds no longer uniquely identify a block, which leads to potential problems. + +We are going to go through the most important patterns to look out for. + + + +[comment]: # (mx-context-auto) + +## Potential problems to look out for + +[comment]: # (mx-context-auto) + +### Replace Hardcoded Block Timing + +If your contract uses hard-coded constants (like `6 seconds` or `6000 milliseconds`) to estimate the time between blocks, this logic will need to be changed. + +It might estimate durations from nonce deltas, or vice-versa, it might estimate number of blocks by timestamps. + +**Fix:** Use the API instead: `self.blockchain().get_block_round_time_millis()`. This returns `6000` (as `DurationMillis`) today and `600` after Supernova. + + + +[comment]: # (mx-context-auto) + +### Avoid Timestamp-Based Monotonicity + +Logic such as: + +``` +require!(ts_now > last_ts) +``` + +may break because multiple blocks can share the same timestamp. + +**Fix:** Use **block nonces** for guaranteed monotonicity. + + + +[comment]: # (mx-context-auto) + +### Prevent Rate-Limit Bypasses + +If your contract allows one action “per block” but checks the difference in **seconds**, multiple blocks in the same second can bypass restrictions. + +**Fix:** Use block nonces or switch to millisecond timestamps. + + + +[comment]: # (mx-context-auto) + +### Revisit Expiration Logic + +Expiration logic written assuming a fixed block interval may accidentally allow: + +* extra blocks before expiration +* longer-than-intended windows for execution + +If your expiration logic uses seconds, double-check assumptions. + + + +[comment]: # (mx-context-auto) + +### Stop Using Timestamps as Block Identifiers + +Before Supernova, a timestamp could uniquely identify a block. +After Supernova, multiple blocks may share the same timestamp. + +If you use timestamps as map keys or identifiers: + +* collisions will occur +* data may be overwritten or skipped + +**Fix:** Use block **nonces**. + + + +[comment]: # (mx-context-auto) + +### Review Reward and Accumulation Calculations + +Reward logic that uses: + +``` +delta = ts_now - last_ts +``` + +may behave unexpectedly: + +* delta may be zero over multiple blocks +* rewards might accumulate slower or unevenly +* divisions by delta may cause division-by-zero errors + +Consider switching to milliseconds for finer granularity. + + + +[comment]: # (mx-context-auto) + +## Migration Guidance + +Just as important as fixing potential issues with Supernova, it is essential not to introduce new bugs in the process. + +Make sure to take the following into consideration when migrating: + + + +[comment]: # (mx-context-auto) + +### Seconds vs. Milliseconds + +Switching from second to millisecond timestamp can be error-prone. + +The most dangerous bug is accidentally mixing second and millisecond values, causing storage and logic corruption. + +To prevent this issue, use the [strongly typed timestamp and duration objects](time-types). + +Starting in **multiversx-sc v0.63.0**, all timestamp APIs have typed replacements: + +* `get_block_timestamp_seconds()` +* `get_block_timestamp_millis()` +* `get_prev_block_timestamp_seconds()` +* `get_prev_block_timestamp_millis()` +* `get_block_round_time_millis()` +* `epoch_start_block_timestamp_millis()` + +And avoid using raw `u64` for time values. + +Make sure to convert form `u64` to type timestamps **before** doing any other refactoring. It is much safer this way. + + + +[comment]: # (mx-context-auto) + +### Backwards compatibility + +When upgrading an existing contract from second to millisecond timestamps, it is essential to: + +* Ensure storage remains consistent +* Update ESDT metadata carefully +* Add compatibility logic if needed + +If you are unsure that this can be done safely, it might be safer to keep the contract running on second timestamps. + + + +[comment]: # (mx-context-auto) + +## Summary Checklist + +To prepare for Supernova: + +* [ ] Upgrade to `multiversx-sc v0.63.0` +* [ ] Use typed timestamp/duration APIs +* [ ] Remove all hardcoded 6-second or 6000-millisecond assumptions +* [ ] Avoid using timestamps as block identifiers +* [ ] Review expiration, reward, and accumulation logic +* [ ] Switch to millisecond timestamps where appropriate +* [ ] Ensure storage/metadata compatibility +* [ ] Update tests to use for millisecond block timestamps, where needed diff --git a/docs/developers/best-practices/time-types.md b/docs/developers/best-practices/time-types.md new file mode 100644 index 000000000..b9a830e59 --- /dev/null +++ b/docs/developers/best-practices/time-types.md @@ -0,0 +1,121 @@ +--- +id: time-types +title: Time-related Types +--- + + +The Supernova release introduces increased block frequency and encourages transitioning to millisecond timestamps, instead of seconds. To support this, the SpaceCraft SDK (starting with `v0.63.0`) provides strong type wrappers for time values to prevent common bugs. + + +[comment]: # (mx-context-auto) + +## Overview + +Traditionally, smart contracts used plain `u64` values to represent timestamps and durations. As the ecosystem transitions to millisecond precision, mixing seconds and milliseconds becomes error-prone. Runtime errors are difficult to detect, so the compiler now assists by enforcing type correctness. + + +[comment]: # (mx-context-auto) + +## The Four Time-Related Types + +The framework introduces four new types: + +* **`TimestampSeconds`** — moment in time measured in seconds +* **`TimestampMillis`** — moment in time measured in milliseconds +* **`DurationSeconds`** — duration measured in seconds +* **`DurationMillis`** — duration measured in milliseconds + +Each is a **newtype wrapper around `u64`**, with an identical underlying representation but distinct meaning. + + +[comment]: # (mx-context-auto) + +## Why These Types Exist + +Using plain `u64` makes it easy to: + +* accidentally mix seconds and milliseconds +* add timestamps together (nonsensical) +* subtract mismatched units +* perform invalid arithmetic without realizing it + +The new types make such mistakes **fail at compile time**. + + +[comment]: # (mx-context-auto) + +## Supported Operations + +The framework implements common operators only where they make sense. + +Examples: + +``` +TimestampMillis - TimestampMillis → DurationMillis +TimestampSeconds + DurationSeconds → TimestampSeconds +DurationSeconds + DurationSeconds → DurationSeconds +``` + +Examples of invalid operations (will not compile): + +``` +TimestampMillis - TimestampSeconds +TimestampMillis + TimestampMillis +``` + + +[comment]: # (mx-context-auto) + +## Codec and ABI Support + +All four types: + +* support codec and ABI traits +* can be used in storage +* can be used in events and arguments +* behave like `u64` for serialization + +This makes them safe to adopt even in existing contracts. + + +[comment]: # (mx-context-auto) + +## When to Use Seconds vs. Milliseconds + +* **Seconds**: acceptable when your contract already stores timestamps in seconds or relies on existing storage/metadata +* **Milliseconds**: recommended for all new contracts and for future-proof logic + +The key rule: **Never mix seconds and milliseconds without explicit conversion.** + + +[comment]: # (mx-context-auto) + +## Testing + +Time-related types also work in testing frameworks. + +In blackbox tests, one can write: + +```rust + let block_timestamp_ms = TimestampMillis::new(123_000_000); + + world + .epoch_start_block() + .block_timestamp_ms(block_timestamp_ms) + .block_nonce(15_000) + .block_round(17_000); +``` + + +Mandos supports both `blockTimestamp` and the newer `blockTimestampMs`. Set both if they are used together for backward compatibility. + + +[comment]: # (mx-context-auto) + +## Summary + +* Time-related newtypes enforce correctness at compile time +* They eliminate a class of subtle bugs related to timestamp unit mismatches +* Both seconds and milliseconds will continue to work, but milliseconds are recommended for new code + +Use these types to ensure your contract remains safe and consistent across all future protocol updates. diff --git a/docs/developers/developer-reference/sc-api-functions.md b/docs/developers/developer-reference/sc-api-functions.md index c83cb7c3d..7ddf50265 100644 --- a/docs/developers/developer-reference/sc-api-functions.md +++ b/docs/developers/developer-reference/sc-api-functions.md @@ -140,14 +140,26 @@ A smart contract call that runs out of gas will revert all operations, so this f [comment]: # (mx-context-auto) -### get_block_timestamp +### get_block_timestamp_seconds ```rust -get_block_timestamp() -> u64 +get_block_timestamp_millis() -> TimestampSeconds ``` Returns the timestamp of the current block, in seconds (UNIX timestamp). + +[comment]: # (mx-context-auto) + +### get_block_timestamp_millis + +```rust +get_block_timestamp_millis() -> TimestampMillis +``` + +Returns the timestamp of the current block, in milliseconds (UNIX timestamp). + + [comment]: # (mx-context-auto) ### get_block_nonce @@ -194,12 +206,26 @@ This will be the same for all the calls in the current block, so it can be predi [comment]: # (mx-context-auto) -### get_prev_block_timestamp +### get_prev_block_timestamp_seconds + +```rust +get_prev_block_timestamp_seconds() -> TimestampSeconds +``` + +Returns the timestamp of the previous block, in seconds (UNIX timestamp). + + +[comment]: # (mx-context-auto) + +### get_prev_block_timestamp_millis ```rust -get_prev_block_timestamp() -> u64 +get_prev_block_timestamp_millis() -> TimestampMillis ``` +Returns the timestamp of the previous block, in milliseconds (UNIX timestamp). + + [comment]: # (mx-context-auto) ### get_prev_block_nonce @@ -232,7 +258,16 @@ get_prev_block_epoch() -> u64 get_prev_block_random_seed() -> ManagedByteArray ``` -The same as the functions above, but for the previous block instead of the current block. +[comment]: # (mx-context-auto) + +### get_block_round_time_millis + +```rust +get_block_round_time_millis(&self) -> DurationMillis +``` + +The block round time, in milliseconds, i.e the time between consecutive blocks. + [comment]: # (mx-context-auto) diff --git a/sidebars.js b/sidebars.js index 8d343db03..e14985bf2 100644 --- a/sidebars.js +++ b/sidebars.js @@ -126,12 +126,14 @@ const sidebars = { }, { type: "category", - label: "Rust Developers Best Practices", + label: "Best Practices", items: [ "developers/best-practices/best-practices-basics", "developers/best-practices/biguint-operations", "developers/best-practices/managed-decimal", "developers/best-practices/the-dynamic-allocation-problem", + "developers/best-practices/time-types", + "developers/best-practices/prepare-sc-supernova", ], }, {