Hi folks! In this tutorial, we are going to learn how to create a simple REST API to interact with the blockchain using . Ethereum Golang Web3.js is the de-facto library to interact for interacting with Ethereum in JavaScript and Node.js. It takes care of encoding payloads and generating the RPC calls. Web3.js is very popular and heavily documented. On the other hand, ( ), the most popular Ethereum implementation, is written in Go. It’s a complete Ethereum node. If you build a dApp in Go, then you’ll be using the go-ethereum libraries directly which means you can do everything the node can do. geth So, for this tutorial, I chose to use Go as our weapon. In simple terms, interacting with the blockchain means that you will be making RPC calls over HTTP. Any language will be able to do that for you but, using a compiled language such as Go will give you a better performance… If performance is important to you or your team. Enough of boring introduction! For our tutorial, we are going to set up four endpoints: Get the latest blockGet transaction by hashGet address balanceTransfer ether to an address This is not a big deal, but I think is cool as a starting point to more complex implementations. If you want to get the whole code you can download it . here SET UP I used go 1.13.8 for this tutorial, to get your Go version just run: $ go version go version go1.13.8 darwin/amd64 # outputs First we are going to create our project by running the following command: $ go mod init That will create at the root of your working directory the file with the following content: go.mod module github.com/LuisAcerv/goeth-api go 1.13 Now let's create our project structure. I have to say here that you can use the structure that better fits your needs. At the root of your project create a new file. main.go $ >> main.go echo "package main" Now we need to create three directories: $ mkdir handler $ mkdir models $ mkdir modules And inside each of those folders we are going to create a file, and we should have the following structure: main.go . ├── handler │ └── main ├── models │ └── main ├── modules │ └── main ├── go ├── go ├── main.go .go .go .go .mod .sum Ganache-CLI In order to interact with the Ethereum blockchain, we need a provider, a provider is a node we have access to make RPC calls over HTTP. You can use a testnet such as ropsten or kovan through a provider such as , but for this tutorial, we are going to set up a local virtual node using . Infura ganache At the official you can download ganache, is pretty easy to set up and will give you all you need for testing proposes. It comes with a nice UI which will show you the transactions, accounts and logs of your "node". truffle website Once you have ganache installed and running we are good to start writing some code. We have a file. This file contains the structures we are going to use in our API. ./models/main.go We add the following content: models Block { BlockNumber Timestamp Difficulty Hash TransactionsCount Transactions []Transaction } Transaction { Hash Value Gas GasPrice Nonce To Pending } TransferEthRequest { PrivKey To Amount } HashResponse { Hash } BalanceResponse { Address Balance Symbol Units } Error { Code Message } package // Block data structure type struct int64 `json:"blockNumber"` uint64 `json:"timestamp"` uint64 `json:"difficulty"` string `json:"hash"` int `json:"transactionsCount"` `json:"transactions"` // Transaction data structure type struct string `json:"hash"` string `json:"value"` uint64 `json:"gas"` uint64 `json:"gasPrice"` uint64 `json:"nonce"` string `json:"to"` bool `json:"pending"` // TransferEthRequest data structure type struct string `json:"privKey"` string `json:"to"` int64 `json:"amount"` // HashResponse data structure type struct string `json:"hash"` // BalanceResponse data structure type struct string `json:"address"` string `json:"balance"` string `json:"symbol"` string `json:"units"` // Error data structure type struct uint64 `json:"code"` string `json:"message"` Now that we have our models defined and ready to be used, we are going to create the methods in charge of interacting with the blockchain. First of all we want to install the module, and we can do that by running the following command: go-ethereum $ go get -u github.com/ethereum/go-ethereum In our we are going to create a function that retrieves the latest block from the blockchain. ./modules/main.go This function is going to give us the information about the block and the transactions embedded in it. { { err := (); err != { fmt.Println(err) } }() header, _ := client.HeaderByNumber(context.Background(), ) blockNumber := big.NewInt(header.Number.Int64()) block, err := client.BlockByNumber(context.Background(), blockNumber) err != { log.Fatal(err) } _block := &Models.Block{ BlockNumber: block.Number().Int64(), Timestamp: block.Time(), Difficulty: block.Difficulty().Uint64(), Hash: block.Hash().String(), TransactionsCount: (block.Transactions()), Transactions: []Models.Transaction{}, } _, tx := block.Transactions() { _block.Transactions = (_block.Transactions, Models.Transaction{ Hash: tx.Hash().String(), Value: tx.Value().String(), Gas: tx.Gas(), GasPrice: tx.GasPrice().Uint64(), Nonce: tx.Nonce(), To: tx.To().String(), }) } _block } * . func GetLatestBlock (client ethclient.Client) Models Block // We add a recover function from panics to prevent our API from crashing due to an unexpected error defer func () if recover nil // Query the latest block nil if nil // Build the response to our model len for range append return Now, we want to have a function to retrieve information about a given transaction, for example, if we transfer ether to from one account to another, the API will respond with de transaction hash, and we can use it to get the transaction information. To do that we add a new function to our ./modules/main.go { { err := (); err != { fmt.Println(err) } }() tx, pending, err := client.TransactionByHash(context.Background(), hash) err != { fmt.Println(err) } &Models.Transaction{ Hash: tx.Hash().String(), Value: tx.Value().String(), Gas: tx.Gas(), GasPrice: tx.GasPrice().Uint64(), To: tx.To().String(), Pending: pending, Nonce: tx.Nonce(), } } // GetTxByHash by a given hash * . func GetTxByHash (client ethclient.Client, hash common.Hash) Models Transaction defer func () if recover nil if nil return Another thing we want to know is our balance, for that we need to add another function. { account := common.HexToAddress(address) balance, err := client.BalanceAt(context.Background(), account, ) err != { , err } balance.String(), } // GetAddressBalance returns the given address balance =P func GetAddressBalance (client ethclient.Client, address ) string ( , error) string nil if nil return "0" return nil The last function we are going to add into our modules package is the one that will allow us to send ether from one account to another. And I want to make a parenthesis here. This function requires that the client sends the sender private key to sign the transaction, that means that your client will broadcast the user's private key through HTTP and if you are going to do something like this using with this code or another else, then at least make sure you are using HTTPS. Said that let's add the function to our modules package. { { err := (); err != { fmt.Println(err) } }() privateKey, err := crypto.HexToECDSA(privKey) err != { , err } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) !ok { , err } fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) nonce, err := client.PendingNonceAt(context.Background(), fromAddress) err != { , err } value := big.NewInt(amount) gasLimit := ( ) gasPrice, err := client.SuggestGasPrice(context.Background()) err != { , err } toAddress := common.HexToAddress(to) data [] tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data) chainID, err := client.NetworkID(context.Background()) err != { , err } signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) err != { , err } err = client.SendTransaction(context.Background(), signedTx) err != { , err } signedTx.Hash().String(), } func TransferEth (client ethclient.Client, privKey , to , amount ) string string int64 ( , error) string defer func () if recover nil // Assuming you've already connected a client, the next step is to load your private key. if nil return "" // Function requires the public address of the account we're sending from -- which we can derive from the private key. if return "" // Now we can read the nonce that we should use for the account's transaction. if nil return "" // in wei (1 eth) uint64 21000 // in units if nil return "" // We figure out who we're sending the ETH to. var byte // We create the transaction payload if nil return "" // We sign the transaction using the sender's private key if nil return "" // Now we are finally ready to broadcast the transaction to the entire network if nil return "" // We return the transaction hash return nil Nice! we have a function to transfer ether, now all together: modules ( Models ) { { err := (); err != { fmt.Println(err) } }() header, _ := client.HeaderByNumber(context.Background(), ) blockNumber := big.NewInt(header.Number.Int64()) block, err := client.BlockByNumber(context.Background(), blockNumber) err != { log.Fatal(err) } _block := &Models.Block{ BlockNumber: block.Number().Int64(), Timestamp: block.Time(), Difficulty: block.Difficulty().Uint64(), Hash: block.Hash().String(), TransactionsCount: (block.Transactions()), Transactions: []Models.Transaction{}, } _, tx := block.Transactions() { _block.Transactions = (_block.Transactions, Models.Transaction{ Hash: tx.Hash().String(), Value: tx.Value().String(), Gas: tx.Gas(), GasPrice: tx.GasPrice().Uint64(), Nonce: tx.Nonce(), To: tx.To().String(), }) } _block } { { err := (); err != { fmt.Println(err) } }() tx, pending, err := client.TransactionByHash(context.Background(), hash) err != { fmt.Println(err) } &Models.Transaction{ Hash: tx.Hash().String(), Value: tx.Value().String(), Gas: tx.Gas(), GasPrice: tx.GasPrice().Uint64(), To: tx.To().String(), Pending: pending, Nonce: tx.Nonce(), } } { { err := (); err != { fmt.Println(err) } }() privateKey, err := crypto.HexToECDSA(privKey) err != { , err } publicKey := privateKey.Public() publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) !ok { , err } fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA) nonce, err := client.PendingNonceAt(context.Background(), fromAddress) err != { , err } value := big.NewInt(amount) gasLimit := ( ) gasPrice, err := client.SuggestGasPrice(context.Background()) err != { , err } toAddress := common.HexToAddress(to) data [] tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data) chainID, err := client.NetworkID(context.Background()) err != { , err } signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey) err != { , err } err = client.SendTransaction(context.Background(), signedTx) err != { , err } signedTx.Hash().String(), } { account := common.HexToAddress(address) balance, err := client.BalanceAt(context.Background(), account, ) err != { , err } balance.String(), } package import "context" "crypto/ecdsa" "fmt" "log" "math/big" "github.com/LuisAcerv/goeth-api/models" "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" // GetLatestBlock from blockchain * . func GetLatestBlock (client ethclient.Client) Models Block // We add a recover function from panics to prevent our API from crashing due to an unexpected error defer func () if recover nil // Query the latest block nil if nil // Build the response to our model len for range append return // GetTxByHash by a given hash * . func GetTxByHash (client ethclient.Client, hash common.Hash) Models Transaction defer func () if recover nil if nil return // TransferEth from one account to another func TransferEth (client ethclient.Client, privKey , to , amount ) string string int64 ( , error) string defer func () if recover nil // Assuming you've already connected a client, the next step is to load your private key. if nil return "" // Function requires the public address of the account we're sending from -- which we can derive from the private key. if return "" // Now we can read the nonce that we should use for the account's transaction. if nil return "" // in wei (1 eth) uint64 21000 // in units if nil return "" // We figure out who we're sending the ETH to. var byte // We create the transaction payload if nil return "" // We sign the transaction using the sender's private key if nil return "" // Now we are finally ready to broadcast the transaction to the entire network if nil return "" // We return the transaction hash return nil // GetAddressBalance returns the given address balance =P func GetAddressBalance (client ethclient.Client, address ) string ( , error) string nil if nil return "0" return nil Now we need to set up our API to interact with the functions we wrote through HTTP endpoints. To do this we are going to use . gorilla/mux In our main file we are going the add the following content: main ( Handlers ) { client, err := ethclient.Dial( ) err != { fmt.Println(err) } r := mux.NewRouter() r.Handle( , Handlers.ClientHandler{client}) log.Fatal(http.ListenAndServe( , r)) } package import "fmt" "log" "net/http" "github.com/LuisAcerv/goeth-api/handler" "github.com/ethereum/go-ethereum/ethclient" "github.com/gorilla/mux" func main () // Create a client instance to connect to our providr "http://localhost:7545" if nil // Create a mux router // We will define a single endpoint "/api/v1/eth/{module}" ":8080" Now we need to create handler for our endpoint, since we have added a parameter to our endpoint we will be able to handle our functions with a single handler. As I said before feel free to use the architecture you wish for your own project. module Now in our we are going to add the following content: ./handler/main.go handlers ( Models Modules ) ClientHandler { *ethclient.Client } { vars := mux.Vars(r) module := vars[ ] address := r.URL.Query().Get( ) hash := r.URL.Query().Get( ) w.Header().Set( , ) module { : _block := Modules.GetLatestBlock(*client.Client) json.NewEncoder(w).Encode(_block) : hash == { json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) } txHash := common.HexToHash(hash) _tx := Modules.GetTxByHash(*client.Client, txHash) _tx != { json.NewEncoder(w).Encode(_tx) } json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) : decoder := json.NewDecoder(r.Body) t Models.TransferEthRequest err := decoder.Decode(&t) err != { fmt.Println(err) json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) } _hash, err := Modules.TransferEth(*client.Client, t.PrivKey, t.To, t.Amount) err != { fmt.Println(err) json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) } json.NewEncoder(w).Encode(&Models.HashResponse{ Hash: _hash, }) : address == { json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) } balance, err := Modules.GetAddressBalance(*client.Client, address) err != { fmt.Println(err) json.NewEncoder(w).Encode(&Models.Error{ Code: , Message: , }) } json.NewEncoder(w).Encode(&Models.BalanceResponse{ Address: address, Balance: balance, Symbol: , Units: , }) } } package import "encoding/json" "fmt" "net/http" "github.com/LuisAcerv/goeth-api/models" "github.com/LuisAcerv/goeth-api/modules" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/ethclient" "github.com/gorilla/mux" // ClientHandler ethereum client instance type struct func (client ClientHandler) ServeHTTP (w http.ResponseWriter, r *http.Request) // Get parameter from url request "module" // Get the query parameters from url request "address" "hash" // Set our response header "Content-Type" "application/json" // Handle each request using the module parameter: switch case "latest-block" case "get-tx" if "" 400 "Malformed request" return if nil return 404 "Tx Not Found!" case "send-eth" var if nil 400 "Malformed request" return if nil 500 "Internal server error" return case "get-balance" if "" 400 "Malformed request" return if nil 500 "Internal server error" return "Ether" "Wei" That's it, if we go to our terminal and run: $ go run main.go We can start testing our API. Make sure you are running your ganache instance and in your browser go to: and if everything is ok then you should see something like this: http://localhost:8080/api/v1/eth/latest-block Now let's try to transfer some ether from one account to another, first copy the private key of the sender from ganache: And also copy an address to send the ether: Now using , let's make a transfer!: curl $ curl -d -H -X POST http://localhost:8080/api/v1/eth/send-eth '{"privKey":"12a770fe34a793800abaab0a48b7a394ae440b9117d000178af81b61cda8ff15", "to":"0xa8Ce5Fb2DAB781B8f743a8096601eB01Ff0a246d", "amount":1000000000000000000}' "Content-Type: application/json" You should receive the transaction hash as response: { : } "hash" "0xa5417ae03a817e41ddf36303f3ea985e6bd64a504c662d988bcb47913be8d472" Now let's get the transaction information using our API, go to: http://localhost:8080/api/v1/eth/get-tx?hash=<tx-hash-from-response> And you should see something like this: And finally, let's check the balance of the address by going to http://localhost:8080/api/v1/eth/get-balance?address=<the-recipient-address> That's it, we have created a simple API to start interacting with the Ethereum blockchain and perform some basic actions. In the following part we are going to improve our existing code and we are going to add functionality to interact with smart contracts and ERC20 tokens. Repository: https://github.com/LuisAcerv/goeth-api Check out this on how create bitcoin HD Wallet using Go. tutorial And that's it, if you want to talk then follow me on . twitter See you soon with the next part or in another coding adventure. Happy hacking!