스마트 컨트랙트를 컴파일하게 되면 ABI code와 Byte Code가 생성됩니다.
스마트 컨트랙트 코드에 대한 설명이 담긴 JSON 형식의 인터페이스
스마트 컨트랙트 코드에 있는 함수에 대해 정의하고, 컨트랙트에 있는 함수에 어떤 인자를 넣어야하는지, 어떤 데이터가 반환되는지 등을 가지고 있으며, 노드가 컨트랙트를 실행하기 위해 어떤 작업을 수행해야하는지 알려준다.
ABI를 생성하는 방법은 2가지
RemixIDE ABI 복사
프로젝트 폴더 경로에서
go mod init
contracts/XXXX.sol 파일 생성 후 solidity 작성
solc 설치 및 확인
brew update
brew tap ethereum/ethereum
brew install solidity
solc --version
Solidity 파일에서 ABI 추출
solc --abi --bin ./contracts/CozToken.sol -o build
# 자신의 파일 이름에 맞게 수정하세요
확인할 수 있는 정보는 다음과 같다.
그 이상의 기능도 지원한다. 스마트컨트랙트와 연결된 기능들
ABI 파일은 명령어로 Go 파일로 변환할 수 있다. 이렇게 만든 ABI Go 파일을 이용하여, Go 프로젝트 백엔드에서 스마트 컨트랙트와 통신할 수 있다.
abigen 설치
$ cd $GOPATH
$ cd ./pkg/mod/github.com/ethereum/go-ethereum@v1.10.26
# 자신이 install한 go-ethereum 버전에 맞게 경로 이동
$ sudo make
$ sudo make devtools
$ abigen --version
abigen version 1.10.25-stable
ABI - Go 파일로 변환
$ abigen --bin=build/CozToken.bin --abi=build/CozToken.abi --pkg=contracts --out=./contracts/CozToken.go
# 자신의 파일 이름에 맞게 수정하세요
Go 파일로 컨트랙트 배포
백엔드(서버)에서 트랜잭션을 발생시키는 경우 서명은 직접 Private Key를 이용해서 서명한다.
go-ethereum 패키지를 이용하면 블록체인 네트워크와 쉽게 연동이 가능합니다.
import (
"github.com/ethereum/go-ethereum/ethclient"
)
client, err := ethclient.Dial(cf.Network.URL)
if err != nil {
log.Fatal(err)
}
HTTP(HTTPS) server endpoint
WS(WSS) server endpoint
- wss://ws.test.wemix.com
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"fmt"
"math/big"
"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"
"github.com/ethereum/go-ethereum/rlp"
)
func TransferWemix() {
// 블록체인 네트워크와 연결할 클라이언트를 생성하기 위한 rpc url 연결
client, err := ethclient.Dial("https://api.test.wemix.com")
if err != nil {
fmt.Println("client error")
}
// metamask에서 뽑아낸 privatekey를 변환
privateKey, err := crypto.HexToECDSA("상기 metamask에서 추출한 자신의 privatekey")
if err != nil {
fmt.Println(err)
}
// privatekey로부터 publickey를 거쳐 자신의 address 변환
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Println("fail convert, publickey")
}
// 보낼 address 설정
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := big.NewInt(700000000000000000)
gasLimit := uint64(21000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 전송받을 상대방 address 설정
toAddress := common.HexToAddress("페어의 주소 또는 타 공개 address")
// 트랜잭션 생성
var data []byte
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
chainID, err := client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// RLP 인코딩 전 트랜잭션 묶음. 현재는 1개의 트랜잭션
ts := types.Transactions{signedTx}
// RLP 인코딩
rawTxBytes, _ := rlp.EncodeToBytes(ts[0])
rawTxHex := hex.EncodeToString(rawTxBytes)
rTxBytes, err := hex.DecodeString(rawTxHex)
if err != nil {
fmt.Println(err.Error())
}
// RLP 디코딩
rlp.DecodeBytes(rTxBytes, &tx)
// 트랜잭션 전송
err = client.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Println(err)
}
//출력된 tx.hash를 익스플로러에 조회 가능
//예) 0x4788935cfa4a0f23807ba7d7b17a6304cc52795616889fdb9ebdb4498adf4a35
fmt.Printf("tx sent: %s\n", tx.Hash().Hex())
}
import (
"context"
"crypto/ecdsa"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"log"
"math/big"
"net/http"
"net/url"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"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"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
"wbe/oos/contracts" // 자신의 경로에 맞게 수정
)
func TransferCtxCoz() {
// 블록체인 네트워크와 연결할 클라이언트를 생성하기 위한 rpc url 연결
client, err := ethclient.Dial("https://api.test.wemix.com")
if err != nil {
fmt.Println("client error")
}
// 본인이 배포한 토큰 컨트랙트 어드레스
tokenAddress := common.HexToAddress("0xe3236FEe84ffbcFA7955241CF0Bd0836169e075f")
instance, err := contracts.NewContracts(tokenAddress, client)
if err != nil {
fmt.Println(err)
}
// 오너 어드레스
address := common.HexToAddress("0x7C910BDA16C4774082DaAF7Ed88d94Ca7c45FcaF")
bal, err := instance.BalanceOf(&bind.CallOpts{}, address)
if err != nil {
fmt.Println(err)
}
// name 출력
name, err := instance.Name(&bind.CallOpts{})
if err != nil {
fmt.Println(err)
}
// symbol 출력
symbol, err := instance.Symbol(&bind.CallOpts{})
if err != nil {
fmt.Println(err)
}
// 사용되는 decimals 출력
decimals, err := instance.Decimals(&bind.CallOpts{})
if err != nil {
fmt.Println(err)
}
fmt.Printf("balance: %s\n", bal) // "balance: 999999999300000000000000000"
fmt.Printf("name: %s\n", name) // "name: Coz Token"
fmt.Printf("symbol: %s\n", symbol) // "symbol: Coz"
fmt.Printf("decimals: %v\n", decimals) // "decimals: 18"
privateKey, err := crypto.HexToECDSA("상기 metamask에서 추출한 privatekey")
if err != nil {
fmt.Println(err)
}
// privatekey로부터 publickey를 거쳐 자신의 address 변환
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Println("fail convert, publickey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := big.NewInt(700000000000000000)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 보낼 주소
toAddress := common.HexToAddress("0x5D86dE4B82091dBF1fd2c706d36ebC98E3d4d5Cd")
// 컨트랙트 전송시 사용할 함수명
transferFnSignature := []byte("transfer(address,uint256)")
hash := sha3.NewLegacyKeccak256()
hash.Write(transferFnSignature)
methodID := hash.Sum(nil)[:4]
fmt.Println(hexutil.Encode(methodID))
paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAddress)) // 0x0000000000000000000000004592d8f8d7b001e72cb26a73e4fa1806a51ac79d
paddedAmount := common.LeftPadBytes(value.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAmount)) // 0x00000000000000000000000000000000000000000000003635c9adc5dea00000
zvalue := big.NewInt(0)
//컨트랙트 전송 정보 입력
var pdata []byte
pdata = append(pdata, methodID...)
pdata = append(pdata, paddedAddress...)
pdata = append(pdata, paddedAmount...)
gasLimit := uint64(200000)
fmt.Println(gasLimit)
// 트랜잭션 생성
tx := types.NewTransaction(nonce, tokenAddress, zvalue, gasLimit, gasPrice, pdata)
chainID, err := client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// 트랜잭션 전송
err = client.SendTransaction(context.Background(), signedTx)
if err != nil {
fmt.Println(err)
}
//tx.hash를 이용해 전송결과를 확인
//예)0x016430c748dad98865afb61038537f3ab8f504b56910769d328e7d857be7886a
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
}
위의 코드에서는 스마트 컨트랙트의 어떤 함수를 실행할 지 transferFnSignature
변수에 지정하여 트랜잭션을 생성했습니다. 여기서는 transfer 함수
로 지정했기 때문에 토큰 전송 기능이 작동한 것입니다. 이를 응용하여 transfer 함수 외에 다양한 스마트 컨트랙트 함수를 사용할 수 있습니다.
여러분은 이제 Go에서 트랜잭션을 블록체인 네트워크에 직접 생성할 수도 있고, 스마트 컨트랙트를 실행하여 생성할 수도 있게 되었습니다. 하지만 이는 말 그대로 Go를 이용하여 트랜잭션을 생성한 것일 뿐입니다.
즉, 서버라고 부르기는 어렵습니다.
여러분들이 앞의 백엔드 과정에서 공부한 Gin Framework 백엔드(서버)에, 방금 학습한 트랜잭션 생성을 적용해보세요! 이제 여러분은 블록체인 네트워크와 연동된 백엔드 서버를 개발할 수 있습니다!
coin := e.Group("/coin", liteAuth())
{
coin.POST("/transfer", r.ct.TransferCoin)
coin.POST("/transferfrom", r.ct.TransferFromCoin)
}
token := e.Group("/token", liteAuth())
{
token.GET("/:tokenname", r.ct.GetToken)
token.GET("/balance/:address", r.ct.GetBalance)
token.POST("/transfer", r.ct.TransferToken)
token.POST("/transferfrom", r.ct.TransferFromToken)
}
GET 이용
- 토큰 이름으로 토큰 심볼 조회
parameter - token name
return - symbol name
func (p *Controller) GetToken(c *gin.Context) {
recvTokenName := c.Param("tokenname")
fmt.Println(recvTokenName)
// 토큰 이름으로 해당 토큰 컨트랙트를 찾는다?
// 찾은 컨트랙트에 심볼을 가져온다?
// 본인이 배포한 토큰 컨트랙트 어드레스
tokenAddress := common.HexToAddress("0xb105B7b288429209e9Fdf6e5D26E7aC4bcba6552")
instance, err := contracts.NewContracts(tokenAddress, p.client)
if err != nil {
fmt.Println(err)
}
// symbol 출력
symbol, err := instance.Symbol(&bind.CallOpts{})
if err != nil {
fmt.Println(err)
}
fmt.Printf("symbol: %s\n", symbol) // "symbol: Coz"
c.JSON(http.StatusOK, gin.H{
"Error": "Request Error",
"path": c.FullPath(),
"data": symbol,
})
}
GET 이용
- 특정 주소가 소유한 토큰의 양 조회
parameter - address
return - token balance
func (p *Controller) GetBalance(c *gin.Context) {
recvAddress := c.Param("address")
fmt.Println(recvAddress)
//토큰 컨트랙트 어드레스
tokenAddress := common.HexToAddress(recvAddress)
instance, err := contracts.NewContracts(tokenAddress, p.client)
if err != nil {
fmt.Println(err)
}
// 오너 어드레스
address := common.HexToAddress("0xDc45fE9fF7aF3522bB2B88a602670Ab4bE2C6f91")
bal, err := instance.BalanceOf(&bind.CallOpts{}, address)
if err != nil {
fmt.Println(err)
}
fmt.Printf("balance: %s\n", bal) // "balance: 999999999300000000000000000"
}
Post 이용
- 특정 주소에 지정한 양의 위믹스 코인을 전송
parameter - address, value
return - 위믹스 코인 전송 결과
func (p *Controller) TransferFromCoin(c *gin.Context) {
recvTransferFrom := model.TransferFrom{}
var err error
if err = c.ShouldBindJSON(&recvTransferFrom); err != nil {
p.RespError(c, nil, http.StatusBadRequest, "fail, This is incorrect JSON", err)
return
}
privateKey, err := crypto.HexToECDSA(recvTransferFrom.PrivateKey)
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Errorf("fail convert, publickey : %v", ok)
}
// 보낼 address 설정
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := p.client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := recvTransferFrom.Value
gasLimit := uint64(21000)
gasPrice, err := p.client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 전송받을 상대방 address 설정
toAddress := common.HexToAddress(recvTransferFrom.Address)
// 트랜잭션 생성
var data []byte
tx := types.NewTransaction(nonce, toAddress, &value, gasLimit, gasPrice, data)
chainID, err := p.client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// RLP 인코딩 전 트랜잭션 묶음. 현재는 1개의 트랜잭션
ts := types.Transactions{signedTx}
// RLP 인코딩
rawTxBytes, _ := rlp.EncodeToBytes(ts[0])
rawTxHex := hex.EncodeToString(rawTxBytes)
rTxBytes, err := hex.DecodeString(rawTxHex)
if err != nil {
fmt.Println(err.Error())
}
// RLP 디코딩
rlp.DecodeBytes(rTxBytes, &tx)
// 트랜잭션 전송
err = p.client.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Println(err)
}
c.JSON(http.StatusOK, gin.H{
"status": "OK",
"path": c.FullPath(),
"tx": signedTx.Hash().Hex(),
})
}
Post 이용
- 다른 개인 키로, 특정 주소에 지정한 양의 위믹스 코인을 전송
parameter - private key, address, value
return - 위믹스 코인 전송 결과
func (p *Controller) TransferFromCoin(c *gin.Context) {
recvTransferFrom := model.TransferFrom{}
var err error
if err = c.ShouldBindJSON(&recvTransferFrom); err != nil {
p.RespError(c, nil, http.StatusBadRequest, "fail, This is incorrect JSON", err)
return
}
privateKey, err := crypto.HexToECDSA(recvTransferFrom.PrivateKey)
if err != nil {
log.Fatal(err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Errorf("fail convert, publickey : %v", ok)
}
// 보낼 address 설정
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := p.client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := recvTransferFrom.Value
gasLimit := uint64(21000)
gasPrice, err := p.client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 전송받을 상대방 address 설정
toAddress := common.HexToAddress(recvTransferFrom.Address)
// 트랜잭션 생성
var data []byte
tx := types.NewTransaction(nonce, toAddress, &value, gasLimit, gasPrice, data)
chainID, err := p.client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// RLP 인코딩 전 트랜잭션 묶음. 현재는 1개의 트랜잭션
ts := types.Transactions{signedTx}
// RLP 인코딩
rawTxBytes, _ := rlp.EncodeToBytes(ts[0])
rawTxHex := hex.EncodeToString(rawTxBytes)
rTxBytes, err := hex.DecodeString(rawTxHex)
if err != nil {
fmt.Println(err.Error())
}
// RLP 디코딩
rlp.DecodeBytes(rTxBytes, &tx)
// 트랜잭션 전송
err = p.client.SendTransaction(context.Background(), tx)
if err != nil {
fmt.Println(err)
}
c.JSON(http.StatusOK, gin.H{
"status": "OK",
"path": c.FullPath(),
"tx": signedTx.Hash().Hex(),
})
}
Post 이용
- 특정 주소에 지정한 양의 토큰을 전송
parameter - address, value
return - 발행한 토큰 전송 결과
func (p *Controller) TransferToken(c *gin.Context) {
recvTransfer := model.Transfer{}
if err := c.ShouldBindJSON(&recvTransfer); err != nil {
p.RespError(c, nil, http.StatusBadRequest, "fail, This is incorrect JSON", err)
return
}
tokenAddress := common.HexToAddress("0xb105B7b288429209e9Fdf6e5D26E7aC4bcba6552")
privateKey, err := crypto.HexToECDSA("개인키")
if err != nil {
fmt.Println(err)
}
// privatekey로부터 publickey를 거쳐 자신의 address 변환
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Println("fail convert, publickey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := p.client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := recvTransfer.Value
gasPrice, err := p.client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 보낼 주소
toAddress := common.HexToAddress(recvTransfer.Address)
// 컨트랙트 전송시 사용할 함수명
transferFnSignature := []byte("transfer(address,uint256)")
hash := sha3.NewLegacyKeccak256()
hash.Write(transferFnSignature)
methodID := hash.Sum(nil)[:4]
fmt.Println(hexutil.Encode(methodID))
paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAddress)) // 0x0000000000000000000000004592d8f8d7b001e72cb26a73e4fa1806a51ac79d
paddedAmount := common.LeftPadBytes(value.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAmount)) // 0x00000000000000000000000000000000000000000000003635c9adc5dea00000
zvalue := big.NewInt(0)
//컨트랙트 전송 정보 입력
var pdata []byte
pdata = append(pdata, methodID...)
pdata = append(pdata, paddedAddress...)
pdata = append(pdata, paddedAmount...)
gasLimit := uint64(200000)
fmt.Println(gasLimit)
// 트랜잭션 생성
tx := types.NewTransaction(nonce, tokenAddress, zvalue, gasLimit, gasPrice, pdata)
chainID, err := p.client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// 트랜잭션 전송
err = p.client.SendTransaction(context.Background(), signedTx)
if err != nil {
fmt.Println(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
c.JSON(http.StatusOK, gin.H{
"status": "OK",
"path": c.FullPath(),
"tx": signedTx.Hash().Hex(),
})
}
Post 이용
- 다른 개인 키로, 특정 주소에 지정한 양의 토큰을 전송
parameter - **private key, address, value
return -** 발행한 토큰 전송 결과
func (p *Controller) TransferFromToken(c *gin.Context) {
recvTransferFrom := model.TransferFrom{}
var err error
if err = c.ShouldBindJSON(&recvTransferFrom); err != nil {
p.RespError(c, nil, http.StatusBadRequest, "fail, This is incorrect JSON", err)
return
}
tokenAddress := common.HexToAddress("0xb105B7b288429209e9Fdf6e5D26E7aC4bcba6552")
privateKey, err := crypto.HexToECDSA(recvTransferFrom.PrivateKey)
if err != nil {
fmt.Println(err)
}
// privatekey로부터 publickey를 거쳐 자신의 address 변환
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
fmt.Println("fail convert, publickey")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
// 현재 계정의 nonce를 가져옴. 다음 트랜잭션에서 사용할 nonce
nonce, err := p.client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
fmt.Println(err)
}
// 전송할 양, gasLimit, gasPrice 설정. 추천되는 gasPrice를 가져옴
value := recvTransferFrom.Value
gasPrice, err := p.client.SuggestGasPrice(context.Background())
if err != nil {
fmt.Println(err)
}
// 보낼 주소
toAddress := common.HexToAddress(recvTransferFrom.Address)
// 컨트랙트 전송시 사용할 함수명
transferFnSignature := []byte("transfer(address,uint256)")
hash := sha3.NewLegacyKeccak256()
hash.Write(transferFnSignature)
methodID := hash.Sum(nil)[:4]
fmt.Println(hexutil.Encode(methodID))
paddedAddress := common.LeftPadBytes(toAddress.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAddress)) // 0x0000000000000000000000004592d8f8d7b001e72cb26a73e4fa1806a51ac79d
paddedAmount := common.LeftPadBytes(value.Bytes(), 32)
fmt.Println(hexutil.Encode(paddedAmount)) // 0x00000000000000000000000000000000000000000000003635c9adc5dea00000
zvalue := big.NewInt(0)
//컨트랙트 전송 정보 입력
var pdata []byte
pdata = append(pdata, methodID...)
pdata = append(pdata, paddedAddress...)
pdata = append(pdata, paddedAmount...)
gasLimit := uint64(200000)
fmt.Println(gasLimit)
// 트랜잭션 생성
tx := types.NewTransaction(nonce, tokenAddress, zvalue, gasLimit, gasPrice, pdata)
chainID, err := p.client.NetworkID(context.Background())
if err != nil {
fmt.Println(err)
}
// 트랜잭션 서명
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
fmt.Println(err)
}
// 트랜잭션 전송
err = p.client.SendTransaction(context.Background(), signedTx)
if err != nil {
fmt.Println(err)
}
fmt.Printf("tx sent: %s", signedTx.Hash().Hex())
c.JSON(http.StatusOK, gin.H{
"status": "OK",
"path": c.FullPath(),
"tx": signedTx.Hash().Hex(),
})
}