티스토리 뷰
0. 이전 게시글
1. 코드 작성
cmd/interact/main.go
package main
import (
"context"
"crypto/ecdsa"
"fmt"
token "go-ethereum-example/gen"
"math/big"
"os"
_ "github.com/joho/godotenv/autoload"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Connect to Ethereum client with RPC endpoint
client, err := ethclient.DialContext(ctx, os.Getenv("RPC_ENDPOINT"))
handleError(err)
defer client.Close()
fmt.Println("Successfully connected to Ethereum client")
// Change these addresses to match your contract!
contractAddress := common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
toAddress := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
// Create an instance of the contract, specifying its address
tokenInstance, err := token.NewToken(contractAddress, client)
handleError(err)
// Parse wallet private key
privateKey := mustParsePrivateKey()
address := crypto.PubkeyToAddress(privateKey.PublicKey)
// Get nonce, gas price and chain ID
nonce, err := client.PendingNonceAt(context.Background(), address)
handleError(err)
gasPrice, err := client.SuggestGasPrice(context.Background())
handleError(err)
fmt.Printf("Suggested gas price: %s\n", gasPrice)
chainID, err := client.NetworkID(context.Background())
handleError(err)
fmt.Printf("Chain ID: %d\n", chainID)
// Create an transactor with the private key, chain ID and nonce
signer, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
handleError(err)
signer.GasPrice = gasPrice
signer.GasLimit = 3000000
signer.Nonce = big.NewInt(int64(nonce))
// Call transfer method (state-changing)
tx, err := tokenInstance.Transfer(signer, toAddress, big.NewInt(1000000))
handleError(err)
fmt.Printf("Transaction hash: %s\n", tx.Hash().Hex())
// Wait for the transaction to be mined
receipt, err := bind.WaitMined(context.Background(), client, tx)
handleError(err)
fmt.Printf("Transaction receipt status %d\n", receipt.Status)
// If the transaction was reverted by the EVM, we can see the reason
if receipt.Status == 0 {
msg := ethereum.CallMsg{
From: address,
To: tx.To(),
Gas: tx.Gas(),
GasPrice: tx.GasPrice(),
Value: tx.Value(),
Data: tx.Data(),
}
_, err = client.CallContract(ctx, msg, nil)
fmt.Printf("Transaction reverted: %v\n", err)
return
}
// Extract the transfer event from the receipt
var transferred *token.TokenTransfer
for _, log := range receipt.Logs {
transferred, err = tokenInstance.ParseTransfer(*log)
if err == nil {
break
}
}
if transferred != nil {
fmt.Printf("Transferred %d tokens from %s to %s\n", transferred.Value, transferred.From.Hex(), transferred.To.Hex())
}
// Call the contract method (read-only)
toBalance, err := tokenInstance.BalanceOf(&bind.CallOpts{Context: ctx}, toAddress)
handleError(err)
fmt.Printf("To balance: %d\n", toBalance)
}
func mustParsePrivateKey() *ecdsa.PrivateKey {
rawPrivateKey := os.Getenv("PRIVATE_KEY")
// Parse the private key
privateKey, err := crypto.HexToECDSA(rawPrivateKey)
handleError(err)
return privateKey
}
func handleError(err error) {
if err != nil {
panic(err)
}
}
테스트넷 RPC 클라이언트 연결
// Connect to Ethereum client with RPC endpoint
client, err := ethclient.DialContext(ctx, os.Getenv("RPC_ENDPOINT"))
handleError(err)
defer client.Close()
fmt.Println("Successfully connected to Ethereum client")
배포된 스마트 컨트랙트의 인스턴스 생성
// Change these addresses to match your contract!
contractAddress := common.HexToAddress("0x5FbDB2315678afecb367f032d93F642f64180aa3")
toAddress := common.HexToAddress("0x70997970C51812dc3A010C7d01b50e0d17dc79C8")
// Create an instance of the contract, specifying its address
tokenInstance, err := token.NewToken(contractAddress, client)
handleError(err)
contractAddress: 배포된 스마트 컨트랙트의 주소
toAddress: transfer 함수의 대상 주소, 로컬 테스트넷 실행 시에 제공되는 두 번째 주소를 사용함
16진수 프라이빗 키를 ECDSA 프라이빗 키로 파싱
// Parse wallet private key
privateKey := mustParsePrivateKey()
트랜잭션 서명자 생성
address := crypto.PubkeyToAddress(privateKey.PublicKey)
fmt.Printf("Deploying contract from address %s\n", address.Hex())
// Get nonce, gas price and chain ID from the Ethereum client
nonce, err := client.PendingNonceAt(ctx, address)
handleError(err)
gasPrice, err := client.SuggestGasPrice(ctx)
handleError(err)
fmt.Printf("Suggested gas price: %s\n", gasPrice)
chainID, err := client.NetworkID(ctx)
handleError(err)
fmt.Printf("Chain ID: %d\n", chainID)
// Create an signer with the private key, chain ID and nonce
signer, err := bind.NewKeyedTransactorWithChainID(privateKey, chainID)
handleError(err)
signer.GasPrice = gasPrice
signer.GasLimit = 3000000
signer.Nonce = big.NewInt(int64(nonce))
transfer 함수 실행 및 마이닝 된 트랜잭션 상태 확인
// Call transfer method (state-changing)
tx, err := tokenInstance.Transfer(signer, toAddress, big.NewInt(1000000))
handleError(err)
fmt.Printf("Transaction hash: %s\n", tx.Hash().Hex())
// Wait for the transaction to be mined
receipt, err := bind.WaitMined(context.Background(), client, tx)
handleError(err)
fmt.Printf("Transaction receipt status %d\n", receipt.Status)
// If the transaction was reverted by the EVM, we can see the reason
if receipt.Status == 0 {
msg := ethereum.CallMsg{
From: address,
To: tx.To(),
Gas: tx.Gas(),
GasPrice: tx.GasPrice(),
Value: tx.Value(),
Data: tx.Data(),
}
_, err = client.CallContract(ctx, msg, nil)
fmt.Printf("Transaction reverted: %v\n", err)
return
}
로그에서 Transfer 이벤트 추출
// Extract the transfer event from the receipt
var transferred *token.TokenTransfer
for _, log := range receipt.Logs {
transferred, err = tokenInstance.ParseTransfer(*log)
if err == nil {
break
}
}
if transferred != nil {
fmt.Printf("Transferred %d tokens from %s to %s\n", transferred.Value, transferred.From.Hex(), transferred.To.Hex())
}
토큰을 전송받은 주소의 잔액 확인
// Call the contract method (read-only)
toBalance, err := tokenInstance.BalanceOf(&bind.CallOpts{Context: ctx}, toAddress)
handleError(err)
fmt.Printf("To balance: %d\n", toBalance)
2. 코드 실행
$ go run ./cmd/interact/
2023/12/12 13:38:36 Successfully connected to Ethereum client
Suggested gas price: 1879547559
Chain ID: 31337
Transaction hash: 0x8336dceaef677f437377c6c90caf4ba15c3851c8f386c060e386fab5f90df64c
Transaction receipt status 1
Transferred 1000000 tokens from 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 to 0x70997970C51812dc3A010C7d01b50e0d17dc79C8
To balance: 1000000
3. 트랜잭션이 revert되는 경우
함수 호출에 잘못된 인수 전달
tx, err := tokenInstance.Transfer(signer, toAddress, big.NewInt(-1))
코드 실행
$ go run ./cmd/interact/
Successfully connected to Ethereum client
Suggested gas price: 1879547559
Chain ID: 31337
Transaction hash: 0x428986a4b3faa46db5e354fc218ed7b31ff09706e00204fd30340fe401bcd81e
Transaction receipt status 0
Transaction reverted: execution reverted
4. 전체 코드
'Solidity' 카테고리의 다른 글
[Solitidy+Go] geth로 스마트 컨트랙트 배포하기 - 6. 메타데이터를 사용한 standard json input 생성 (0) | 2023.12.15 |
---|---|
[Solitidy+Go] geth로 스마트 컨트랙트 배포하기 - 5. 이벤트 구독 (0) | 2023.12.14 |
[Solitidy+Go] geth로 스마트 컨트랙트 배포하기 - 3. 생성된 Go 코드로 스마트 컨트랙트 배포 (0) | 2023.12.14 |
[Solitidy+Go] geth로 스마트 컨트랙트 배포하기 - 2. 스마트 컨트랙트로 Go 코드 생성 (0) | 2023.12.13 |
[Solitidy+Go] geth로 스마트 컨트랙트 배포하기 - 1. 초기 구성 (0) | 2023.12.13 |