เรียน Golang วันที่ 6 - Ethereum Development with Go
วันนี้ลองเขียน Golang เพื่อทำการสร้าง Wallet Address และลองทำการส่ง ETH ไปให้อีก 1 wallet โดยการใช้ Testnet
พอดีว่าผมมีความสนใจด้าน Blockchain / Ethereum ด้วยส่วนนึง และปกติก็ใช้แค่ ethers.js ในการเป็น Client เชื่อมต่อกับทาง Blockchain ส่วน Node รู้แค่ว่าเค้าน่าจะใจ geth
กัน ซึ่งเป็นภาษา Go
วันนี้ก็เลยนั่งลองอ่านบทความคร่าวๆ จากเว็บ Ethereum Development with Go

ลองต่อ Public Node ด้วย Go
ผมลองทำการ connect Public RPC ด้วยการใช้เว็บ RPC Info หรือถ้าใครใช้ Local node ก็รันด้วย Foundry Anvil ได้เช่นกัน

ส่วน Go หลักๆ ก็เป็น ตัว ethclient ครับ
go mod init go-eth-101
go get github.com/ethereum/go-ethereum/ethclient
ผมมี 2 nodes คือ
- Local Node รันด้วย Anvil ได้ URL คือ
http://localhost:8545
- RPC Node : Sepolia Testnet URL คือ
https://rpc.sepolia.org
จากนั้น สร้าง main.go
เพื่อลองต่อ RPC Node
package main
import (
"fmt"
"log"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
localNodeUrl := "http://localhost:8545"
// sepoliaUrl := "https://rpc.sepolia.org"
client, err := ethclient.Dial(localNodeUrl)
if err != nil {
log.Fatal(err)
}
fmt.Println("RPC Connected!")
_ = client
}
ลองสร้าง New Wallet ด้วย Go
สร้าง function เอาไว้ generate Wallet
package main
import (
"crypto/ecdsa"
"fmt"
"log"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
func newWallet() {
privateKey, err := crypto.GenerateKey()
if err != nil {
log.Fatal(err)
}
privateKeyBytes := crypto.FromECDSA(privateKey)
fmt.Println("Private Key : ", hexutil.Encode(privateKeyBytes))
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
fmt.Println("Public Key :", hexutil.Encode(publicKeyBytes)[4:])
address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
fmt.Println("Address : ", address)
}
func main() {
// localNodeUrl := "http://localhost:8545"
sepoliaUrl := "https://rpc.sepolia.org"
client, err := ethclient.Dial(sepoliaUrl)
if err != nil {
log.Fatal(err)
}
fmt.Println("RPC Connected!")
_ = client
newWallet()
}
ลองรัน แล้วได้ผลลัพธ์แบบนี้
RPC Connected!
Private Key : <PRIVATE_KEY>
Public Key : <PUBLIC_KEY>
Address : 0xEef7020F55A53B1227BE3ACe2514C392c40b8Bfc
ลองเอา Address ไปรับ Faucet ซักนิด

โอน ETH ไปให้อีก Account
ทำการลองโอน ETH จากกระเป๋า ที่สร้างใหม่ กระเป๋า 1 ไปกระเป๋า 2
package main
import (
"context"
"crypto/ecdsa"
"fmt"
"log"
"math/big"
"os"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func newWallet() {
privateKey, err := crypto.GenerateKey()
if err != nil {
log.Fatal(err)
}
privateKeyBytes := crypto.FromECDSA(privateKey)
fmt.Println("Private Key : ", hexutil.Encode(privateKeyBytes))
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
publicKeyBytes := crypto.FromECDSAPub(publicKeyECDSA)
fmt.Println("Public Key :", hexutil.Encode(publicKeyBytes)[4:])
address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex()
fmt.Println("Address : ", address)
}
// add amount as parameter
func transferTo(to string) {
// localNodeUrl := "http://localhost:8545"
sepoliaUrl := "https://rpc.sepolia.io"
client, err := ethclient.Dial(sepoliaUrl)
if err != nil {
log.Fatal(err)
}
fmt.Println("RPC Connected!")
privateKey, err := crypto.HexToECDSA(os.Getenv("PRIVATE_KEY"))
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("error casting public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatal(err)
}
// send 0.1eth (in wei)
value := big.NewInt(100000000000000000)
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatal(err)
}
toAddress := common.HexToAddress(to)
var data []byte
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
chainID, err := client.NetworkID(context.Background())
if err != nil {
log.Fatal(err)
}
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
log.Fatal(err)
}
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
}
func main() {
// newWallet() // create new wallet, no need client
transferTo("<YOUR_WALLET>")
}
ทดสอบโอนเงิน 0.1 ETH
PRIVATE_KEY=<YOUR_PRIVATE_KEY> go run main.go
เรียบร้อย เราสามารถโอน ETH ไปให้อีกกระเป๋า ผ่าน go ได้แล้ว 🎉
Source Codeสรุป
วันนี้ได้ลองฝึกใช้ Go แม้ว่าจะไม่ได้เรียนรู้อะไรใหม่ๆ เท่าไหร่ มีเรื่อง context
ที่ไม่เข้าใจ ฮ่าๆ แต่ก็ได้รู้วิธีการ generate key และ transfer ETH ถ้าเทียบกัน ส่วนตัวก็ยังรู้สึกว่า ethers.js ใช้งานง่ายกว่า และไม่ยุ่งยาก เพราะ ethers มันจัดการให้หมดแล้ว ส่วน go อันนี้ ต้องมาคำนวณ gas price สร้าง tx มา sign แล้วก็ send tx เองหมดเลย ก็สนุกดี
Happy Coding ❤️
References

