# Conflux-ABIGEN

Conflux-Abigen is migrated from Ethereum tool [Abigen](https://geth.ethereum.org/docs/dapp/native-bindings).

Conflux-Abigen, like Abigen, is a source code generator to convert Conflux contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://docs.soliditylang.org/en/develop/abi-spec.html) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see Ethereum [Native DApps](https://geth.ethereum.org/docs/dapp/native-bindings) page for details.

This page introduces the concept of server-side native Dapps: Go language bindings to any Conflux contract that is compiled-time type-safe, highly performant, and best of all, can be generated fully automatically from a contract ABI and optionally the CVM bytecode.

## How to use

[Example](https://github.com/conflux-fans/conflux-abigen-example)

### Install

install from remote

```
$ go install github.com/Conflux-Chain/go-conflux-sdk/cmd/cfxabigen
```

or install from local

1. download the code

```
$ git clone https://github.com/Conflux-Chain/go-conflux-sdk.git
```

2. install

```
$ go install ./cmd/cfxabigen
```

The exectuion file named `cfxabigen` will be installed to `$GOPATH/bin/cfxabigen`

### Generating the bindings

The single essential thing needed to generate a Go binding to a Conflux contract is the contract's ABI definition `JSON` file. For our [`Token`](https://github.com/conflux-fans/conflux-abigen-example/blob/main/token/token.sol) contract tutorial you can obtain this by compiling the Solidity code yourself

To generate a contract abi, simply call:

```
solc --abi token.sol -o ./token
```

This command will generate contract abi json and write the result in to specified path (MyERC20Token.abi is created under token directory for this case)

To generate a binding, simply call:

```
$ cfxabigen --abi ./token/MyERC20Token.abi --pkg main --type MyERC20Token --out token.go
```

when you need to deploy it, you should generate a binding with bytecode:

```
$ cfxabigen --abi ./token/MyERC20Token.abi --bin token.bin --pkg main --type MyERC20Token --out token.go
```

Where the flags are:

* `--abi`: Mandatory file to the contract ABI to bind to
* `--bin`: binary file Path to the Conflux contract bytecode (generate deploy method)
* `--type`: Optional Go type name to assign to the binding struct
* `--pkg`: Mandatory Go package name to place the Go code into
* `--out`: Optional output path for the generated Go source file (default: stdout)

This will generate a type-safe Go binding for the Token contract. The generated code will look something like [`token.go`](https://gist.github.com/karalabe/5839509295afa4f7e2215bc4116c7a8f),

### Prepare

Interact with a contract using binding, you need to use a backend that implements functions including RPC invoking and transaction signing, generally create a new sdk.Client instance.

### Deploy contract

Interacting with existing contracts is nice, but let’s take it up a notch and deploy a brand new contract onto the Conflux blockchain! To do so, however, the contract ABI we used to generate the binding is not enough. We need the compiled bytecode too to allow deploying it.

```
$ cfxabigen --abi token.abi  --bin token.bin --pkg main --out token.go
```

This will generate something similar to token.go. If you quickly skim this file, you’ll find an extra `DeployToken` function that was just injected compared to the previous code. Besides all the parameters specified by Solidity, it also needs the usual authorization options to deploy the contract with and the Conflux backend to deploy the contract through.

```golang
package main

import (
	"fmt"
	"log"
	"math/big"
	"time"

	"abigen/bind"

	sdk "github.com/Conflux-Chain/go-conflux-sdk"
	"github.com/Conflux-Chain/go-conflux-sdk/types"
	"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
	"github.com/sirupsen/logrus"
)

func main() {
	client, err := sdk.NewClient("https://test.confluxrpc.com", sdk.ClientOption{
		KeystorePath: "../keystore",
		Logger:       os.Stdout,
	})
	if err != nil {
		log.Fatal(err)
	}

	err = client.AccountManager.UnlockDefault("hello")
	if err != nil {
		log.Fatal(err)
	}

	oneCfx := new(big.Int).Mul(big.NewInt(1e9), big.NewInt(1e9))
	tx, hash, _, err := DeployMyERC20Token(nil, client, new(big.Int).Mul(big.NewInt(1000000), oneCfx), "ABC", 18, "ABC")
	if err != nil {
		panic(err)
	}

	receipt, err := client.WaitForTransationReceipt(*hash, time.Second)
	if err != nil {
		panic(err)
	}

	logrus.WithFields(logrus.Fields{
		"tx":               tx,
		"hash":             hash,
		"contract address": receipt.ContractCreated,
	}).Info("deploy token done")
```

### Accessing an Conflux contract

To interact with a contract deployed on the blockchain, you'll need to know the `address` of the contract itself, and need to specify a `backend` through which to access Conflux.

```golang
package main

import (
	"fmt"
	"log"
	"math/big"
	"time"

	"abigen/bind"

	sdk "github.com/Conflux-Chain/go-conflux-sdk"
	"github.com/Conflux-Chain/go-conflux-sdk/types"
	"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
	"github.com/sirupsen/logrus"
)

func main() {
	client, err := sdk.NewClient("https://test.confluxrpc.com", sdk.ClientOption{
		KeystorePath: "../keystore",
	})
	if err != nil {
		panic(err)
	}

	err = client.AccountManager.UnlockDefault("hello")
	if err != nil {
		panic(err)
	}

	contractAddr := cfxaddress.MustNew("cfxtest:acd7apn6pnfhna7w1pa8evzhwhv3085vjjp1b8bav5")
	instance, err := NewMyERC20Token(contractAddr, client)
	if err != nil {
		panic(err)
	}

	user := cfxaddress.MustNew("cfxtest:aasfup1wgjyxkzy3575cbnn87xj5tam2zud125twew")
	result, err := instance.BalanceOf(nil, user.MustGetCommonAddress())
	if err != nil {
		panic(err)
	}

	logrus.WithField("balance", result).WithField("user", user).Info("access contract") // "bar"
}
```

### Transacting with an Conflux contract

```golang
package main

import (
	"fmt"
	"log"
	"math/big"
	"time"

	"abigen/bind"

	sdk "github.com/Conflux-Chain/go-conflux-sdk"
	"github.com/Conflux-Chain/go-conflux-sdk/types"
	"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
	"github.com/sirupsen/logrus"
)

func main() {
	client, err := sdk.NewClient("https://test.confluxrpc.com", sdk.ClientOption{
		KeystorePath: "../keystore",
	})
	if err != nil {
		panic(err)
	}

	err = client.AccountManager.UnlockDefault("hello")
	if err != nil {
		panic(err)
	}

	contractAddr := cfxaddress.MustNew("cfxtest:acd7apn6pnfhna7w1pa8evzhwhv3085vjjp1b8bav5")

	instance, err := NewMyERC20Token(contractAddr, client)
	if err != nil {
		panic(err)
	}

	to := cfxaddress.MustNew("cfxtest:aasfup1wgjyxkzy3575cbnn87xj5tam2zud125twew")
	tx, hash, err := instance.Transfer(nil, to.MustGetCommonAddress(), big.NewInt(1))
	if err != nil {
		panic(err)
	}

	logrus.WithField("tx", tx).WithField("hash", hash).Info("transfer")
	receipt, err := client.WaitForTransationReceipt(*hash, time.Second)
	if err != nil {
		panic(err)
	}

	logrus.WithField("transfer receipt", receipt).Info()
}
```

### Batch accessing an Conflux contract

1. New `BulkCaller`
2. There is a struct called `XXXBulkCaller` _(XXX means your contract name)_ for bulk call contract methods in contract binding struct
3. `XXXBulkCaller.YourContractMethod` to append request to its first parameter which is BulkSender instance, and the returned result and error arepointersr for saving results after requests be sent.
4. The result and error pointer of step 2 are filled by request results
5. `BulkCaller.Clear` to clear request cache for new bulk call action.

> see example from [example-bulk](https://github.com/conflux-fans/go-conflux-sdk-examples/tree/main/example_bulk)

### Batch transacting with an Conflux contract

1. New `BulkSender`
2. There is a struct called `XXXBulkTransactor` _(XXX means your contract name)_ for bulk send contract transactions in contract binding struct
3. `BulkSender.SignAndSend` to send requests. The transaction hashes and errors will be returned. All of them are slice with the same length of appended transactions.
4. `BulkSender.Clear` to clear request cache for new bulk send action.

> see example from [example-bulk](https://github.com/conflux-fans/go-conflux-sdk-examples/tree/main/example_bulk)

### Watch event

```golang
package main

import (
	"log"
	"math/big"
	"time"

	"abigen/bind"

	sdk "github.com/Conflux-Chain/go-conflux-sdk"
	"github.com/Conflux-Chain/go-conflux-sdk/middleware"
	"github.com/Conflux-Chain/go-conflux-sdk/types"
	"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
	"github.com/ethereum/go-ethereum/common"
	"github.com/sirupsen/logrus"
)

func main() {
	client, err := sdk.NewClient("ws://test.confluxrpc.com/ws", sdk.ClientOption{
		KeystorePath: "../keystore",
	})
	if err != nil {
		panic(err)
	}

	err = client.AccountManager.UnlockDefault("hello")
	if err != nil {
		panic(err)
	}

	contractAddr := cfxaddress.MustNew("cfxtest:acd7apn6pnfhna7w1pa8evzhwhv3085vjjp1b8bav5")
	instance, err := NewMyERC20Token(contractAddr, client)
	if err != nil {
		panic(err)
	}

	eventCh := make(chan *MyERC20TokenTransferOrChainReorg, 100)
	sub, err := instance.WatchTransfer(nil, eventCh, nil, nil)
	if err != nil {
		panic(err)
	}

	for {
		select {
		case l, ok := <-eventCh:
			if l.ChainReorg == nil {
				logrus.WithFields(logrus.Fields{
					"log": l.Event,
					"ok":  ok,
				}).Info("receive setted log")
			} else {
				logrus.WithFields(logrus.Fields{
					"reorg": l.ChainReorg,
					"ok":    ok,
				}).Info("receive setted log")
			}

		case err := <-sub.Err():
			panic(err)
		}
	}
}
```

### Filter event

```golang
package main

import (
	"log"
	"math/big"
	"time"

	"abigen/bind"

	sdk "github.com/Conflux-Chain/go-conflux-sdk"
	"github.com/Conflux-Chain/go-conflux-sdk/middleware"
	"github.com/Conflux-Chain/go-conflux-sdk/types"
	"github.com/Conflux-Chain/go-conflux-sdk/types/cfxaddress"
	"github.com/ethereum/go-ethereum/common"
	"github.com/sirupsen/logrus"
)

func main() {
	client, err := sdk.NewClient("https://test.confluxrpc.com", sdk.ClientOption{
		KeystorePath: "../keystore",
		Logger:       os.Stdout,
	})
	if err != nil {
		panic(err)
	}

	err = client.AccountManager.UnlockDefault("hello")
	if err != nil {
		panic(err)
	}

	contractAddr := cfxaddress.MustNew("cfxtest:acd7apn6pnfhna7w1pa8evzhwhv3085vjjp1b8bav5")
	instance, err := NewMyERC20Token(contractAddr, client)
	if err != nil {
		panic(err)
	}

	start := big.NewInt(35779622)
	end := big.NewInt(35779722)

	it, err := instance.FilterTransfer(&bind.FilterOpts{
		Start: types.NewEpochNumber(types.NewBigIntByRaw(start)),
		End:   types.NewEpochNumber(types.NewBigIntByRaw(end)),
	}, []common.Address{common.HexToAddress("0x1502ADd5a4a14c85C525e30a850c58fA15325f8C")}, nil,
	)

	if err != nil {
		panic(err)
	}

	for {
		if it.Next() {
			logrus.WithField("Transfer", it.Event).Info("Transfer log")
		} else {
			if err := it.Error(); err != nil {
				panic(err)
			}
			return
		}
	}
}
```

### Project integration (i.e. `go generate`)

The `abigen` command was made in such a way as to play beautifully together with existing Go toolchains: instead of having to remember the exact command needed to bind a Conflux contract into a Go project, we can leverage `go generate` to remember all the nitty-gritty details.

Place the binding generation command into a Go source file before the package definition:

```
//go:generate cfxabigen --abi token.abi --bin token.bin --pkg main --type token --out token.go
```

for example

```golang
//go:generate cfxabigen --abi token.abi --bin token.bin --pkg main --type token --out token.go
package main

func main() {
	...
}
```

After which whenever the Solidity contract is modified, instead of needing to remember and run the above command, we can simply call `go generate` on the package (or even the entire source tree via `go generate ./...`), and it will correctly generate the new bindings for us.
