Function selector calls for interacting with smart contracts using go

There are many ways for go to interact with smart contracts, one of which can be called without knowing the source code of the contract, let's learn together.

 1. First, let's install go-ethereum

go get -u github.com/ethereum/go-ethereum

2. Create a new main.go file and add dependencies

import (
	"context"
	"crypto/ecdsa"
	"fmt"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"
	"math/big"
	"os"
)

3. Define constants

const (
	privateKey      = "你的钱包私钥"
	contractAddress = "调用合约地址"
	toAddress       = "接收转账地址" //这里我使用transfer方法作为案例,所以需要一个接收转账地址
)

4. Define the calling function

func transfer(client *ethclient.Client, privateKey, toAddress, contract string) (string, error) {}

4.1. First deduce the public key from the private key, and then deduce the wallet address from the public key

	//从私钥推导出 公钥
	privateKeyECDSA, err := crypto.HexToECDSA(privateKey)
	if err != nil {
		fmt.Println("crypto.HexToECDSA error ,", err)
		return "", err
	}
	publicKey := privateKeyECDSA.Public()
	publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
	if !ok {
		fmt.Println("publicKeyECDSA error ,", err)
		return "", err
	}
	//从公钥推导出钱包地址
	fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
	fmt.Println("钱包地址:", fromAddress.Hex())

4.2. Construct request parameters

    var data []byte
    methodName := crypto.Keccak256([]byte("transfer(address,uint256)"))[:4]
    paddedToAddress := common.LeftPadBytes(common.HexToAddress(toAddress).Bytes(), 32)
    amount, _ := new(big.Int).SetString("100000000000000000000", 10)
    paddedAmount := common.LeftPadBytes(amount.Bytes(), 32)
    data = append(data, methodName...)
    data = append(data, paddedToAddress...)
    data = append(data, paddedAmount...)

methodName: This is the function selector of the contract method we want to call. The meaning of this definition is to encrypt the calling method (excluding definition fields and spaces) with the keccak256 method, and take the first 4 bytes. Example format: 0xa9059cbb

paddedToAddress: Convert toAddress to address type first, and then fill it with 0 to 32 bits on the left. Example format: 0000000000000000000000002228e53c4642dedae43f498eeda78b23571b84d8

amount : defines a large decimal number, that is, the amount we need to transfer, here is 1000 * 10^18, this depends on how many digits the decimal defined by the transfer token is

paddedAmount: Same as paddedToAddress , it is also padded with 0 on the left to 32 bits. Example format: 0000000000000000000000000000000000000000000000056bc75e2d63100000

Finally, append methodName, paddedToAddress, and paddedAmount to the byte array to form the final data parameter

4.3. Construct transaction objects

    //获取nonce
	nonce, err := client.NonceAt(context.Background(), fromAddress, nil)
	if err != nil {
		return "", err
	}
	//获取小费
	gasTipCap, _ := client.SuggestGasTipCap(context.Background())
	//transfer 默认是 使用 21000 gas
	gas := uint64(100000)
	//最大gas fee
	gasFeeCap := big.NewInt(38694000460)

	contractAddress := common.HexToAddress(contract)
	//创建交易
	tx := types.NewTx(&types.DynamicFeeTx{
		Nonce:     nonce,
		GasTipCap: gasTipCap,
		GasFeeCap: gasFeeCap,
		Gas:       gas,
		To:        &contractAddress,
		Value:     big.NewInt(0),
		Data:      data,
	})

Here we use the DynamicFeeTx structure to construct the transaction object, which mainly supports the EIP-1559 protocol and can dynamically set Max Priority (miner's tip) and Max Gas Fee (maximum gas fee)

Among them , Value indicates the amount of the original currency to be transferred (for example, if we are on the eth main network, that is the amount of eth to be transferred by the token)

4.4. Transaction signature/send transaction

    // 获取当前区块链的ChainID
	chainID, err := client.ChainID(context.Background())
	if err != nil {
		fmt.Println("获取ChainID失败:", err)
		return "", err
	}

	fmt.Println("当前区块链的ChainID:", chainID)
	//创建签名者
	signer := types.NewLondonSigner(chainID)
	//对交易进行签名
	signTx, err := types.SignTx(tx, signer, privateKeyECDSA)
	if err != nil {
		return "", err
	}
	//发送交易
	err = client.SendTransaction(context.Background(), signTx)
	if err != nil {
		return "", err
	}

Because we use DynamicFeeTx to create transaction objects, we need to use NewLondonSigner to build the signer here

4.5, call the function in the main function

func main() {
	client, err := ethclient.Dial("区块链rpc地址")
	if err != nil {
		fmt.Println("ethclient.Dial error : ", err)
		os.Exit(0)
	}
	tx, err := transfer(client, privateKey, toAddress, contractAddress)
	if err != nil {
		fmt.Println("transfer error : ", err)
		os.Exit(0)
	}

	fmt.Println("transfer tx : ", tx)

}

Fill in the specific node address of the chain you want to call in the blockchain rpc address, if not, you can  create it through INFURA

5. Execute the main method

The goerli test chain of Ethereum I used here is tested. See that our code above has been successfully executed. Let's go to the blockchain browser to have a look

 

 

It can be seen that the blockchain has confirmed our transaction

In this tutorial, we learned how to use go to call contract methods without knowing the source code of the contract. If you have any questions during the learning process, you can leave me a message on the official account. I will reply you as soon as I see it. , In addition, the official account will also share cutting-edge information about blockchain and web3 from time to time. Interested friends can stay tuned

 Please pay attention to the official account: Web3_preacher ( web3_preacher ) , reply "go contract call" to receive the complete code

Guess you like

Origin blog.csdn.net/rao356/article/details/131962039