Skip to content
LogoLogo

Literals and units

Integers

42          (* an integer literal: its type comes from context *)
-42         (* an int *)
1_000_000  (* underscores separate digits *)
0xff        (* hexadecimal *)

An integer literal takes the type its context expects: a binding's annotation, an assignment's target, a parameter, or a return type. It must fit that type. const x : uint8 := 300; is a compile error:

type Tiny is uint8;
 
storage is record {
  n : uint;
}
 
external function go() is {
  const x : Tiny := 300;
  skip
}
literal 300 does not fit type `Tiny`

With no context, an integer literal is a uint. In arithmetic and comparisons, a literal takes the type of the other side, so level > 3 and level + 1 work for a uint8.

Ether units

wei, gwei, and eth suffixes produce values of type wei:

1wei          (* 1 *)
20gwei        (* 20 * 10^9 *)
1eth          (* 10^18 *)
1eth + 500gwei

Time units

seconds, minutes, hours, and days produce durations. A duration adds to a timestamp and is also usable as a uint number of seconds:

import "std/evm";
 
storage is record {
  deadline : timestamp;
  period : uint;
}
 
external function start() is {
  storage.deadline := Evm.blockTimestamp() + 1days + 12hours;
  storage.period := 30minutes;
}

Hex literals

A 0x... literal can be an integer, an address, a bytesN, or bytes, depending on context. A bytesN literal is written at its full width, two digits per byte, and an address literal at 40 digits: as a bytes32, 0x11 is an error. Zero is the one exception, since it has no alignment to get wrong. To make a number a bytesN or address, convert it: bytes32(0x11) is 0x00…0011, and address(0x01) is 0x00…0001. As bytes, a literal is exactly the bytes written, leading zeros included, so its digits must come in pairs: 0x0001 is two bytes, and 0x123 is an error. A bytes literal can be any length; one of more than 64 digits can only be bytes, since no other type holds more than a word.

storage is record {
  selector : bytes4;
  blob : bytes;
  owner : address;
  mask : uint;
}
 
external function set() is {
  storage.selector := 0xa9059cbb;
  storage.blob := 0x616263;
  storage.owner := 0x00000000000000000000000000000000000000ab;
  storage.mask := 0xff;
}

Strings

String literals are double-quoted and cannot span lines. A backslash starts an escape:

EscapeMeaning
\n, \t, \r, \0newline, tab, carriage return, zero byte
\\, \", \'backslash, double quote, single quote
\xHHthe byte HH, from 00 to 7f
\u{H…}a Unicode code point of 1 to 6 hex digits, stored as UTF-8

Any other escape is an error. For bytes above 7f that aren't UTF-8, use a hex literal.

"Hello, world!"
"say \"hi\""
"\x19Ethereum Signed Message:\n32"

Booleans and unit

true and false are the two bool values. Unit is the single value of type unit.

Aggregate literals

LiteralType
{ amount = 1; to = who; }a record, whose type comes from context
(1, who)a tuple, (uint, address)
[1, 2, 3]a list, list(uint) or list(uint, 3)
Some(x), Nonean option
type Payment is record {
  to : address;
  amount : uint;
}
 
storage is record {
  payments : map(uint, Payment);
}
 
external function pay(const id : uint; const to : address) is {
  const p : Payment := { to = to; amount = 5; };
  storage.payments[id] := p;
}
 
external pure function pair() : (uint, bool) is
  (1, true)
 
external pure function sum3() : uint is {
  const xs : list(uint, 3) := [1, 2, 3];
} with xs[0] + xs[1] + xs[2]