HOME
BLOG
TEZOS
Introducing TzGo v1.12 for Tezos Ithaca
TzGo is a low-level Golang SDK for the Tezos blockchain. Now with Ithaca support, flexible signers and FA token standards.
Alexander EichhornRead more posts by this author.
In our last post we introduced TzGo v1 for Hangzhou which was the first release that also allowed developers to send transactions. With our new release you can more easily send smart contract calls, use custom signers and work with FA token standards. Oh, and it's also compatible with Ithaca. For documentation visit the TzGo docs page and for source code head over to our Github.
What's new?
At a glance, we have
added Ithaca support (mostly new hashes, RPC calls, and operations)
added generic smart contract call support and support for calling off-chain views
added support for FA1.2 and FA2 transfers, allowances and views
added cost estimation for gas, storage, fees and flexible configuration options to manage costs (i.e. overwrite simulated values)
improved Micheline encoding and decoding performance at least 2x by reimplementing the JSON encoder for primitives
written more examples to show how to use contract and operation sending features
Structural changes
With the introduction of Tenderbake in Ithaca there has been a multitude of changes to how transaction data is serialized (new operations and operation tags) and it makes less sense to support older protocols at the level of encoding/signing. TzGo now produces Ithaca compatible operation encodings by default. You can find (and change) the defaults at tezos/params.go:var ( // DefaultParams defines the blockchain configuration for Mainnet under the latest // protocol. It is used to generate compliant transaction encodings. To change, // either overwrite this default or set custom params per operation using // op.WithParams(). DefaultParams = NewParams(). ForNetwork(Mainnet). ForProtocol(ProtoV012_2). Mixin(&Params{ OperationTagsVersion: 2, MaxOperationsTTL: 120, HardGasLimitPerOperation: 1040000, HardGasLimitPerBlock: 5200000, OriginationSize: 257, CostPerByte: 250, HardStorageLimitPerOperation: 60000, MinimalBlockDelay: 30 * time.Second, }) )
The RPC package still supports all previous protocols, so that reading old transaction receipts remains possible. As rights and snapshots have significantly changed in Ithaca, the RPC package does a few more calls internally to determine which protocol was active at a requested block if that's important to fetch or decode the correct data.
It turned out that the wallet package we introduced in our tech preview last time was not so useful after all. We decided to move all its logic for completing, simulating and broadcasting transactions into the existing rpc package and added a new signer package with a public interface.type Signer interface { // Return a list of addresses the signer manages. ListAddresses(context.Context) ([]tezos.Address, error) // Returns the public key for a managed address. Required for reveal ops. GetKey(context.Context, tezos.Address) (tezos.Key, error) // Sign an arbitrary text message wrapped into a failing noop. SignMessage(context.Context, tezos.Address, string) (tezos.Signature, error) // Sign an operation. SignOperation(context.Context, tezos.Address, *codec.Op) (tezos.Signature, error) // Sign a block header. SignBlock(context.Context, tezos.Address, *codec.BlockHeader) (tezos.Signature, error) }
The signer is used by the rpc.Send() and contract.Call() family of functions, but you can always use it directly and attach the signature to an operation with op.WithSignature()
As initial signers we support an in-memory signer that can be created from a private key and a client for the Tezos remote signer interface. Feel free to add your own custom signer implementation, but it should be compatible with the interface above to use the convenience wrappers.
Sending Transactions
We made it easy to send simple transactions and even batches, but at the same time allow full control of all the details. The shortest complete example for transferring tez (ignoring errors) is this:import ( "context" "blockwatch.cc/tzgo/codec" "blockwatch.cc/tzgo/rpc" "blockwatch.cc/tzgo/signer" "blockwatch.cc/tzgo/tezos" ) // load key and receiver sk := tezos.MustParsePrivateKey("...private_key...") to := tezos.MustParseAddress("...receiver...") // create an RPC client c, _ := rpc.NewClient("https://rpc.tzstats.com", nil) // use private key to sign c.Signer = signer.NewFromKey(sk) // construct a transfer operation op := codec.NewOp().WithTransfer(to, 1_000_000) // send (will complete reveal, simulate cost, add cost, sign, broadcast, wait) rcpt, _ := c.Send(ctx, op, nil) // do smth with receipt, e.g. check for success, read costs _ = rcpt.IsSuccess() _ = rcpt.TotalCosts()
For more control, use optional features from the Op struct or provide extra call options as a third parameter to rpc.Client.Send():// Use different protocol parameters func (o *Op) WithParams(p *tezos.Params) *Op // Set a custom TTL or directly set the branch to use func (o *Op) WithTTL(n int64) *Op func (o *Op) WithBranch(hash tezos.BlockHash) *Op // Set custom gas, storage, fees for each operation in a batch func (o *Op) WithLimits(limits []tezos.Limits, margin int64) *Op // Use custom call options for rpc.Client.Send() type CallOptions struct { // number of confirmations to wait after broadcast Confirmations int64 // max acceptable fee, optional (default = 0) MaxFee int64 // max lifetime for operations in blocks TTL int64 // ignore simulated limits and use user-defined limits from op.WithLimits() IgnoreLimits bool // custom signer to use for signing the transaction Signer signer.Signer // custom address to sign for (use when signer manages multiple addresses) Sender tezos.Address // custom block observer for waiting on confirmations Observer *Observer }
TzGo simulates every operation before signing and then uses the simulated gas and storage costs to avoid rejection. In some cases, usually for high throughput dapps that send many transactions per block, it is possible that the Tezos node underestimates storage costs. This is a known and unresolved issue. If this happens frequently, please set custom fees/costs for such operations and disable the use of simulated values with CallOptions.IgnoreLimits = true (make sure you set a reasonable baker fee as well).
If no user-defined fees are set (default), TzGo calculates minimum fee using the minimal fee algorithm published here. Setting IgnoreLimits = true disables this behavior.
Calling Smart Contracts
TzGo v1.12 adds another package contract that simplifies working with smart contracts and FA tokens. It also allows you to batch several contract calls into the same transaction. A simple FA2 transfer can be implemented asimport ( "context" "blockwatch.cc/tzgo/codec" "blockwatch.cc/tzgo/rpc" "blockwatch.cc/tzgo/signer" "blockwatch.cc/tzgo/tezos" ) // create an RPC client c, _ := rpc.NewClient("https://rpc.tzstats.com", nil) // use private key to sign c.Signer = signer.NewFromKey(tezos.MustParsePrivateKey("SK...")) // constuct a new contract con := contract.NewContract(tezos.MustParseAddress("KT1..."), c) // construct an FA2 token (use token id 0) token := con.AsFA2(0) // construct simple transfer arguments args := token.Transfer( tezos.MustParseAddress("tz..."), // from tezos.MustParseAddress("tz..."), // to tezos.NewZ(1_000_000), // amount ) // execute the call (will complete reveal, simulate cost, add cost, sign, broadcast, wait) rcpt, _ := con.Call(ctx, args, nil)
In more complex scenarios you can add multiple transfers to the same FA2 transfer list// construct complex transfer args args := contract.NewFA2TransferArgs() args.WithTransfer(from_1, to_2, token_id_1, amount_1) args.WithTransfer(from_2, to_2, token_id_2, amount_2) // optional, optimize args (for minimal parameter size) args.Optimize() // execute the call rcpt, _ := con.Call(ctx, args, nil)
or you can batch multiple calls// append multiple calls (silly example, but you get the point) args := []contract.CallArguments{ fa2token.AddOperator(owner, operator), fa2token.Transfer(from, to, amount), fa2token.Transfer(from, to, amount), fa2token.RemoveOperator(owner, operator), } // execute all calls in a single batch transaction rcpt, _ := con.CallMulti(ctx, args, nil)
Outlook
With the current release, TzGo has all the tools you need to write complex Tezos applications in Go. A few things are left on our TODO list which will make TzGo even more useful in specific scenarios. Upcoming releases may include
a more powerful Observer that can filter by addresses and monitor the Tezos mempool (all the basics already exist in packages tezos and rpc)
create keys and signers from mnemonic seed phrases and faucets
helpers to access contract storage and bigmaps more easily
reading and writing contract/token metadata
Conclusion
If you like TzGo or if you have an idea on how we can improve or extend it in the future, please reach out on Twitter, Discord or email.
Happy building!
MORE IN TEZOS
Using Tezos in any application with Polywrap2 Feb 2022 – 3 min read
Announcing TzGo 1.015 Jan 2022 – 2 min read
Announcing Tezos Polywrapper24 Dec 2021 – 5 min read
See all 15 posts →TEZOSUsing Tezos in any application with PolywrapEasy integration of Tezos dapps into any application written in any language. Learn how to use Tezos Polywrappers.
Oliver Simon
OLIVER SIMON2 FEB 2022 • 3 MIN READBlockwatch Data Blog © 2022Latest PostsTwitterGhost











