Micheline

Smart contract data encoding and transformation

Visit the Go Doc docu or read the source code on Github.

Tezos uses Micheline for encoding smart contract data and code. Micheline is strongly typed, but complex and has a few ambiguities that make it hard to use. TzGo contains a library that lets you easily decode, analyze and construct compliant Micheline data structures from Go types.

Primitives

Micheline uses basic primitives (Prim) for encoding types and values. These primitives can be expressed in JSON and binary format and TzGo can translate between them efficiently. Micheline also supports type annotations which are used by high-level languages to express complex data types like records and their field names.

TzGo defines a basic Prim data type to work with Micheline primitives directly:

type Prim struct {
	Type      PrimType // primitive type
	OpCode    OpCode   // primitive opcode (invalid on sequences, strings, bytes, int)
	Args      []Prim   // optional nested arguments
	Anno      []string // optional type annotations
	Int       *big.Int // decoded value when Prim is an int
	String    string   // decoded value when Prim is a string
	Bytes     []byte   // decoded value when Prim is a byte sequence
	WasPacked bool     // true when content has been unpacked
}

Since Micheline value encoding is quite verbose and can be ambiguous, TzGo supports unfolding of raw Micheline using helpers Value and Type with member functions like Map(), GetInt64(), GetAddress():

  • Type is a TzGo wrapper for simple or complex primitives which contain annotated type info

  • Value is a TzGo wrapper for simple or complex primitives representing Micheline values in combination with their Type

  • Key is a TzGo wrapper for special comparable values that are used as map or bigmap keys

Sometimes Micheline values have been packed into byte sequences with the Michelson PACK instruction and it is desirable to unpack them before processing (e.g. to retrieve UFT8 strings or nested records). TzGo supports Unpack() and UnpackAll() functions on primitives and values and also detects the internal type of packed data which is necessary for unfolding.

Value API

Micheline type and value trees are verbose and can be ambiguous due to comb-pair optimizations. If you don't know or don't care about what that even means, you may want to use the Value API which helps you translate Micheline into human readable form.

There are multiple options to access decoded data:

// 1/
// decode into a Go type tree using `Map()` which produces
// - `map[string]interface{}` for records and maps
// - `[]interface{}` for lists and sets
// - `time.Time` for time values
// - `string` stringified numbers for Nat, Int, Mutez
// - `bool` for Booleans
// - `tezos.Address` for any string or bytes sequence that contains an address
// - `tezos.Key` for keys
// - `tezos.Signature` for signatures
// - `tezos.ChainIdHash` for chain ids
// - hex string for untyped bytes
// - opcode names for any Michelson opcode
// - opcode sequences for lambdas
m, err := val.Map()
buf, err := json.MarshalIndent(m, "", "  ")
fmt.Println(string(buf))

// when you know the concrete type you can cast directly
fmt.Println("Value=", m.(map[string]interface{})["transfer"].(map[string]interface{})["value"])

// 2/
// access individual nested fields using value helpers (`ok` is true when the field
// exists and has the correct type; helpers exist for
//   GetString() (string, bool)
//   GetBytes() ([]byte, bool)
//   GetInt64() (int64, bool)
//   GetBig() (*big.Int, bool)
//   GetBool() (bool, bool)
//   GetTime() (time.Time, bool)
//   GetAddress() (tezos.Address, bool)
//   GetKey() (tezos.Key, bool)
//   GetSignature() (tezos.Signature, bool)
from, ok := val.GetAddress("transfer.from")

// 3/
// unmarshal the decoded Micheline parameters into a json-tagged Go struct
type FA12Transfer struct {
    From  tezos.Address `json:"from"`
    To    tezos.Address `json:"to"`
    Value int64         `json:"value,string"`
}

// FA1.2 params are nested, so we need an extra wrapper
type FA12TransferParams struct {
	Transfer FA12Transfer `json:"transfer"`
}

var transfer FA12TransferParams
err := val.Unmarshal(&transfer)

Last updated