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
andBigMap
values - Why Michelson
Map
andBigMap
don't look like a JavascriptMap
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 storageThis 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.
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
.
#
Accessing the values of the mapThis 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.
#
A Contract with a Map using an unannotated pair/tuple as a keyHere 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 keysSince the key of the map has no annotations, MichelsonMap requires that we use an index value starting at 0
to initialize its elements.
#
Accessing Map values using PairsThe 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.
#
A Map with nested Pairs as keysThis 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 keysIn this example, the contract schema does not have map annotations, which means that each value needs to have an index as a property name.
#
Accessing Map values with complex keysThe get
method of the MichelsonMap
class accesses values of the map for a specified key.
#
BigMapsMap 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 bigMapThe 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#
Accessing the values of the map and the bigMapThe 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.
#
Local packing for big mapsBy 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:
- Importing the
MichelCodecPacker
class from@taquito/taquito
- Creating an instance of the
MichelCodecPacker
class and passing it to thesetPackerProvider
method of theTezosToolkit
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 onceIt 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.