[Solidity+Go] geth로 스마트 컨트랙트 배포하기 - 7. standard json input을 사용해 배포한 컨트랙트 검증

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

1. .env 파일 업데이트

PRIVATE_KEY=<YOUR_PRIVATE_KEY>
RPC_ENDPOINT=<YOUR_RPC_ENDPOINT>
RPC_WS_ENDPOINT=ws://localhost:8545
ETHERSCAN_API_KEY=<YOUR_ETHERSCAN_API_KEY>

Polygon Mumbai 테스트넷 사용

PRIVATE\_KEY : Polygon Mumbai 테스트 이더를 가지고 있는 지갑의 프라이빗 키
RPC_ENDPOINT : Alchemy에서 제공하는 Polygon Mumbai 테스트넷 RPC URL을 사용
ETHERSCAN_API_KEY : Polygonscan의 API 키


2. Polygon Mumbai 테스트넷에 컨트랙트 배포

$ go run ./cmd/deploy/

3. solc 버전 확인

$ solc --version
solc, the solidity compiler commandline interface
Version: 0.8.22+commit.4fc1097e.Linux.g++

4. 코드 작성

cmd/verify/main.go

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"math/big"
	"net/http"
	"net/url"
	"os"
	"time"

	"github.com/ethereum/go-ethereum/accounts/abi"
	_ "github.com/joho/godotenv/autoload"
)

var etherscanURL = "https://api-testnet.polygonscan.com/api"

func main() {
	// read input file
	f, err := os.Open("verify/MyToken_input.json")
	handleErr(err)

	defer f.Close()

	sourceCodeBytes, err := io.ReadAll(f)
	handleErr(err)

	// generate abi-encoded constructor arguments
	initialSupply := big.NewInt(0).Mul(big.NewInt(1000000), big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18), nil))

	uint256Ty, err := abi.NewType("uint256", "uint256", nil)
	handleErr(err)

	args := abi.Arguments{
		{
			Type: uint256Ty,
		},
	}

	encodedArgsBytes, err := args.Pack(initialSupply)
	handleErr(err)

	encodedArgsHex := fmt.Sprintf("%x", encodedArgsBytes)

	// set url encode data
	data := url.Values{
		"apiKey":                []string{os.Getenv("ETHERSCAN_API_KEY")},
		"module":                []string{"contract"},
		"action":                []string{"verifysourcecode"},
		"sourceCode":            []string{string(sourceCodeBytes)},
		"contractaddress":       []string{"0x7Fc3c9ae336291EC87296bb10D4B03f7d23357e4"},
		"codeformat":            []string{"solidity-standard-json-input"},
		"contractname":          []string{"contracts/MyToken.sol:MyToken"}, // contractfile.sol:contractname format
		"compilerversion":       []string{"v0.8.22+commit.4fc1097e"},
		"optimizationUsed":      []string{"1"},
		"constructorArguements": []string{encodedArgsHex}, // abi-encoded constructor arguments
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// create request
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, etherscanURL, bytes.NewBufferString(data.Encode()))
	handleErr(err)

	// set request headers
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	client := http.DefaultClient

	// send request
	resp, err := client.Do(req)
	handleErr(err)

	defer resp.Body.Close()

	// read response body
	body, err := io.ReadAll(resp.Body)
	handleErr(err)

	// success: 200 OK

	// print response body
	fmt.Println(string(body))
}

func handleErr(err error) {
	if err != nil {
		panic(err)
	}
}

standard json input 불러오기

// read input file
f, err := os.Open("verify/MyToken_input.json")
handleErr(err)

defer f.Close()

sourceCodeBytes, err := io.ReadAll(f)
handleErr(err)

스마트 컨트랙트 생성자 인수 abi 인코딩

// generate abi-encoded constructor arguments
initialSupply := big.NewInt(0).Mul(big.NewInt(1000000), big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18), nil))

uint256Ty, err := abi.NewType("uint256", "uint256", nil)
handleErr(err)

args := abi.Arguments{
	{
		Type: uint256Ty,
	},
}

encodedArgsBytes, err := args.Pack(initialSupply)
handleErr(err)

encodedArgsHex := fmt.Sprintf("%x", encodedArgsBytes)

 생성자 인수는 abi 인코딩이 필요하며, 16진수값으로 전달되어야 함

API 호출 인수 설정

// set url encode data
data := url.Values{
	"apiKey":                []string{os.Getenv("ETHERSCAN_API_KEY")},
	"module":                []string{"contract"},
	"action":                []string{"verifysourcecode"},
	"sourceCode":            []string{string(sourceCodeBytes)},
	"contractaddress":       []string{"0x7Fc3c9ae336291EC87296bb10D4B03f7d23357e4"},
	"codeformat":            []string{"solidity-standard-json-input"},
	"contractname":          []string{"contracts/MyToken.sol:MyToken"}, // contractfile.sol:contractname format
	"compilerversion":       []string{"v0.8.22+commit.4fc1097e"},
	"optimizationUsed":      []string{"1"},
	"constructorArguements": []string{encodedArgsHex}, // abi-encoded constructor arguments
}

요청 실행

// create request
req, err := http.NewRequestWithContext(ctx, http.MethodPost, etherscanURL, bytes.NewBufferString(data.Encode()))
handleErr(err)

// set request headers
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

client := http.DefaultClient

// send request
resp, err := client.Do(req)
handleErr(err)

defer resp.Body.Close()

// read response body
body, err := io.ReadAll(resp.Body)
handleErr(err)

// success: 200 OK

// print response body
fmt.Println(string(body))

5. 코드 실행

$ go run ./cmd/verify
{"status":"1","message":"OK","result":"vykmzujkyimxbzxn5cek1iyfmv8hj1hf2xdwxw4ephyk8maeyb"}

6. 전체 코드