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.