Prerequisites
This page is fairly beginner-friendly and designed for people starting out with writing Go native dapps. The core concepts will be introduced gradually as a developer would encounter them. However, some basic familiarity with Ethereum, Solidity and Go is assumed.What is an ABI?
Parallax smart contracts have a schema that defines its functions and return types in the form of a JSON file. This JSON file is known as an Application Binary Interface, or ABI. The ABI acts as a specification for precisely how to encode data sent to a contract and how to decode the data the contract sends back. The ABI is the only essential piece of information required to generate Go bindings. Go developers can then use the bindings to interact with the contract from their Go application without having to deal directly with data encoding and decoding. An ABI is generated when a contract is compiled.Abigen: Go binding generator
The Parallax client includes a source code generator calledabigen
that can convert Parallax ABI definitions into easy to use, type-safe Go packages. With a valid Go development environment set up and the Parallax repository checked out correctly, abigen can be built as follows:
Generating the bindings
To demonstrate the binding generator a contract is required. The contractStorage.sol
implements two very simple functions: store
updates a user-defined uint256
to the contract’s storage, and retrieve
displays the value stored in the contract to the user. The Solidity code is as follows:
Storage.sol
.
The following code snippet shows how an ABI can be generated for Storage.sol
using the Solidity compiler solc.
Storage.sol
(Storage.abi
) looks as follows:
--abi
: Mandatory path to the contract ABI to bind to--pkg
: Mandatory Go package name to place the Go code into--type
: Optional Go type name to assign to the binding struct--out
: Optional output path for the generated Go source file (not set = stdout)
Storage.go
contains all the bindings required to interact with Storage.sol
from a Go application. However, this isn’t very useful unless the contract is actually deployed on Parallax mainnet or one of Parallax’s testnets. The following sections will demonstrate how to deploy the contract to the Parallax testnet and interact with it using the Go bindings.
Deploying contracts to Ethereum
In the previous section, the contract ABI was sufficient for generating the contract bindings from its ABI. However, deploying the contract requires some additional information in the form of the compiled bytecode. The bytecode is obtained by running the compiler again but this passing the--bin
flag, e.g.
abigen
can be run again, this time passing Storage.bin
:
DeployStorage
function has been injected:
DeployStorage()
function can be used to deploy the contract to the Parallax testnet from a Go application. To do this requires incorporating the bindings into a Go application that also handles account management, authorization and Parallax backend to deploy the contract through. Specifically, this requires:
- A running Parallax client connected to the Parallax testnet
- An account in the keystore prefunded with enough Laxes to cover gas costs for deploying and interacting with the contract
prlclient
can be instantiated with the local Parallax node’s ipc file, providing access to the testnet from the Go application. The key can be instantiated as a variable in the application by copying the JSON object from the keyfile in the keystore.
Putting it all together would result in:
Storage
contract on the Testnet blockchain. The contract functions can be called while the contract is waiting to be included in a block.
DeployStorage
returns four variables:
address
: the deployment address of the contracttx
: the transaction hash that can be queried using the Parallax clientinstance
: an instance of the deployed contract whose functions can be called in the Go applicationerr
: a variable that handles errors in case of a deployment failure
Accessing a Parallax contract
To interact with a contract already deployed on the blockchain, the deployment address is required and a backend through which to access Parallax must be defined. The binding generator provides an RPC backend out-of-the-box that can be used to attach to an existing Parallax node via IPC, HTTP or WebSockets. As in the previous section, a Parallax node running on the Parallax testnet and an account with some test Laxes to cover gas is required. TheStorage.sol
deployment address is also needed.
Again, an instance of prlclient
can be created, passing the path to the Parallax client’s ipc file. In the example below this backend is assigned to the variable conn.
Storage
contract are defined in Storage.go
. To create a new instance of the contract in a Go application, the NewStorage()
function can be used. The function is defined in Storage.go
as follows:
NewStorage()
takes two arguments: the deployment address and a backend (conn
) and returns an instance of the deployed contract. In the example below, the instance is assigned to store.
value
stored in the contract, the contract’s Retrieve()
function can be called. Again, the function is defined in Storage.go
as follows:
Retrieve()
function requires a parameter to be passed, even though the original Solidity contract didn’t require any at all none. The parameter required is a *bind.CallOpts
type, which can be used to fine tune the call. If no adjustments to the call are required, pass nil
. Adjustments to the call include:
Pending
: Whether to access pending contract state or the current stable oneGasLimit
: Place a limit on the computing resources the call might consume
Retrieve()
function in the Go application:
Value: 56
Transacting with an Parallax contract
Invoking a method that changes contract state (i.e. transacting) is a bit more involved, as a live transaction needs to be authorized and broadcast into the network. Go bindings require local signing of transactions and do not delegate this to a remote node. This is to keep accounts private within dapps, and not shared (by default) between them. Thus to allow transacting with a contract, your code needs to implement a method that given an input transaction, signs it and returns an authorized output transaction. Since most users have their keys in the Web3 Secret Storage format, the bind package contains a small utility method (bind.NewTransactor(keyjson, passphrase)
) that can create an authorized transactor from a key file and associated password, without the user needing to implement key signing themselves.
Changing the previous code snippet to update the value stored in the contract:
*bind.TransactOpts
type, which authorizes the transaction and potentially fine tunes it:
From
: Address of the account to invoke the method with (mandatory)Signer
: Method to sign a transaction locally before broadcasting it (mandatory)Nonce
: Account nonce to use for the transaction ordering (optional)GasLimit
: Place a limit on the computing resources the call might consume (optional)GasPrice
: Explicitly set the gas price to run the transaction with (optional)Value
: Any funds to transfer along with the method call (optional)
bind
package if the auth options are constructed using bind.NewTransactor
. The nonce and gas related fields are automatically derived by the binding if they are not set. Unset values are assumed to be zero.
Pre-configured contract sessions
Reading and state modifying contract-calls require a mandatory first parameter which can authorize and fine tune some of the internal parameters. However, most of the time the same accounts and parameters will be used to issue many transactions, so constructing the call/transact options individually quickly becomes unwieldy. To avoid this, the generator also creates specialized wrappers that can be pre-configured with tuning and authorization parameters, allowing all the Solidity defined methods to be invoked without needing an extra parameter. These are named similarly to the original contract type name but suffixed withSessions
:
Bind Solidity directly
The compilation and binding steps can be joined together into a pipeline, for example:Project integration (go generate
)
The abigen
command was made in such a way as to integrate easily into existing Go toolchains: instead of having to remember the exact command needed to bind a Parallax contract into a Go project, go generate
can handle all the fine details.
Place the binding generation command into a Go source file before the package definition:
go generate
on the package (or even the entire source tree via go generate ./...
), and it will correctly generate the new bindings for us.
Blockchain simulator
Being able to deploy and access deployed Parallax contracts from native Go code is a powerful feature. However, using public testnets as a backend does not lend itself well to automated unit testing. Therefore, the Parallax client also implements a simulated blockchain that can be set as a backend to native contracts the same way as a live RPC backend, using the commandbackends.NewSimulatedBackend(genesisAccounts)
. The code snippet below shows how this can be used as a backend in a Go application.