Skip to content

Substrate Contract SDK for Golang As a part of Himalia

License

Notifications You must be signed in to change notification settings

patractlabs/go-patract

Repository files navigation

Himalia PatractGo

Substrate Contract SDK for Golang As a part of Himalia


PatractGo is a Golang contract SDK. In addition to supporting the basic interactive API with the contract, it mainly supports the development of micro-services based on the contract status. For example, for common ERC20 contracts, a service can be developed based on PatractGo to synchronize all transfer information to the database, and based on the database to implemente the statistics and processing of transfer data. In addition, developers can also develop some command-line tools based on Golang to assist in testing and debugging.

PatractGo will be based on Centrifuge's GSRPC, which is a Go sdk for Substrate.

Element Group for disscusion: https://app.element.io/#/room/#PatractLabsDev:matrix.org

Intruduction

Most contract behaviors are highly related to context. In addition to interacting with the chain, user-oriented contract applications also need to provide users with current relevant context status information:

+--DAPP-Front-End--------------+        +---Chain-------------------------+
|                              |        |                                 |
| +----+  +------------------+ |        | +-------+     +-------+         |
| |    |  |                  | | Commit | |       |     |       |         |
| |    |  |   Polkadot-JS    +------------> Node  +---->+ Node  |         |
| |    +->+                  | |   Tx   | |       |     |       |         |
| |    |  |                  | |        | +-------+     +----+-++         |
| |    |  +------------------+ |        |                    ^ |          |
| | UI |                       |        +---------------------------------+
| |    |  +------------------+ |                             | |
| |    |  |                  | |        +--DAPP-Server--------------------+
| |    |  |                  | |  Push  | +--------+     +-----v-------+  |
| |    +<-+   Model          +<-----------+        +-----+             |  |
| |    |  |                  | |        | | Server |     |  PatractGo  |  |
| |    |  |                  +------------>        +-----+             |  |
| +----+  +------------------+ | Query  | +----+---+     +-----+-------+  |
+------------------------------+        |      |               |          |
                                        |      |         +-----v-------+  |
                                        |      |         |             |  |
                                        |      +-------->+   DataBase  |  |
                                        |                |             |  |
                                        |                +-------------+  |
                                        |                                 |
                                        +---------------------------------+

PatractGo is mainly responsible for implementing micro-services in a DApp. Unlike querying the state of the chain API, PatractGo can monitor the calls and events generated by the specified contract. Developers can obtain the state storage based on this information to maintain consistent state with the chain. Through data services based on a typical API-DB architecture, the front-end DApp can efficiently and concisely obtain the state on the chain as context information.

Based on the API of chain nodes, PatractGo obtains block information and summarizes and filters it, and sends contract-related messages and events based on metadata analysis to the handler protocol specified by the developer. For example, for a typical ERC20 contract, the developer can use the channel to subscribe to all transfer events that occur, and then synchronize them into the database, so that other microservices can provide services corresponding to the token data of the account, such as querying the current token holding distribution and other logics.

Therefor, PatractGo will achieve the following support:

  • Complete the secondary packaging of the contract module interface, complete operations such as put_code, call, instantiate, etc.
  • Parse the metadata.json information of the contract, and support the automatic generation of http service interface for the metadata corresponding contract
  • Scanning and monitoring support of the contract status on the chain for statistics and analysis
  • Basic command line tool support for native interaction with the contract, mainly used to test the security of the contract
  • SDK development examples for ERC20 contract support

Getting Start

PatractGo based on GSRPC, So we need install some depends:

