1. Understand the basic operation process of the chain
2. Understand contracts and perform contract-related operations on the chain
3. Interact with the chain using golang
Note: 1.The testrpc in this article is https://test1.thinkiumrpc.net, also has other available test RPCs as follows https://test2.thinkiumrpc.net、https://test103.thinkiumrpc.net ,Readers can choose by themselves. 2.The source code clone address of this article is https://github.com/ThinkiumGroup/web3go-demo.git
- The basic golang environment, I won't go into details here
- Basic git environment
- Basic abigen compilation environment
- Basic solc compilation environment (installation on the Internet by yourself)
- Blockchain browser
- Tap Link
-
Clone the base project with git
-
configure abigen
go get -u github.com/ethereum/go-ethereum cd $GOPATH/src/github.com/ethereum/go-ethereum/ make make devtools
Note: protoc needs to be installed in advance. The tools generated after make devtools are completed are in $GOPATH/bin/abigen, and the path is introduced into the system variable PATH to test the command
-
Initialize the project
-
Project Structure Analysis
- The RPC_URL in the const of eth in eth is the RPC address
- examples
- account contains test methods for creating wallets and main currency transfers
- chain contains the test method for querying the current block height
- contract
- Contains deploy and call contract test methods
- storage.go is the generated file (details can be viewed below)
- lib is the go file generated using bin and abi
- The bin and abi in the storage in resources use solc to compile the sol file, which is the source file for this test
-
First prepare a Solidity source code file, this test file - Storage.sol in Storage in the project resource
-
Then use solc to compile the file to generate data (refer to the command: sudo solc --bin --abi Storage.sol), the results are as follows
solc --bin --abi Storage.sol ======= Storage.sol:Storage ======= Binary: 608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea26469706673582212205fd13809e018341b527cfb6c32c0bb14338d2116c0487c547c4faea7f246e16464736f6c63430008090033 Contract JSON ABI [{"inputs":[],"name":"retrieve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"num","type":"uint256"}],"name":"store","outputs":[],"stateMutability":"nonpayable","type":"function"}]Below Binary is the bin of the contract, which is bytecode, and after ABI is the abi compiled by the contract.
-
After the result is obtained, the bytecode is stored in a file, and the abi is also stored in another file (note that it is an empty file), such as Storage.bin and Storage.abi in the project
-
Use abigen to compile the file to generate the go class file, the command is as follows
sudo abigen --abi=Storage.abi --bin=Storage.bin --pkg=main --out=storage.go
-
At this point, the preparation work is complete, and then start the test
-
First determine the Rpc address in the calling package
-
included in the project
-
wallet creation
-
func TestAccount(t *testing.T) { //get account pv, err := crypto.GenerateKey() if err != nil { panic(err) } privateKey := hex.EncodeToString(crypto.FromECDSA(pv)) //c1f14e4132c1858b390ff169dd045082b9ba1ca022de641e7aa24b0322510499 fmt.Println("privateKey: ", privateKey) // change to your rpc provider web3, err := web3.NewWeb3(eth.RPC_URL) if err != nil { panic(err) } err = web3.Eth.SetAccount(privateKey) if err != nil { panic(err) } //get address //0x901F21a0a09536F24feF8f5565eBcacB7aC5DE31 fmt.Println("address: ", web3.Eth.Address()) }
-
The result is as follows
-
API server listening at: [::]:41549 === RUN TestAccount privateKey: 50014d49478a00f62f775e015e389bd73dede68726a14d4f8b28253165bff23a address: 0x2aE433ae1C9e34BdD48296fB7BCFE8a6E60c7d38
-
-
After the address is created go to Tap Linktake the test tkm
-
-
Transfer function (main currency tkm transfer)
-
func TestSendTransaction(t *testing.T) { // change to your rpc provider web3, err := web3.NewWeb3(eth.RPC_URL) if err != nil { panic(err) } web3.Eth.SetAccount("c1f14e4132c1858b390ff169dd045082b9ba1ca022de641e7aa24b0322510499") balance, err := web3.Eth.GetBalance(web3.Eth.Address(), nil) if err != nil { panic(err) } fmt.Println("balance: ", balance) nonce, err := web3.Eth.GetNonce(web3.Eth.Address(), nil) if err != nil { panic(err) } fmt.Println("Latest nonce: ", nonce) //transfer //set chainId web3.Eth.SetChainId(50001) // transfer 1 tkm from 0x901F21a0a09536F24feF8f5565eBcacB7aC5DE31 to 0x90021a2CcA84a0611363ec1f9ccb36B4761DE08E hash, err := web3.Eth.SendRawEIP1559Transaction(common.HexToAddress("0x90021a2CcA84a0611363ec1f9ccb36B4761DE08E"), new(big.Int).SetInt64(1000000000000000000),300000,new(big.Int).SetInt64(300000),new(big.Int).SetInt64(300000),nil) if err != nil { panic(err) } fmt.Println("hash: ", hash) }
-
The result is as follows:
-
API server listening at: [::]:38469 === RUN TestSendTransaction balance: 97315968400000000000 Latest nonce: 24 hash: 0x3c781964d927a2194897830cce8d89bdf2e20a9ff3ddd0fa3cda7b3f3f6adc39 --- PASS: TestSendTransaction (0.05s) PASS
-
-
-
deploy contract
func TestDeployContract(t *testing.T) { // change to your rpc provider web3, err := web3.NewWeb3(eth.RPC_URL) if err != nil { panic(err) } privateKey, err := crypto.HexToECDSA("c1f14e4132c1858b390ff169dd045082b9ba1ca022de641e7aa24b0322510499") if err != nil { panic(err) } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { fmt.Println("error casting public key to ECDSA") } fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) nonce, err := client.PendingNonceAt(context.Background(), fromAddress) if err != nil { panic(err) } gasPrice, err := client.SuggestGasPrice(context.Background()) if err != nil { panic(err) } //set chainID auth, err := bind.NewKeyedTransactorWithChainID(privateKey,new(big.Int).SetInt64(50001)) if err != nil { panic(err) } auth.Nonce = big.NewInt(int64(nonce)) auth.Value = big.NewInt(0) // in wei auth.GasLimit = uint64(300000) // in units auth.GasPrice = gasPrice address, tx, instance, err :=DeployMain(auth,client) if err != nil { panic(err) } fmt.Println("contract:"+address.Hex()) fmt.Println("deploy hash:"+tx.Hash().String()) _, _ = instance, tx }
- To get the contract address, subsequent operations need to use the contract address, test execution, and get the contract address as0x63C21800Fc7970D796811ac21d5ca42688382195,The result is as follows
-
API server listening at: [::]:38869 === RUN TestDeployContract contract:0x63C21800Fc7970D796811ac21d5ca42688382195 deploy hash:0x59fe057a32e6650ae5134ae2d9a61d884d05f5f687828ea79d146f4fc747f6d8 --- PASS: TestDeployContract (0.04s) PASS
-
- To get the contract address, subsequent operations need to use the contract address, test execution, and get the contract address as0x63C21800Fc7970D796811ac21d5ca42688382195,The result is as follows
-
contract call
-
func TestCallContract(t *testing.T) { // change to your rpc provider web3, err := web3.NewWeb3(eth.RPC_URL) if err != nil { panic(err) } //load contract 0x771B03a85843907B89B66dbbCD90E1b8761a4fa3 main, err := NewMain(common.HexToAddress("0x797C88225547126879aA4EF444A66D627E402366"), client) if err != nil { panic(err) } num, err := main.Retrieve(nil) if err != nil { panic(err) } fmt.Printf("num:%d", num) fmt.Println("") //write contract privateKey, err := crypto.HexToECDSA("c1f14e4132c1858b390ff169dd045082b9ba1ca022de641e7aa24b0322510499") if err != nil { panic(err) } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) if !ok { fmt.Println("error casting public key to ECDSA") } fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) nonce, err := client.PendingNonceAt(context.Background(), fromAddress) if err != nil { panic(err) } gasPrice, err := client.SuggestGasPrice(context.Background()) if err != nil { panic(err) } chainId,err := client.ChainID(context.Background()) //set chainID auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainId) if err != nil { panic(err) } auth.Nonce = big.NewInt(int64(nonce)) auth.Value = big.NewInt(0) // in wei auth.GasLimit = uint64(300000) // in units auth.GasPrice = gasPrice main.Store(auth,new(big.Int).SetInt64(1000)) }
-
In the above method, we implemented 2 methods, namely read and write
-
After NewMain, you can perform some operations on the contract
-
Get the default variable value set by the contract main.Retrieve(nil) This is the read method
-
main.Store(auth, new(big.Int).SetInt64(10)) resets the value in the contract, which is the writing method
-
Note: In the middle sleep time, because the transaction needs to be confirmed, it is not possible to read directly after writing.
-
The result is as follows
-
API server listening at: [::]:40991 === RUN TestCallContract num:100 newNum:1000 --- PASS: TestCallContract (5.06s) PASS
-
It can be clearly seen that the default value is 100, and the query after writing is 1000
-
-
-
-
The above is the simple process and method of using golang to interact with the thinkium chain. I hope you have a happy programming!