Skip to content

Types

SmartPy features a comprehensive type system that provides strong typing and type safety for smart contract development.

Primitive

SmartPy TypeDescriptionDocumentation
sp.intArbitrary precision integerIntegers and Mutez
sp.natNon-negative integerIntegers and Mutez
sp.boolBoolean type with values True or FalseBooleans
sp.stringString of charactersStrings and Bytes
sp.bytesSequence of bytesStrings and Bytes
sp.timestampTimestamp valueTimestamps

Tezos specific

SmartPy TypeDescriptionDocumentation
sp.mutezMicro-tez (unit of currency)Integers and Mutez
sp.addressTezos addressAddresses
sp.keyPublic keyKeys and Signatures
sp.key_hashHash of a public keyKeys and Signatures
sp.signatureCryptographic signatureKeys and Signatures
sp.chain_idChain identifierAddresses
sp.operationTezos operationOperations

Cryptographic

SmartPy TypeDescriptionDocumentation
sp.bls12_381_frBLS12-381 scalar field elementBLS12-381
sp.bls12_381_g1BLS12-381 G1 pointBLS12-381
sp.bls12_381_g2BLS12-381 G2 pointBLS12-381

Compound types

SmartPy TypeDescriptionDocumentation
sp.pair[T1, T2]Pair of valuesTuples
sp.record(field1=T1, ...)Named structure with fieldsRecords
sp.option[T]Optional value of type T or NoneOptions and Variants
sp.variant(tag1=T1, ...)Tagged union typeOptions and Variants
sp.list[T]List of elements of type TLists, Sets, and Maps
sp.set[T]Collection of unique elementsLists, Sets, and Maps
sp.map[K, V]Associative array mapping keys to valuesLists, Sets, and Maps
sp.big_map[K, V]Lazy map to store a lot of valuesLists, Sets, and Maps
sp.lambda_(t1, t2, **effects)Function from type A to type BLambdas
sp.ticket[T]Authenticated quantities issued by contracts or usersTickets
sp.contract[T]Representation of a contract interface accepting values of type TContracts

Type declaration

SmartPy uses Python type annotations for explicit type declarations:

smartpy
def f(x: sp.int):
    pass

A type can be defined using : type = to be reused later (only at module level).

smartpy
my_nat: type = sp.nat


def g(x: my_nat):
    pass

Type inference

SmartPy infers types for most values:

python
x = 1  # Inferred as sp.int
y = sp.nat(1)  # Explicitly building sp.nat
z = sp.cast(1, sp.nat)  # Explicitly set to sp.nat

Type declaration vs conversion

SmartPy provides multiple ways to manipulate types:

Type declaration with constructors

Type constructors in SmartPy are used to explicitly declare a value of a specific type, not to convert between types:

smartpy
# This DECLARES a nat value - it doesn't convert an int to a nat
# For example: sp.nat(sp.int(5)) isn't allowed
x = sp.nat(5)

# This also DECLARES a value of type list[sp.int]
my_list = [1, 2, 3]  # Not a Python list being converted, but a SmartPy sp.list[sp.int]

Type annotation with sp.cast

sp.cast is used to explicitly annotate the type of an expression, not to convert between types:

smartpy
# This annotates {} as being a map[sp.address, sp.int]
# It does NOT convert a Python dict to a SmartPy map
x = sp.cast({}, sp.map[sp.address, sp.int])

# Similarly, this annotates 5 as a nat
y = sp.cast(5, sp.nat)

When to use type declaration

SmartPy must know the type of all variables to compile to Michelson. Type declaration is necessary in cases such as:

  1. Empty collections - When initializing empty collections where the type cannot be inferred:

    smartpy
    empty_map = sp.cast({}, sp.map[sp.string, sp.nat])
  2. Explicit contract storage - When defining the expected structure of contract storage:

    smartpy
    @sp.module
    def main():
        t_storage: type = sp.record(counter=sp.nat)
    
        class Counter(sp.Contract):
            def __init__(self, init_value):
                self.data.counter = init_value
                # Ensure storage matches the declared type
                sp.cast(self.data, t_storage)
  3. Layout specifications - To set specific field layouts for records:

    python
    t_param: type = sp.record(
        from_=sp.address, txs=sp.list[sp.record(to_=sp.address, amount=sp.nat)]
    ).layout(("from_", "txs"))

Common misconceptions

  1. Not Type Conversion: sp.cast and type constructors do not change a value's type - they declare or annotate it.

  2. Not Python Types: SmartPy types are distinct from Python types. For example, [1, 2, 3] in SmartPy is a sp.list[sp.int], not a Python list.

Type system features

Type inference

SmartPy infers the types of most expressions automatically:

smartpy
x = 1 + 2  # x inferred as sp.int
y = 1 < 2  # y inferred as sp.bool
z = [1, 2, 3]  # z inferred as sp.list[sp.int]

Generics

Many SmartPy container types support generic type parameters:

python
addresses: type = sp.set[sp.address]
balances: type = sp.map[sp.address, sp.mutez]

Type compatibility

SmartPy has a strict type system. Values must match their expected types exactly, with a few exceptions for numerical types:

smartpy
def f(x: sp.nat):
    pass


f(5)  # 5 is inferred as sp.nat due to the function signature

# `f(-5)` would fails because -5 cannot be a nat

The SmartPy library provides conversion functions like sp.as_nat() and sp.to_int() when explicit type conversion is needed. These are not casts but actual conversion operations that check values and fail if the conversion is not possible.

For more detailed explanation, see casting