---
title: "12. Data Types and the Runtime API"
url: "https://radix.wiki/developers/scrypto/12-data-types-and-runtime"
updated: 2026-11-14
license: CC-BY-4.0
license_url: "https://creativecommons.org/licenses/by/4.0/"
version: "1.0.0"
---

# 12. Data Types and the Runtime API

|  |  |
| --- | --- |
| Series | [Scrypto track](/developers/scrypto), part 12 |
| Tested with | Scrypto 1.4.0 and `scrypto-test`'s ledger simulator, 14 November 2026 |
| Decimal | 192-bit, 18 decimal places |
| PreciseDecimal | 256-bit, 36 decimal places |
| Hash | Blake2b-256; Keccak-256 through `CryptoUtils` |

## Introduction

A Scrypto blueprint is Rust compiled to WebAssembly, so it can use most of Rust's types, plus a set that Scrypto adds for money, ownership and the ledger. This guide covers the types a blueprint keeps in its state and passes across calls, the fixed-point arithmetic that replaces floating point, and the `Runtime` and `Clock` APIs through which a blueprint reads the epoch, the time, its own address and the transaction it is running in ([Data Types](https://docs.radixdlt.com/docs/data-types)).

The results quoted come from a blueprint run in the ledger simulator. Three of them correct the official docs: the docs say Scrypto's `Hash` is SHA-256, say Rust's integer types wrap silently on overflow, and tell you to quote fractional literals for `dec!`. None of the three holds in Scrypto 1.4.0, as the sections below show.

## Which types can a blueprint use?

Anything whose type implements `ScryptoSbor`, the encoding used to store state and pass arguments ([SBOR](/developers/transactions/06-sbor)). That covers:

- **Rust primitives**: `bool`, `i8` to `i128`, `u8` to `u128`, `String`. `isize` and `usize` compile to 32-bit integers. There is no `f32` or `f64`: floating-point results can differ between processors, and every validator must compute the same result.
- **Containers**: `Option`, arrays, tuples, `Vec`, `HashMap`, `BTreeMap`, `IndexMap` and the matching sets.
- **Your own structs and enums**, with `#[derive(ScryptoSbor)]`. The docs say generic structs and enums are not supported.
- **Wide integers** that always panic on overflow: `I8` to `I512` and `U8` to `U512`.
- **Ledger types**: `ComponentAddress`, `ResourceAddress`, `PackageAddress`; `Global`, a typed reference to a global component; `Owned`, a component owned by another; `Vault`, `Bucket` and `Proof` and their fungible and non-fungible forms; `KeyValueStore`; `NonFungibleLocalId`; `Hash`.

A `NonFungibleLocalId` takes one of four forms, written in a manifest as `#7#` for an integer, `` for a string, `[0a1b]` for bytes, and `{…}` for a RUID, the random 32-byte ID the engine assigns when a blueprint calls `mint_ruid_non_fungible`.

The choice between a map and a `KeyValueStore` matters most. A `HashMap` in component state is loaded in full every time the component is called, and every byte read costs fees. A `KeyValueStore` is loaded one entry at a time. A collection that grows with users belongs in a `KeyValueStore` ([Before You Release](/developers/scrypto/13-before-you-release)).

## How do Decimal and PreciseDecimal work?

`Decimal` is a 192-bit signed integer read as a number with 18 decimal places; `PreciseDecimal` is 256-bit with 36 ([decimal.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-common/src/math/decimal.rs)). Resource amounts are always `Decimal`. Both are written with macros, `dec!(10)` and `pdec!("0.5")`. The macro parses a fractional literal such as `dec!(1.001)` from its digits at compile time, so it never becomes a float; the docs' instruction to put such literals in quotes is no longer needed ([radix-common-derive](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-common-derive/src/decimal.rs)).

```
pub fn thirds() -> (Decimal, PreciseDecimal, Decimal) {
    let d = dec!(1) / dec!(3);
    let p = pdec!(1) / pdec!(3);
    let back = (p * pdec!(3)).checked_truncate(RoundingMode::ToZero).unwrap();
    (d, p, back)
}
// (0.333333333333333333,
//  0.333333333333333333333333333333333333,
//  0.999999999999999999)

pub fn checked(a: Decimal, b: Decimal) -> (Option, Option) {
    (a.checked_mul(b), a.checked_div(Decimal::ZERO))
}
// checked(Decimal::MAX, 2) returns (None, None)

pub fn round_half(x: Decimal) -> (Decimal, Decimal, Decimal) {
    (
        x.checked_round(0, RoundingMode::ToNearestMidpointToEven).unwrap(),
        x.checked_round(0, RoundingMode::ToNearestMidpointAwayFromZero).unwrap(),
        x.checked_round(0, RoundingMode::ToZero).unwrap(),
    )
}
// round_half(2.5) returns (2, 3, 2)
```

