Skip to main content

Typing

Type Inference

Just like in Python, most of the time there is no need to specify the type of an object in SmartPy. For a number of reasons (e.g., because SmartPy's target language, Michelson, requires types), each SmartPy expression does however need a type. Therefore SmartPy uses type inference in order to determine each expressions type.

In practice, this means that information about an expression is gathered according to its usage: for example, when somewhere in your contract you write self.data.x == "abc", SmartPy will automatically determine and remember that self.data.x is a string.

Note that SmartPy types are distinct from Python types: self.data.x == "abc" has the Python type sp.Expr (simply because it is a SmartPy expression), whereas it has the SmartPy type sp.TBool (see below).

While most of the time the user will not write many types explicitly it is beneficial to at least have a basic understanding of what they are. This also helps understanding error messages better.

Basic Types

nat

sp.TNat is the type of non-negative integer values.

See Integers documentation.

Michelson nat

int

sp.TInt is the type of signed integer values.

See Integers documentation.

Michelson int

int or nat

sp.TIntOrNat is the type of integer values when SmartPy doesn't know yet which type is applicable, either sp.TInt or sp.TNat.

See Integers documentation.

string

The type of string values.

The current version of Michelson restricts strings to be the printable subset of 7-bit ASCII, namely characters with codes from within [32, 126] range, plus the following escape characters \n, \\, \". Unescaped line breaks (both \n and \r) cannot appear in a string.

"", '', "Hello World", 'Hello World'

See Strings documentation.

Michelson string

bool

The type of boolean values.

True, False

See Booleans documentation.

Michelson bool

bytes

The type of serialized data.

Bytes are used for serializing data, in order to check signatures and compute hashes on them. They can also be used to incorporate data from the wild and untyped outside world.

sp.bytes("0x"), sp.bytes("0x00"), sp.bytes("0x0001"), sp.bytes("0xff"), etc.

See reference Strings and Bytes template.

See Bytes documentation.

Michelson bytes

mutez

The type of tez amounts in SmartPy is sp.TMutez.

Mutez (micro-Tez) are internally represented by 64-bit signed integers. These are restricted to prevent creating a negative amount of mutez. Instructions are limited to prevent overflow and mixing them with other numerical types by mistake. They are also mandatorily checked for overflows.

sp.mutez(1000000)
# or
sp.tez(1)

See Mutez documentation.

Michelson mutez

timestamp

A moment in time.

Literal timestamp is written either using RFC3339 notation in a string (readable), or as the number of seconds since Epoch in a natural (optimized).

sp.timestamp(1571761674)

See Timestamps documentation.

Michelson timestamp

address

An address of a contract or implicit account.

The address type merely gives the guarantee that the value has the form of a Tezos address, as opposed to contract that guarantees that the value is indeed a valid, existing account.

A valid Tezos address is a string prefixed by either tz1, tz2, tz3 or KT1 and followed by a Base58 encoded hash and terminated by a 4-byte checksum.

The prefix designates the type of address:

  • tz1 addresses are followed by a ed25519 public key hash
  • tz2 addresses are followed by a Secp256k1 public key hash
  • tz3 addresses are followed by a NIST p256r1 public key hash
  • KT1 addresses are followed by a contract hash

Addresses prefixed by tz1, tz2 and tz3 designate implicit accounts, whereas those prefixed KT1 designate originated accounts.

Finally, addresses can specify an entrypoint by providing a %<entrypoint_name> suffix.

sp.address("tz1YtuZ4vhzzn7ssCt93Put8U9UJDdvCXci4")

See Addresses and Contracts documentation.

Michelson address

key

A public cryptographic key.

Supported Curves:

sp.key("edpktx799pgw7M4z8551URER52VcENNCSZwE9f9cst4v6h5vCrQmJE")

See Keys documentation.

Michelson key

key_hash

A hash of a public cryptographic key.

sp.key_hash("tz1YtuZ4vhzzn7ssCt93Put8U9UJDdvCXci4")

See Keys documentation.

Michelson key_hash

signature

A cryptographic signature.

sp.signature("edsigu3QszDjUpeqYqbvhyRxMpVFamEnvm9FYnt7YiiNt9nmjYfh8ZTbsybZ5WnBkhA7zfHsRVyuTnRsGLR6fNHt1Up1FxgyRtF")

See Signatures documentation.

Michelson signature

chain_id

A chain identifier.

An identifier for a chain, used to distinguish the test and the main chains.

The value is derived from the genesis block hash and will thus be different on the main chains of different networks and on the test chains for which the value depends on the first block of the test-chain.

Please note that chain ids are non comparable. Equality can be verified in test scenarios by using verify_equal.

sp.chain_id_cst("0x9caecab9")

See Chain Id documentation.

Michelson chain_id

unit

The type whose only value is Unit, to use as a placeholder when some result or parameter is non-necessary. For instance, when the only goal of a contract is to update its storage.

sp.unit

See Unit documentation.

Michelson unit

bls12_381_g1

A point on the BLS12-381 curve G1.

Written as raw bytes, using a big-endian point encoding, as specified here.

See reference BLS12 template.

sp.bls12_381_g1("0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1")

See BLS12 381 documentation.

Michelson bls12_381_g1

bls12_381_g2

A point on the BLS12-381 curve G2.

Written as raw bytes, using a big-endian point encoding, as specified here.

See reference BLS12 template.

sp.bls12_381_g2("0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb80606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be0ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801")

See BLS12 381 documentation.

Michelson bls12_381_g2

bls12_381_fr

An element of the BLS12-381 scalar field Fr.

An element of the scalar field Fr, used for scalar multiplication on the BLS12-381 curves G1 and G2.

Written as raw bytes, using a big-endian point encoding, as specified here.

See reference BLS12 template.

sp.bls12_381_fr("0x2ef123703093cbbbd124e15f2054fa5781ed0b8d092ec3c6e5d76b4ca918a221")

See BLS12 381 documentation.

Michelson bls12_381_fr

chest

Represents timelocked arbitrary bytes with the necessary public parameters to open it.

sp.chest("0x01234")

See Timelock documentation.

Michelson chest

chest_key

Represents the decryption key, alongside with a proof that the key is correct.

sp.chest_key("0x01234")

See Timelock documentation.

Michelson chest_key

Container Types

pair of type1 and type2

A pair or tuple of values.

The type pair type1 type2 is the type of binary pairs composed of a left element of type type1 and a right element of type type2.

(1, True)
# or
sp.pair(sp.nat(1), True)
Michelson pair

list of type

A single, immutable, homogeneous linked list, whose elements are of type type.

[1, 2, 3]
Michelson list

set of type

An immutable set of comparable values of type type.

sp.set([1, 2, 3])
Michelson set

map of kty and vty

An immutable map from kty to vty.

Immutable maps from keys of type kty and values of type vty.

{ 1: "A String" }
# or
sp.map({ 1: "A String" }, tkey = sp.TNat, tvalue = sp.TString)
Michelson map

big_map of kty and vty

A lazily deserialized map from kty to vty.

Lazily deserialized maps from keys of type kty of values of type vty. These maps should be used if you intend to store large amounts of data in a map. Using big_map can reduce gas costs significantly compared to standard maps, as data is lazily deserialized. Note however that individual operations on big_map have higher gas costs than those over standard maps. A big_map also has a lower storage cost than a standard map of the same size, when large keys are used, since only the hash of each key is stored in a big_map.

The behavior of GET, UPDATE, GET_AND_UPDATE and MEM is the same on big_map as on normal map, except that under the hood, elements are loaded and deserialized on demand.

Literal big_map cannot be pushed directly in contract code. Instead, they must be created using EMPTY_BIG_MAP and manipulated using GET, UPDATE, GET_AND_UPDATE and MEM.

Values of the big_map type cannot be serialized using PACK.

sp.big_map({ 1 : "A String" }, tkey = sp.TNat, tvalue = sp.TString)
Michelson big_map

option of type

The type of an optional values.

They represent values of type type or nothing.

Optional values are useful for accommodating missing data, that will only be available after origination.

sp.some(1)
# or
sp.none
Michelson option

contract of type

sp.TContract(t) is the type of contracts whose parameters are of type t.

A value of type contract type is guaranteed to be a valid, existing account whose parameter type is type. This can be opposed to the address type, that merely gives the guarantee that the value has the form of a Tezos address. Values of the contract type cannot be stored. There are not literal values of type contract. Instead, such values are created using instructions such as CONTRACT or IMPLICIT_ACCOUNT.

sp.contract(sp.TNat, "tz1YtuZ4vhzzn7ssCt93Put8U9UJDdvCXci4", entry_point = "foo").open_some("InvalidInterface")
Michelson contract

sapling_state of memo size

sp.TSaplingState(memo_size = size) is the type of sapling states with memo size of size.

Example

# A type
sp.TSaplingState(memo_size = 8)

# Some value
sp.sapling_empty_state(memo_size = 8)

Please see the Sapling integration page for a more comprehensive description of the Sapling protocol.

Michelson sapling_state

sapling_transaction of memo size

sp.TSaplingTransaction(memo_size = size) is the type of sapling transactions with memo size of size.

## only for tests
sp.sapling_test_transaction(source, target, amount, memo_size)

Please see the Sapling integration page for a more comprehensive description of the Sapling protocol.

Michelson sapling_transaction

Record, Variant and Layout

In SmartPy, we can use custom data types called records and variants. Records and variants are translated in Michelson into binary trees of pair and or with annotations corresponding to each field.

The geometry of these binary trees is described by a layout.

Record

A record type represents a cartesian product of several types similar to a struct in C with a layout.

sp.TRecord(**fields) A record type is introduced by enumerating the field names together with types

sp.TRecord(x = sp.TInt, y = sp.TInt)

It uses the default layout as determined by SmartPy, and can be changed by specifying default_record_layout flag.

.layout(layout)

A record type, i.e. something of the form sp.TRecord(...), can be used to define a record type with a layout by doing:

t = sp.TRecord(owner = sp.TAddress, operator = sp.TAddress, token_id = sp.TString)
t_with_layout = t.layout(("owner", ("operator", "token_id")))

.right_comb()

Like .layout(...) but the geometry used is (f1, (f2, .. (f_{k-1}, f_k))))), the list of fields is determined automatically and sorted alphabetically.

