Micheline
Smart contract data encoding and transformation
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.
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 infoValue
is a TzGo wrapper for simple or complex primitives representing Micheline values in combination with their TypeKey
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.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 modified 1yr ago