github.com/mavryk-network/mvgo@v1.19.9/README.md (about)

     1  ## Blockwatch MvGo - Mavryk Go SDK
     2  
     3  MvGo is [Blockwatch](https://blockwatch.cc)'s low-level Tezos Go SDK for reliable, high-performance applications. This SDK is free to use in commercial and non-commercial projects with a permissive license. Blockwatch is committed to keeping interfaces stable, providing long-term support, and updating MvGo on a regular basis to stay compliant with the most recent Tezos network protocol.
     4  
     5  MvGo's main focus is on **correctness**, **stability**, and **compliance** with Tezos mainnet. It supports binary and JSON encodings for all Tezos types including Micheline smart contract data and all transaction formats. It's an ideal fit for high-performance applications that read from and write to the Tezos blockchain.
     6  
     7  Current Mavryk protocol support in MvGo
     8  
     9  - Boreas v002
    10  - Atlas v001
    11  
    12  ### SDK features
    13  
    14  MvGo contains a full set of features to read, monitor, decode, translate, analyze and debug data from the Tezos blockchain, in particular from Tezos smart contracts:
    15  
    16  - a low-level **Types library** `mvgo/mavryk` to handle hashes, addresses, keys, signatures other types found on-chain
    17  - a powerful **Micheline library** `mvgo/micheline` to decode and translate data found in smart contract calls, storage, and bigmaps
    18  - an **RPC library** `mvgo/rpc` for accessing the Tezos Node RPC
    19  - a **Codec library** `mvgo/codec` to construct and serialize operations
    20  - a **Contract library** `mvgo/contract` for smart contract calls and tokens
    21  - a **Signer library** `mvgo/signer` to sign transactions local or remote
    22  - helpers like an efficient base58 en/decoder, hash maps, etc
    23  - a **Code generator** [TzGen](https://github.com/blockwatch-cc/mvgo/tree/master/cmd/tzgen) to produce pure Go clients for smart contract interfaces
    24  - an **Automation Tool** [TzCompose](https://github.com/blockwatch-cc/mvgo/tree/master/cmd/tzcompose) to setup test cases and deploy complex contract ecosystems
    25  
    26  ### MvGo Compatibility
    27  
    28  MvGo's RPC package attempts to be compatible with all protocols so that reading historic block data is always supported. Binary transaction encoding and signing support is limited to the most recent protocol.
    29  
    30  We attempt to upgrade MvGo whenever new protocols are proposed and will add new protocol features as soon as practically feasible and as demand for such features exists. For example, we don't fully Sapling and BLS signatures yet, but may add support in the future.
    31  
    32  ### Usage
    33  
    34  ```sh
    35  go get -u github.com/mavryk-network/mvgo
    36  ```
    37  
    38  Then import, using
    39  
    40  ```go
    41  import (
    42  	"github.com/mavryk-network/mvgo/codec"
    43  	"github.com/mavryk-network/mvgo/mavryk"
    44  	"github.com/mavryk-network/mvgo/micheline"
    45  	"github.com/mavryk-network/mvgo/rpc"
    46  	"github.com/mavryk-network/mvgo/wallet"
    47  )
    48  ```
    49  
    50  ### Micheline Support
    51  
    52  Tezos uses [Micheline](https://protocol.mavryk.org/shell/micheline.html) for encoding smart contract data and code. The positive is that Micheline is strongly typed, the downside is that it's complex and has a few ambiguities that make it hard to use. MvGo contains a library that lets you decode, analyze and construct compliant Micheline data structures from Go.
    53  
    54  Micheline uses basic **primitives** for encoding types and values. These primitives can be expressed in JSON and binary format and MvGo 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.
    55  
    56  MvGo defines a basic `Prim` data type to work with Micheline primitives directly:
    57  
    58  ```go
    59  type Prim struct {
    60  	Type      PrimType // primitive type
    61  	OpCode    OpCode   // primitive opcode (invalid on sequences, strings, bytes, int)
    62  	Args      []Prim   // optional nested arguments
    63  	Anno      []string // optional type annotations
    64  	Int       *big.Int // decoded value when Prim is an int
    65  	String    string   // decoded value when Prim is a string
    66  	Bytes     []byte   // decoded value when Prim is a byte sequence
    67  	WasPacked bool     // true when content has been unpacked
    68  }
    69  ```
    70  
    71  Since Micheline value encoding is quite verbose and can be ambiguous, MvGo supports **unfolding** of raw Micheline using the following MvGo wrapper types and their helper functions like `Map()`, `GetInt64()`, `GetAddress()`:
    72  
    73  - `Type` is a MvGo wrapper for simple or complex primitives which contain annotated type info
    74  - `Value` is a MvGo wrapper for simple or complex primitives representing Micheline values in combination with their Type
    75  - `Key` is a MvGo wrapper for special comparable values that are used as maps or bigmap keys
    76  
    77  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). MvGo supports `Unpack()` and `UnpackAll()` functions on primitives and values and also detects the internal type of packed data which is necessary for unfolding.
    78  
    79  
    80  ### Examples
    81  
    82  Below are a few examples showing how to use MvGo to easily access Tezos data in your application.
    83  
    84  #### Parsing an address
    85  
    86  To parse/decode an address and output its components you can do the following:
    87  
    88  ```go
    89  import "github.com/mavryk-network/mvgo/mavryk"
    90  
    91  // parse and panic if invalid
    92  addr := mavryk.MustParseAddress("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9")
    93  
    94  // parse and return error if invalid
    95  addr, err := mavryk.ParseAddress("tz3RDC3Jdn4j15J7bBHZd29EUee9gVB1CxD9")
    96  if err != nil {
    97  	fmt.Printf("Invalid address: %v\n", err)
    98  }
    99  
   100  // Do smth with the address
   101  fmt.Printf("Address type = %s\n", addr.Type)
   102  fmt.Printf("Address bytes = %x\n", addr.Hash)
   103  
   104  ```
   105  
   106  See [examples/addr.go](https://github.com/blockwatch-cc/mvgo/blob/master/examples/addr/main.go) for more.
   107  
   108  #### Monitoring for new blocks
   109  
   110  A Tezos node can notify applications when new blocks are attached to the chain. The Tezos RPC calls this monitor and technically it's a long-poll implementation. Here's how to use this feature in MvGo:
   111  
   112  ```go
   113  import "github.com/mavryk-network/mvgo/rpc"
   114  
   115  // init SDK client
   116  c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)
   117  
   118  // create block header monitor
   119  mon := rpc.NewBlockHeaderMonitor()
   120  defer mon.Close()
   121  
   122  // all SDK functions take a context, here we just use a dummy
   123  ctx := context.TODO()
   124  
   125  // register the block monitor with our client
   126  if err := c.MonitorBlockHeader(ctx, mon); err != nil {
   127  	log.Fatalln(err)
   128  }
   129  
   130  // wait for new block headers
   131  for {
   132  	head, err := mon.Recv(ctx)
   133  	if err != nil {
   134  		log.Fatalln(err)
   135  	}
   136  
   137  	// do smth with the block header
   138  	fmt.Printf("New block %s\n", head.Hash)
   139  }
   140  
   141  ```
   142  
   143  #### Fetch and decode contract storage
   144  
   145  ```go
   146  import (
   147  	"github.com/mavryk-network/mvgo/micheline"
   148  	"github.com/mavryk-network/mvgo/rpc"
   149  	"github.com/mavryk-network/mvgo/mavryk"
   150  )
   151  
   152  // we use the Baker Registry on mainnet as example
   153  addr := mavryk.MustParseAddress("KT1ChNsEFxwyCbJyWGSL3KdjeXE28AY1Kaog")
   154  
   155  // init RPC client
   156  c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)
   157  
   158  // fetch the contract's script and most recent storage
   159  script, _ := c.GetContractScript(ctx, addr)
   160  
   161  // unfold Micheline storage into human-readable form
   162  val := micheline.NewValue(script.StorageType(), script.Storage)
   163  m, _ := val.Map()
   164  buf, _ := json.MarshalIndent(m, "", "  ")
   165  fmt.Println(string(buf))
   166  ```
   167  
   168  #### Decode contract call parameters
   169  
   170  ```go
   171  import (
   172  	"github.com/mavryk-network/mvgo/micheline"
   173  	"github.com/mavryk-network/mvgo/rpc"
   174  	"github.com/mavryk-network/mvgo/mavryk"
   175  )
   176  
   177  // init RPC client
   178  c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)
   179  
   180  // assuming you have this transaction
   181  tx := block.Operations[3][0].Contents[0].(*rpc.TransactionOp)
   182  
   183  // load the contract's script for type info
   184  script, err := c.GetContractScript(ctx, tx.Destination)
   185  
   186  // unwrap params for nested entrypoints
   187  ep, param, err := tx.Parameters.MapEntrypoint(script.ParamType())
   188  
   189  // convert Micheline param data into human-readable form
   190  val := micheline.NewValue(ep.Type(), param)
   191  
   192  // e.g. access individual nested fields using value helpers
   193  from, ok := val.GetAddress("transfer.from")
   194  ```
   195  
   196  #### Use MvGo's Value API
   197  
   198  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.
   199  
   200  There are multiple options to access decoded data:
   201  
   202  ```go
   203  // 1/
   204  // decode into a Go type tree using `Map()` which produces
   205  // - `map[string]interface{}` for records and maps
   206  // - `[]interface{}` for lists and sets
   207  // - `time.Time` for time values
   208  // - `string` stringified numbers for Nat, Int, Mumav
   209  // - `bool` for Booleans
   210  // - `mavryk.Address` for any string or bytes sequence that contains an address
   211  // - `mavryk.Key` for keys
   212  // - `mavryk.Signature` for signatures
   213  // - `mavryk.ChainIdHash` for chain ids
   214  // - hex string for untyped bytes
   215  // - opcode names for any Michelson opcode
   216  // - opcode sequences for lambdas
   217  m, err := val.Map()
   218  buf, err := json.MarshalIndent(m, "", "  ")
   219  fmt.Println(string(buf))
   220  
   221  // when you know the concrete type you can cast directly
   222  fmt.Println("Value=", m.(map[string]interface{})["transfer"].(map[string]interface{})["value"])
   223  
   224  // 2/
   225  // access individual nested fields using value helpers (`ok` is true when the field
   226  // exists and has the correct type; helpers exist for
   227  //   GetString() (string, bool)
   228  //   GetBytes() ([]byte, bool)
   229  //   GetInt64() (int64, bool)
   230  //   GetBig() (*big.Int, bool)
   231  //   GetBool() (bool, bool)
   232  //   GetTime() (time.Time, bool)
   233  //   GetAddress() (mavryk.Address, bool)
   234  //   GetKey() (mavryk.Key, bool)
   235  //   GetSignature() (mavryk.Signature, bool)
   236  from, ok := val.GetAddress("transfer.from")
   237  
   238  // 3/
   239  // unmarshal the decoded Micheline parameters into a json-tagged Go struct
   240  type FA12Transfer struct {
   241      From  mavryk.Address `json:"from"`
   242      To    mavryk.Address `json:"to"`
   243      Value int64         `json:"value,string"`
   244  }
   245  
   246  // FA1.2 params are nested, so we need an extra wrapper
   247  type FA12TransferParams struct {
   248  	Transfer FA12Transfer `json:"transfer"`
   249  }
   250  
   251  var transfer FA12TransferParams
   252  err := val.Unmarshal(&transfer)
   253  ```
   254  
   255  #### List a contract's bigmaps
   256  
   257  ```go
   258  import (
   259  	"context"
   260  
   261  	"github.com/mavryk-network/mvgo/rpc"
   262  	"github.com/mavryk-network/mvgo/mavryk"
   263  )
   264  
   265  // we use the hic et nunc NFT market on mainnet as example
   266  addr := mavryk.MustParseAddress("KT1Hkg5qeNhfwpKW4fXvq7HGZB9z2EnmCCA9")
   267  
   268  // init RPC client
   269  c, _ := rpc.NewClient("https://rpc.tzpro.io", nil)
   270  ctx := context.TODO()
   271  
   272  // fetch the contract's script and most recent storage
   273  script, _ := c.GetContractScript(ctx, addr)
   274  
   275  // bigmap pointers as named map[string]int64 (names from type annotations)
   276  // Note that if a bigmap is anonymous, e.g. in a list, a temporary name will
   277  // be returned here
   278  named := script.Bigmaps()
   279  
   280  // bigmap pointers as []int64
   281  ids := []int64{}
   282  for _, v := range named {
   283  	ids = append(ids, v)
   284  }
   285  ```
   286  
   287  #### Fetch and decode bigmap values
   288  
   289  ```go
   290  // init RPC client
   291  c, _ := rpc.NewClient("https://rpc.tzstats.com", nil)
   292  
   293  // load bigmap type info (use the Baker Registry on mainnet as example)
   294  biginfo, _ := c.GetBigmapInfo(ctx, 17)
   295  
   296  // list all bigmap keys
   297  bigkeys, _ := c.GetBigmapKeys(ctx, 17)
   298  
   299  // visit each value
   300  for _, key := range bigkeys {
   301  	bigval, _ := c.GetBigmapValue(ctx, 17, key)
   302  
   303  	// unfold Micheline type into human readable form
   304  	val := micheline.NewValue(micheline.NewType(biginfo.ValueType), bigval)
   305  	m, _ := val.Map()
   306  	buf, _ := json.MarshalIndent(m, "", "  ")
   307  	fmt.Println(string(buf))
   308  }
   309  ```
   310  
   311  #### Custom RPC client configuration
   312  
   313  MvGo's `rpc.NewClient()` function takes an optional Go `http.Client` as parameter which you can configure before or after passing it to the library. The example below shows how to set custom timeouts and disable TLS certificate checks (not recommended in production, but useful if you use self-signed certificates during testing).
   314  
   315  
   316  ```go
   317  import (
   318  	"crypto/tls"
   319  	"log"
   320  	"net"
   321  	"net/http"
   322  
   323  	"github.com/mavryk-network/mvgo/rpc"
   324  )
   325  
   326  
   327  func main() {
   328  	hc := &http.Client{
   329  		Transport: &http.Transport{
   330  			Dial: (&net.Dialer{
   331  				Timeout:   2 * time.Second,
   332  				KeepAlive: 180 * time.Second,
   333  			}).Dial,
   334  			TLSClientConfig: &tls.Config{
   335  				InsecureSkipVerify: true,
   336  			}
   337  		}
   338  	}
   339  
   340  	c, err := rpc.NewClient("https://my-private-node.local:8732", hc)
   341  	if err != nil {
   342  		log.Fatalln(err)
   343  	}
   344  }
   345  ```
   346  
   347  
   348  ## License
   349  
   350  The MIT License (MIT) Copyright (c) 2020-2024 Blockwatch Data Inc.
   351  
   352  Permission is hereby granted, free of charge, to any person obtaining a copy
   353  of this software and associated documentation files (the "Software"), to deal
   354  in the Software without restriction, including without limitation the rights
   355  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   356  copies of the Software, and to permit persons to whom the Software is furnished
   357  to do so, subject to the following conditions:
   358  
   359  The above copyright notice and this permission notice shall be included in all
   360  copies or substantial portions of the Software.
   361  
   362  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   363  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   364  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   365  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   366  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   367  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
   368  THE SOFTWARE.