Dividing first and multiplying back loses the last digit even in `PreciseDecimal`. The checked methods return `None` on overflow and on division by zero; the `+`, `-`, `*` and `/` operators panic instead, which fails the transaction. Rounding takes an explicit mode: `ToNearestMidpointToEven`, banker's rounding, turned 2.5 into 2, and `ToNearestMidpointAwayFromZero` turned it into 3.

## Do Rust integers overflow silently in Scrypto?

Not in a package created with `scrypto new-package`. The docs say built-in types such as `i32` wrap silently while Scrypto's `I32` panics. That is Rust's default for a release build, but the `Cargo.toml` that `scrypto new-package` generates sets `overflow-checks = true` in its release profile, so `i32` panics too:

```
pub fn add_i32(a: i32, b: i32) -> i32 {
    a + b
}
// add_i32(i32::MAX, 1) fails the transaction:
// PanicMessage("attempt to add with overflow @ src/numbers.rs:29:13")
```

A package whose profile was edited, or that predates this template, may behave differently; the setting is worth checking before relying on either behaviour.

## What can a blueprint ask the engine?

The `Runtime` and `Clock` structs are a blueprint's view of its surroundings ([runtime.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/scrypto/src/runtime/runtime.rs), [clock.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/scrypto/src/runtime/clock.rs)):

```
pub fn context(&self) -> (u64, i64, String, Hash, Hash) {
    info!("context called");
    debug!("debug line");
    (
        Runtime::current_epoch().number(),
        Clock::current_time_rounded_to_seconds().seconds_since_unix_epoch,
        Runtime::bech32_encode_address(Runtime::global_address()),
        hash("radix"),                       // Blake2b-256
        CryptoUtils::keccak256_hash("radix"), // Keccak-256
    )
}
```

- **Time**: `Runtime::current_epoch()` returns the consensus epoch, about five minutes long on mainnet. `Clock::current_time_rounded_to_seconds()` and `…_rounded_to_minutes()` return the proposer's timestamp; `Clock::current_time_is_strictly_before(instant, precision)` and its relatives compare against a deadline without reading the exact time. In the simulator, after advancing the clock to 1,800,000,000 seconds, the method returned exactly that.
- **Identity**: `Runtime::global_address()`, `package_address()` and `blueprint_name()` describe the running component; `Runtime::bech32_encode_address()` turns an address into the network's text form, here a `component_sim1…` address on the simulator network.
- **Addresses for later**: `Runtime::allocate_component_address()` reserves a component's address before it exists ([Badge Patterns](/developers/scrypto/10-badge-patterns)).
- **The transaction**: `Runtime::transaction_hash()`, `generate_ruid()`, `emit_event()`, `assert_access_rule()`, and the fee figures `get_fee_balance()`, `get_usd_price()` and `get_tip_percentage()`.

`hash()` is Blake2b-256 ([hash.rs](https://github.com/radixdlt/radixdlt-scrypto/blob/main/radix-common/src/crypto/hash.rs)); the docs' statement that it is SHA-256 is wrong. Ethereum-compatible Keccak-256 is `CryptoUtils::keccak256_hash()`, and `CryptoUtils` also verifies BLS12-381 signatures ([Keccak256](https://docs.radixdlt.com/docs/keccak256), [BLS12-381](https://docs.radixdlt.com/docs/bls12-381)).

## Logging

Five macros write to the transaction receipt's log: `error!`, `warn!`, `info!`, `debug!` and `trace!`, each taking a format string like `println!` ([Logging](https://docs.radixdlt.com/docs/logging)). Which of them are compiled in depends on a build setting: `scrypto build` includes levels up to `INFO` unless given `--log-level DEBUG` or `TRACE`, and the macros above the level compile to nothing. `scrypto-test` compiles packages at `TRACE`, so tests see every level; the simulator run above logged both the `info!` and the `debug!` line.

## Functions, methods and visibility

A **function** has no `self` and is called on the blueprint, typically to instantiate a component. A **method** takes `&self` to read the component's state or `&mut self` to change it, and is called on a component. Every `pub` function and method is callable from any transaction unless an access rule restricts it; one without `pub` is private to the blueprint and absent from its interface ([Functions and Methods](https://docs.radixdlt.com/docs/functions-and-methods)). Restricting a public method is the job of `enable_method_auth!` ([Authorization and Access Rules](/developers/scrypto/03-authorization-and-badges)).

## Next Steps

- [Before You Release](/developers/scrypto/13-before-you-release) – the mistakes these types make possible, and how to avoid them
- [SBOR](/developers/transactions/06-sbor) – how these types are encoded on the ledger and in manifests

## External Links

- [Data Types](https://docs.radixdlt.com/docs/data-types) – Radix documentation
- [Runtime](https://docs.rs/scrypto/1.4.0/scrypto/runtime/struct.Runtime.html) and [Clock](https://docs.rs/scrypto/1.4.0/scrypto/runtime/struct.Clock.html) – docs.rs
- [Decimal](https://docs.rs/radix-common/1.4.0/radix_common/math/decimal/struct.Decimal.html) – docs.rs
- [Logging](https://docs.radixdlt.com/docs/logging) – Radix documentation
