Version: 24.3.0

Global constants

Written by Roxane Letourneau & Maxwell Ward

Global constants allow users to originate larger contracts or share code between contracts by using registered constants, sort of like a variable or reusable function in traditional programming languages.

The global constants feature includes

  • A register_global_constant operation that allows users to register Micheline expressions in the global table of constants.
  • A constant primitive that allows contracts to reference these constants by their index.

A scenario where you would use a global constant could be something like:

  • Alice wants to originate a contract, but its code is so large that it doesn’t fit within the Tezos size limit for contracts.
  • Alice registers a chosen expression from her contract to the global table of constants by sending a register_global_constant operation to the node.
  • The precedent operation returns the index of the registered constant, which corresponds to the hash of the expression (Blake2b hash + Base58 encode + prefix expr).
  • Alice replaces the newly registered expression in the code of her contract with the primitive constant and the corresponding hash.
  • Alice can now originate her contract as its size has been compressed.

Registering a global constant with Taquito

Contract API

A registerGlobalConstant method is available on the ContractProvider class. A value representing the Micheline expression to register in its JSON format is minimally required as a parameter. The registerGlobalConstant method returns an instance of RegisterGlobalConstantOperation containing a globalConstantHash member that corresponds to the index (hash) of the newly registered constant.

Conversion between Micheline and its JSON format can be achieved using the @taquito/michel-codec if needed.

Note

An expression can only be registered once and will result in an error from the node if anyone tries to register the same constant multiple times.

This example is for demonstration purposes and has no real value as the registered expression is very small.

const op = await Tezos.contract.registerGlobalConstant({
    value: { "prim": "or",
                "args":
                  [ { "prim": "int", "annots": [ "%decrement" ] },
                    { "prim": "int", "annots": [ "%increment" ] } ] }
    });

await op.confirmation();

const hash = op.globalConstantHash; // expr...

The registered expression can be replaced by its corresponding hash in the contract code as follows:

[ { "prim": "parameter",
    "args":
      [ { "prim": "or",
          "args":
            [ { "prim": "constant",
                "args": [ { "string": "expr..." } ] }, <-- Constant hash here
              { "prim": "unit", "annots": [ "%reset" ] } ] } ] },
  { "prim": "storage", "args": [ { "prim": "int" } ] },
  { "prim": "code",
    "args":
      [ [ { "prim": "UNPAIR" },
          { "prim": "IF_LEFT",
            "args":
              [ [ { "prim": "IF_LEFT",
                    "args":
                      [ [ { "prim": "SWAP" }, { "prim": "SUB" } ],
                        [ { "prim": "ADD" } ] ] } ],
                [ { "prim": "DROP", "args": [ { "int": "2" } ] },
                  { "prim": "PUSH",
                    "args": [ { "prim": "int" }, { "int": "0" } ] } ] ] },
          { "prim": "NIL", "args": [ { "prim": "operation" } ] },
          { "prim": "PAIR" } ] ] } ]
[ { "prim": "parameter",
    "args":
      [ { "prim": "or",
          "args":
            [ { "prim": "or",
                "args":
                  [ { "prim": "int", "annots": [ "%decrement" ] },
                    { "prim": "int", "annots": [ "%increment" ] } ] },
              { "prim": "unit", "annots": [ "%reset" ] } ] } ] },
  { "prim": "storage", "args": [ { "prim": "int" } ] },
  { "prim": "code",
    "args":
      [ [ { "prim": "UNPAIR" },
          { "prim": "IF_LEFT",
            "args":
              [ [ { "prim": "IF_LEFT",
                    "args":
                      [ [ { "prim": "SWAP" }, { "prim": "SUB" } ],
                        [ { "prim": "ADD" } ] ] } ],
                [ { "prim": "DROP", "args": [ { "int": "2" } ] },
                  { "prim": "PUSH",
                    "args": [ { "prim": "int" }, { "int": "0" } ] } ] ] },
          { "prim": "NIL", "args": [ { "prim": "operation" } ] },
          { "prim": "PAIR" } ] ] } ]

Batch API

It is also possible to register global constants using the batch API.

Here is an example using the withRegisterGlobalConstant method:

import { OpKind } from '@taquito/taquito';

const batchOp = await Tezos.contract.batch()
.withRegisterGlobalConstant({
    value: {
        prim: 'pair',
        args: [
            {
                prim: 'pair',
                args: [{ prim: 'address', annots: ['%address0'] }, { prim: 'address', annots: ['%address1'] }]
            },
        { prim: 'contract', args: [{ prim: 'nat' }], annots: ['%nat2'] }
        ]
    }
}).send();

await batchOp.confirmation();

Here is an example without using the withRegisterGlobalConstant method:

import { OpKind } from '@taquito/taquito';

const batchOp = await Tezos.contract.batch([
    {
        kind: OpKind.REGISTER_GLOBAL_CONSTANT,
        value: {
            prim: 'list',
            args: [{ prim: 'nat' }]
        }
    },
    // other batched operations
]).send();

await batchOp.confirmation();

Deploying a contract with a storage property containing a global constant

When deploying a contract with global constants in its storage, Taquito needs to know the Michelson values of those constants. You provide these by setting a global constant provider on your TezosToolkit instance.

Taquito provides a default global constant provider named DefaultGlobalConstantsProvider where the hash and corresponding JSON Michelson value must be manually provisioned using its loadGlobalConstant method.

Alternatively, you can create your own provider by implementing the GlobalConstantsProvider interface with a getGlobalConstantByHash method.

import { TezosToolkit, DefaultGlobalConstantsProvider } from '@taquito/taquito';

// create an instance of the `DefaultGlobalConstantsProvider`, load the global constants used in the contract, inject the instance on the TezosToolkit
const expression = { "prim": "int" }
const constantHash = 'expruu5BTdW7ajqJ9XPTF3kgcV78pRiaBW3Gq31mgp3WSYjjUBYxre';

const Tezos = new TezosToolkit('rpc_url');
const globalConstantProvider = new DefaultGlobalConstantsProvider();
globalConstantProvider.loadGlobalConstant({
  [constantHash]: expression
})
Tezos.setGlobalConstantsProvider(globalConstantProvider);

// The `getGlobalConstantByHash` method of the configured global constant provider is internally called when preparing the operation. 
// This allows accessing the right Michelson type to encode the storage object into the corresponding Michelson data properly.
const op = await Tezos.contract.originate({
     code: [{
         prim: 'parameter',
         args: [{
             prim: 'or',
             args: [{
                 prim: 'or',
                 args: [
                     { prim: 'int', annots: ['%decrement'] },
                     { prim: 'int', annots: ['%increment'] }
                 ]
             },
             { prim: 'unit', annots: ['%reset'] }]
         }]
     },
     { prim: 'storage', args: [{ prim: 'constant', args: [{ string: constantHash }] }] },
     {
         prim: 'code',
         args: [
             [
                 { prim: 'UNPAIR' },
                 {
                     prim: 'constant',
                     args: [{ string: constantHash2 }]
                 },
                 { prim: 'NIL', args: [{ prim: 'operation' }] },
                 { prim: 'PAIR' }
             ]
         ]
     }
     ],
     storage: 4
 });

await op.confirmation();