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