Maps and BigMaps

Learn how to:

  • Fetch data from a Map datatype on a Tezos Smart Contract
  • Fetch data from a BigMap datatype on a Tezos Smart Contract
  • Initialize Map data while originating a new contract to the Tezos Blockchain
  • Use Pairs as a key to access Map and BigMap values
  • Why Michelson Map and BigMap don't look like a Javascript Map

Taquito provides MichelsonMap to make it easy for developers to work with the native Michelson map datatypes. MichelsonMap supports initialization, get and set methods to Maps using primitive datatypes and pairs as keys.

Michelson offers two variants of Maps that are semantically the same but have different implementations and trade-offs in terms of gas and storage costs on a contract. A Map uses more storage but costs less gas, whereas a BigMap consumes less storage but has higher gas costs during the Smart Contract's execution.

A Contract with a single Map for storage#

Origination of the contract with an initial storage#

This example builds on the Ligo Lang Taco Shop learning resources.

The storage of the contract used in the following example is a map where a key is a natural number (a nat), and a value is a pair composed of two values representing the quantity of stock and tez tokens, respectively. The contract's source code is available here. In the example, the contract is originated with initial values using the MichelsonMap class' set method.

Live Editor
Result

The fromLiteral convenience method can be used instead of using set for each element. Here is the same origination operation but using fromLiteral to create our MichelsonMap.

Live Editor
Result

Accessing the values of the map#

This example loads the same type of Taco Shop contract (we created this one earlier). Taquito provides a get method of the MichelsonMap on storage of type Map, and in this case, we access the value stored with a key of 1.

The example calls the Contracts main function of the contract using the key 1 as its parameter. Remember, we can only change contract storage by calling the function provided by the contract. The main function on this Smart Contract is decreasing the value of the current_stock associated with the key 1. We use the get method of the MichelsonMap class to see the difference in storage after the method call.

Live Editor
Result

A Contract with a Map using an unannotated pair/tuple as a key#

Here we have the storage of our contract defined in Michelson.

It has a Map with the annotated name %theMap. This Map uses a pair consisting of a natural number and an address as its key (1, tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx). Its value is also a pair of values, consisting of an int (annotated as %quantity) and mutez (annotated as %amount).

(pair (pair (address %theAddress)
(map %theMap (pair nat address) (pair (mutez %amount) (int %quantity))))
(int %theNumber))

Origination of the contract with Pair as Map keys#

Since the key of the map has no annotations, MichelsonMap requires that we use an index value starting at 0 to initialize its elements.

Live Editor
Result

Accessing Map values using Pairs#

The get method of the MichelsonMap class accesses values of the map for a specified key.

This example accesses the map using its theMap annotation. If the storage does now annotate its properties, the caller must use numeric indexes instead.

Recall that this contract does not annotate the pairs of the key pair either. We use numeric indexes for this also.

Live Editor
Result

A Map with nested Pairs as keys#

This contract schema has a key with eight nested pairs and the value of an int. This example type of key is impractical, but we offer it as an example to illustrate how to work with complex keys.

The Michelson storage schema with a map using eight pairs as a key:

(map (pair int
(pair nat
(pair string
(pair bytes
(pair mutez
(pair bool
(pair key_hash
(pair timestamp address)))))))) int)

Origination of a contract with complex keys#

In this example, the contract schema does not have map annotations, which means that each value needs to have an index as a property name.

Live Editor
Result

Accessing Map values with complex keys#

The get method of the MichelsonMap class accesses values of the map for a specified key.

Live Editor
Result

BigMaps#

Map and BigMap are semantically the same except for everything you learned about Maps applies to working with BigMaps. The only difference is that when calling get on a bigMap will return a Javascript Promise, whereas get on a Map returns directly. In this example, the contract schema does not have map annotations, which means that each value needs to have an index as a property name.

Contract storage containing a map and a bigMap#

The MichelsonMap class also supports the bigMap type. The following example uses a contract containing both a map and a bigMap in its storage. Here is the Michelson definition of storage for this example:

(pair (big_map %thebigmap (pair nat address) int) (map %themap (pair nat address) int))

Origination of the contract with an initial storage#

Live Editor
Result

Accessing the values of the map and the bigMap#

The get method of the MichelsonMap class accesses the values of the map and values of the bigMap. The difference is that the value gets returned directly for a map while the get method on a bigMap returns a promise.

Live Editor
Result

Local packing for big maps#

By default, a call to an RPC node is used to pack data when fetching values from a big map. Big map keys need to be serialized or packed and Taquito relies on the PACK functionality of a Tezos RPC node to pack the big map keys. This may be considered inefficient as it adds a request to a remote node to fetch data.

Now, Taquito allows you to pack the required data locally to fetch values from a big map. By relying on the local pack implementation, Taquito eliminates one RPC roundtrip when fetching big map values. This feature makes fetching big map values 50% faster.

Implementing this feature is a very easy 2 step process:

  1. Importing the MichelCodecPacker class from @taquito/taquito
  2. Creating an instance of the MichelCodecPacker class and passing it to the setPackerProvider method of the TezosToolkit instance.

Here is an example:

import { MichelCodecPacker } from "@taquito/taquito";
const Tezos = new TezosToolkit(RPC_URL);
Tezos.setPackerProvider(new MichelCodecPacker());

After that, Taquito will automatically pack the keys locally when you want to fetch the values of a big map.

Fetch multiple big map values at once#

It is possible to fetch multiple big map values using Taquito with one call using the getMultipleValues method of the BigMapAbstraction class. Taquito will ensure that all fetched big maps come from the same block to ensure a consistent state.

The method takes an array of string (keys) to query and an optional block level as a parameter and returns an object containing the keys and their value in a well-formatted JSON object format.

Live Editor
Result