.with_fields(fields)

Adds some fields to the TRecord(...) where fields is a Python (string, type)-dictionary of fields.

.without_fields(fields)

Removes some fields from the TRecord(...) where fields is a Python list of fields.

Variant

A variant type represents a union of several choices, similar to a clean version of a struct with an enum/union pattern in C.

sp.TVariant(**fields) A variant type is introduced by enumerating the constructor names together with their inner types.

# Example
sp.TVariant(default_choice = sp.TInt, alternative_choice = sp.TString)

It uses the default layout as determined by SmartPy, and can be changed by specifying default_variant_layout flag.

.layout(layout)

A variant type, i.e. something of the form sp.TVariant(...), can be used to define a variant type with a layout by doing:

t = sp.TVariant(choice_1 = sp.TAddress, choice_2 = sp.TString, choice_3 = sp.TString)
t_with_layout = t.layout(("choice_1", ("choice_2", "choice_3")))

.right_comb()

Like .layout(...) but the geometry used is (f1, (f2, .. (f_{k-1}, f_k))))), the list of fields is determined automatically and sorted alphabetically.

Layouts

A layout is a Python expression listing all fields or constructors in a binary tree structure such as ("b", ("a", "c")). There is no equivalent instruction in Michelson.

See reference Data Type Layouts template.

