Skip to main content
Version: 1.7.0

Bytes

Bytes are used for serializing data, for example to compute signature hashes. Conversely, they can be used to deserialise external data, in which case the expected LIGO type needs to be specified.

Literals

Byte literals are sequences of bytes (eight-bit values, also known as octets), defined using the prefix 0x followed by hexadecimal digits, or none if the denoted literal is zero:

const a : bytes = 0x70FF;
const zero : bytes = 0x;
const zero_too = 0x00;

Clearly, this means that literal bytes are always comprised of an even number of hexadecimal digits (because one hexadecimal digit requires up to four bits in binary, and eight are needed to make up a byte).

From numbers to bytes and back

Some other numerals can be converted to bytes by means of calling the predefined function bytes, which is overloaded. The reverse conversion is done by the predefined functions int and nat. For instance, here how to create bytes from natural numbers and integers:

const b: bytes = bytes(123n); // 7B in hexadecimal
const c: bytes = bytes(123);
const d: bytes = bytes(-123); // Two's complement
const n: nat = nat(0x7B); // n == 123n
const i: int = int(0x7B); // i == 123

Note: See Two's complement.

From strings

A string literal can be converted to bytes in two ways:

  1. by interpreting the ASCII code of each character (which spans over two hexadecimal digits) as one byte;
  2. by interpreting directly each character as one hexadecimal digit.

In the former case, the syntax is somewhat odd -- as opposed to simply calling the function bytes:

const from_ascii: bytes = bytes`foo`; // Not a call

The latter case is implemented as a type cast:

// raw == from_ascii
const raw: bytes = ("666f6f" as bytes);

Note that both syntaxes apply respectively only to verbatim string literals and general strings, not general expressions of type string. In other words, the contents of the strings must be available at compile-time. (This actually reveals that ("666f6f" as bytes) is not really a cast, as casts are non-operations.)

Concatenating

Two or more bytes can be concatenated.

const two: bytes = Bytes.concat(0x70, 0xAA);
const three: bytes = Bytes.concats([0x70, 0xAA, 0xFF]);

Sizing

In order to obtain the length of a sequence of bytes, use the predefined function Bytes.length like so:

const len: nat = Bytes.length(0x0AFF); // len == 2n

Slicing

Bytes can be extracted using the predefined function Bytes.sub. The first parameter is the start index and the second is the number of bytes of the slice we want. Keep in mind that the first byte in a sequence has index 0n.

const large = 0x12345678;
const slice = Bytes.sub(1n, 2n, large); // sub == 0x3456

Bitwise operations

The bitwise operations on sequences of bytes are as follows:

// Bitwise "and"
const and: bytes = 0x0005 & 0x0106; // 0x0004
// Bitwise "or"
const or: bytes = 0x0005 | 0x0106; // 0x0107
// Bitwise "xor"
const xor: bytes = 0x0005 ^ 0x0106; // 0x0103
// Bitwise "shift left"
const shift_left: bytes = 0x06 << 8n; // 0x0600
// Bitwise "shift right"
const shift_right: bytes = 0x0006 >> 1n; // 0x0003

Packing and unpacking

As Michelson provides the instructions PACK and UNPACK for data serialisation, so does LIGO with Bytes.pack and Bytes.unpack. The former serialises Michelson data structures into a binary format, and the latter reverses that transformation. Unpacking may fail, so the return type of Byte.unpack is an option that needs to be annotated.

Note: PACK and UNPACK are Michelson instructions that are intended to be used by people that really know what they are doing. There are several risks and failure cases, such as unpacking a lambda from an untrusted source or casting the result to the wrong type. Be careful.

const id_string = (p: string) : option<string> => {
let packed = Bytes.pack(p);
return Bytes.unpack(packed);
};

Cryptography

One common use of bytes, beyond packing and unpacking, is cryptography. The predefined module Crypto provides the following hashing functions, which are efficient because they are natively supported by the Michelson virtual machine:

const blake2b: bytes => bytes;
const sha256: bytes => bytes;
const sha512: bytes => bytes;
const sha3: bytes => bytes;
const keccak: bytes => bytes;