[Solidity+Go] geth로 스마트 컨트랙트 배포하기 - 3. 생성된 Go 코드로 스마트 컨트랙트 배포

geth로 스마트 컨트랙트 배포하기 · 3/7
  1. 1. 초기 구성
  2. 2. 스마트 컨트랙트로 Go 코드 생성
  3. 3. 생성된 Go 코드로 스마트 컨트랙트 배포
  4. 4. 배포된 스마트 컨트랙트와 상호작용
  5. 5. 이벤트 구독
  6. 6. 메타데이터를 사용한 standard json input 생성
  7. 7. standard json input을 사용해 배포한 컨트랙트 검증

1. 로컬 테스트넷 실행

$ anvil

2. 최상위 경로에 .env 파일 생성

PRIVATE_KEY=<anvil 실행 시 제공되는 프라이빗 키 중 하나>
RPC_ENDPOINT=http://localhost:8545

이 때 PRIVATE\_KEY가 0x로 시작하면 파싱에서가 발생하므로 0x를 지워준 16진수값만 넣어줍니다.


3. 배포 코드 작성

cmd/deploy/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/accounts/abi/bind"
	"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")

	// 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))

	// Deploy the contract with initial supply of 1,000,000 tokens
	initialSupply := big.NewInt(0).Mul(big.NewInt(1000000), big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18), nil))

	_, tx, _, err := token.DeployToken(signer, client, initialSupply)
	handleError(err)

	fmt.Printf("Transaction hash: %s\n", tx.Hash().Hex())

	// Wait for the deployment to be mined
	contractAddress, err := bind.WaitDeployed(ctx, client, tx)
	handleError(err)

	fmt.Printf("Contract deployed! Contract address: %s\n", contractAddress.Hex())
}

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)
	}
}

.env 파일 자동으로 불러오기

import (
	...

	_ "github.com/joho/godotenv/autoload"

	...
)

테스트넷 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")

16진수 프라이빗 키를 ECDSA 프라이빗 키로 파싱

// Parse wallet private key
privateKey := mustParsePrivateKey()
func mustParsePrivateKey() *ecdsa.PrivateKey {
	rawPrivateKey := os.Getenv("PRIVATE_KEY")

	// Parse the private key
	privateKey, err := crypto.HexToECDSA(rawPrivateKey)
	handleError(err)

	return privateKey
}

트랜잭션 서명자 생성

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))

abigen으로 생성된 코드를 통해 스마트 컨트랙트 배포

// Deploy the contract with initial supply of 1,000,000 tokens
initialSupply := big.NewInt(0).Mul(big.NewInt(1000000), big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18), nil))

_, tx, _, err := token.DeployToken(signer, client, initialSupply)
handleError(err)

fmt.Printf("Transaction hash: %s\n", tx.Hash().Hex())

// Wait for the deployment to be mined
contractAddress, err := bind.WaitDeployed(ctx, client, tx)
handleError(err)

fmt.Printf("Contract deployed! Contract address: %s\n", contractAddress.Hex())

4. 스마트 컨트랙트 배포

$ go run ./cmd/deploy/
Successfully connected to Ethereum client
Deploying contract from address 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
Suggested gas price: 2000000000
Chain ID: 31337
Transaction hash: 0x9b3b8b92cf9370e0a51c4f1f35726385839ef4aba748c42f22d7327f00cca5ad
Contract deployed! Contract address: 0x5FbDB2315678afecb367f032d93F642f64180aa3

5. 전체 코드