The SmartPy compiler recognizes a special format "[source] as [target]" to translate field names between SmartPy and Michelson.
A specific case "[source] as" is also supported to generate annotation-less types.

See FA1.2 template.

Bounded types

See Bounded types documentation.

Setting a type constraint

This is usually not needed for small contracts or prototypes but gets useful typically when interacting with the outside world, implementing a given interface, etc.

self.init_type(t)

Constrain the contract storage to be of type t (called in the __init__ constructor).
This is useful because it allows the user to make the storage type explicit or even to define storage-less contracts.
In a test, if not determined in the __init__ method, the storage can be initialized by calling c.init_storage(expression).

See reference Init Type Only template.
This is not mandatory but is appreciated by many in practice.

class MyContract(sp.Contract):
def __init__(self):
## Possibly set the storage type
self.init_type(sp.TRecord(a = sp.TInt, b = sp.TString))
## Possibly set the storage
self.init(...)

sp.set_type(expression, t)

Constrain expression to be of type t. This can only be used as a command inside a contract.

There is no equivalent instruction in Michelson.
A usual pattern for big contracts is to explicitly setting types in the first lines of entry points.

This is usually not needed but is appreciated by many in practice.

@sp.entry_point
def my_entry_point(self, x, y, z):
## First set types
sp.set_type(x, sp.TInt)
sp.set_type(y, sp.TString)
sp.set_type(z, sp.TList(sp.TInt))
## Then do the computations
...

sp.set_result_type(t)

Wrap a block of commands and constrain its result type to t. This can only be used as a command inside a contract.

There is no equivalent instruction in Michelson.

This is useful for making types of failures explicit.

@sp.private_lambda()
def oh_no(params):
with sp.set_result_type(sp.TInt):
sp.if params > 10:
sp.failwith("too big")
sp.else:
sp.result(params)

sp.set_type_expr(expression, t)

Wrap expression to be of type t. This can only be used as an expression.

There is no equivalent instruction in Michelson.

A few words of caution about the differences between sp.set_type and sp.set_type_expr:

# Inside a contract:

@sp.entry_point
def my_entry_point(self, params):
...
## Warning: this is not taken into account (the expression is simply dropped).
sp.set_type_expr(params, sp.TInt)
...

@sp.entry_point
def my_entry_point(self, params):
...
## This is taken into account (when we call params afterward).
params = sp.set_type_expr(params, sp.TInt)
... params ...

@sp.entry_point
def my_entry_point(self, params):
...
## This is taken into account (sp.set_type is a command).
sp.set_type(params, sp.TInt) ## This is taken into account.
...

# Inside a test scenario:

scenario += ...
## This is illegal (command outside of a contract).
sp.set_type(..., ...)

## This is OK (usually useless nonetheless)
x = sp.set_type_expr(..., ...)
scenario += c.f(x)

Containers have built-in optional constraint arguments.

Define a map of (optional) elements in l with optional key type tkey and optional value type tvalue.

sp.map(l = ..., tkey = ..., tvalue = ...)

Define a big_map of (optional) elements in l with optional key type tkey and optional value type tvalue.

sp.big_map(l = ..., tkey = ..., tvalue = ...)

Defines a set of (optional) elements in list l with optional element type t.

sp.set(l = ..., t = ...)`

Defines a list of (optional) elements in list l with optional element type t.

sp.list(l = ..., t = ...)