First is subkey(This dependency could be removed after GSRPC marge pr#114):

cargo install --force subkey --git https://github.com/paritytech/substrate --version 2.0.0
subkey --version

For Now, the sdk examples will connect to the europa, and also need cli tools:

> git clone --recurse-submodules https://github.com/patractlabs/europa.git
## or do following commands
> git clone https://github.com/patractlabs/europa.git
> cd europa/vendor
> git submodule update --init --recursive
> cargo build

For some examples, we can simply run a europa node:

europa --dev --tmp

use for external port:

europa --dev --tmp --ws-external

We advise developers to use jupiter for debugging their code.

# build jupiter
git clone --recurse-submodules https://github.com/patractlabs/jupiter.git
cd jupiter
cargo build

For jupiter or europa, please read their README to provide their suitable "extending types" for go sdk. More information refers to go-substrate-rpc-client#hdr-Types

Design

PatractGo consists of the following packages:

  • patractgo/metadata contract metadata processing, and metadata-based contract processing
  • patractgo/rpc/native re-encapsulation of the contract module interface to provide the contract-related interaction based on chain RPC
  • patractgo/rpc implement the interaction with the contract based on metadata
  • patractgo/rest implements an http service based on metadata to interact with the contract
  • patractgo/observer Monitoring and Scanning support for contract status on the chain
  • patractgo/contracts/erc20 supports ERC20 contracts and examples
  • patractgo/tools some tools for contracts develop

Currently, we haven't designed the module which could auto-gen code for a contract based on a metadata, thus we provide patractgo/contracts/erc20 as an example to show how to warp a contract as a go source file.

This auto contract code generator feature would be developed with java-patract repo later (in next version), for their have same logic to generate the code for contracts.

Test

PatractGo use go test to test, but as need europa environments, so need run test in one process:

Test Environment need add europa to bash PATH.

go test -v -p 1 ./...

The unittest will start a europa process for test, if need use a europa start by localhost, can use this:

europa --dev --tmp --ws-external
go test ./contracts/erc20/ -v -p 1 -v -args "extern" -run TestTransfer

This will run TestTransfer test to europa

Usage

PatractGo divides into 3 parts:

  • Contracts: provides the functions to react with contracts.
  • Rest: provides a way to generate an offline signature for contracts.
  • Observer: listens contract events and parse events by contract metadata.

Contracts

Also can read: Transfer test.

Currently, contracts part just parses metadata and uniforms interface to react with contracts. Based on the generic interface, the auto contract code generator could be implemented easily in the future. Thus, if developers want to react with their contracts by PatractGo, they need to create some wrapper functions for contracts, just like what PatractGo do in contracts/erc20.

This process is duplicated for every contract, we would provide auto contract code generator to simplify this process.

Contracts Code

Put Code:

   // read the code wasm from file
   codeBytes, err := ioutil.ReadFile("/path/to/contracts.wasm")
   if err != nil {
      return err
   }

   // create the api
   cApi, err := rpc.NewContractAPI(env.URL())
   
   // read the abi(metadata) for contract
   metaBz, err := ioutil.ReadFile("/path/to/contracts_metadata.json")
   cApi.WithMetaData(metaBz)

   // create context with from auth, like Alice
   ctx := api.NewCtx(context.Background()).WithFrom(authKey)

   // put code
   _, err = cApi.Native().PutCode(ctx, codeBytes)
   
   // do next steps

Get Code:

   codeHash := readCodeHash() // get code hash

   var codeBz []byte
   
	if err := cApi.Native().Cli.GetStorageLatest(&codeBz,
		"Contracts", "PristineCode",
		[]byte(codeHash), nil); err != nil {
		return err
   }
   
   // codeBz is now code

Instantiate

	var endowment uint64 = 1000000000000

	// Instantiate
	_, contractAccount, err := cApi.Instantiate(ctx,
		types.NewCompactBalance(endowment),
		types.NewCompactGas(test.DefaultGas),
		contracts.CodeHashERC20,
		types.NewU128(totalSupply),
   )

api will return contractAccount, which can use to call the contract.

Call

For a contract, we can read or exec messages:

Read:

Read the total_supply of ERC20 contract, no request params:

	var res types.U128

	err := a.CallToRead(ctx,
		&res,
		a.ContractAccountID,
		[]string{"total_supply"},
	)

Read the balance_of of AccountID for ERC20 contract:

	req := struct {
		Address types.AccountID
	}{
		Address: owner,
	}

	var res types.U128

	err := a.CallToRead(ctx,
		&res,
		ContractAccountIDForERC20,
		[]string{"balance_of"},
		req,
	)

Exec:

Call transfer:

	toParam := struct {
		Address AccountID
	}{
		Address: to,
	}

	valueParam := struct {
		Value U128
	}{
		Value: amt,
	}

	return a.CallToExec(ctx,
		a.ContractAccountID,
		types.NewCompactBalance(0),
		types.NewCompactGas(test.DefaultGas),
		[]string{"transfer"},
		toParam, valueParam,
	)

Rest

We can use rest to get unsigned raw byte data for contract call, it can help to build an offline signature for contract.

can use this for example: rest

start the rest server:

go run ./examples/rest 

to get data:

curl -X POST \
  'http://localhost:8899/erc20/exec/transfer?isOffline=true&contract=5HKinTRKW9THEJxbQb22Nfyq9FPWNVZ9DQ2GEQ4Vg1LqTPuk' \
  -H 'content-type: application/json' \
  -d '{
	"nonce":1,
	"chain_status":{
		"spec_version":1,
		"tx_version":1,
		"block_hash":"0xc20f241b61039e5685d118c7fbc8b27210153c21eee7686a9466f22e01281114",
		"genesis_hash":"0xc20f241b61039e5685d118c7fbc8b27210153c21eee7686a9466f22e01281114"
	},
	"contract":"5HKinTRKW9THEJxbQb22Nfyq9FPWNVZ9DQ2GEQ4Vg1LqTPuk",
	"origin":"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY",
	"gas_limit":"500000000000",
	"args":{
		"to":"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty",
		"value":"100000000"
	}
}'

Observer

For a contract, we need observer events for the contract, can use observer to build a contract events observer service:

example: observer

Thanks

About

Substrate Contract SDK for Golang As a part of Himalia

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages