Reading Contract Data

TzGo allows you to work with smart contract data in a straight forward way using regular Go types and without the need to parse through Micheline encoded trees. TzGo can natively process contract scripts, storage, entrypoints, views, call parameters and bigmaps.

With a few notable exceptions (see below), most data is available on the Tezos RPC, so you can use TzGo and a Tezos node to write most parts of an application without the need for an indexer. The following features are unavailable on the Tezos RPC:

  • lookup of operations by hash

  • listing of historic transactions from and to an address

  • listing of blocks where an account update happened

  • the contents of bigmap keys (the node only stores bigmap key hashes)

  • listing of bigmap keys and values (this exists, but lacks pagination support)

Fetching and decoding contract storage

import (
	"blockwatch.cc/tzgo/micheline"
	"blockwatch.cc/tzgo/rpc"
	"blockwatch.cc/tzgo/tezos"
)

// we use the Baker Registry on mainnet as example
addr := tezos.MustParseAddress("KT1ChNsEFxwyCbJyWGSL3KdjeXE28AY1Kaog")

// init RPC client
c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)

// fetch the contract's script and most recent storage
script, _ := c.GetContractScript(ctx, addr)

// unfold Micheline storage into human-readable form
val := micheline.NewValue(script.StorageType(), script.Storage)
m, _ := val.Map()
buf, _ := json.MarshalIndent(m, "", "  ")
fmt.Println(string(buf))

Decoding contract call parameters

import (
	"blockwatch.cc/tzgo/micheline"
	"blockwatch.cc/tzgo/rpc"
	"blockwatch.cc/tzgo/tezos"
)

// init RPC client
c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)

// assuming you have this transaction
tx := block.Operations[3][0].Contents[0].(*rpc.TransactionOp)

// load the contract's script for type info
script, err := c.GetContractScript(ctx, tx.Destination)

// unwrap params for nested entrypoints
ep, param, err := tx.Parameters.MapEntrypoint(script.ParamType())

// convert Micheline param data into human-readable form
val := micheline.NewValue(ep.Type(), param)

// e.g. access individual nested fields using value helpers
from, ok := val.GetAddress("transfer.from")

Listing a contract's bigmaps

import (
	"blockwatch.cc/tzgo/micheline"
	"blockwatch.cc/tzgo/rpc"
	"blockwatch.cc/tzgo/tezos"
)

// we use the hic et nunc NFT market on mainnet as example
addr := tezos.MustParseAddress("KT1Hkg5qeNhfwpKW4fXvq7HGZB9z2EnmCCA9")

// init RPC client
c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)

// fetch the contract's script and most recent storage
script, _ := c.GetContractScript(ctx, addr)

// bigmap pointers as []int64
ids := script.BigmapsById()

// bigmap pointers as named map[string]int64 (names from type annotations)
named := script.BigmapsByName()

Fetching and decoding bigmap values

// init RPC client
c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)

// load bigmap type info (use the Baker Registry on mainnet as example)
biginfo, _ := c.GetBigmapInfo(ctx, 17)

// list all bigmap keys
bigkeys, _ := c.GetBigmapKeys(ctx, 17)

// visit each value
for _, key := range bigkeys {
	bigval, _ := c.GetBigmapValue(ctx, 17, key)

	// unfold Micheline type into human readable form
	val := micheline.NewValue(micheline.NewType(biginfo.ValueType), bigval)
	m, _ := val.Map()
	buf, _ := json.MarshalIndent(m, "", "  ")
	fmt.Println(string(buf))
}

Last updated