Skip to content

Commit

Permalink
wip - golang tests
Browse files Browse the repository at this point in the history
  • Loading branch information
arianejasuwienas committed May 30, 2024
0 parents commit c0fc3f1
Show file tree
Hide file tree
Showing 8 changed files with 353 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
PRIVATE_KEY=YOUR_PRIVATE_KEY
INFURA_URL='https://testnet.hashio.io/api'
CHAIN_ID=296
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.env
hedera-golang-example-project
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
1. Install go: https://go.dev/doc/install
2. sudo apt install solc
3. https://docs.soliditylang.org/en/latest/installing-solidity.html
2. go get github.com/ethereum/go-ethereum/ethclient
3. go get github.com/ethereum/go-ethereum
go get github.com/ethereum/go-ethereum/accounts/abi/bind
go get github.com/ethereum/go-ethereum/crypto
4. go get github.com/joho/godotenv
5. export GOPATH=$HOME/go
export PATH=$PATH:$GOPATH/bin
5. go install github.com/ethereum/go-ethereum/cmd/abigen@latest
3. go build .
4. ./hedera-golang-example-project - run tests
1 change: 1 addition & 0 deletions cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*
208 changes: 208 additions & 0 deletions client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
package main

import (
"bytes"
"context"
"crypto/ecdsa"
"encoding/json"
"fmt"
"log"
"math/big"
"os"
"os/exec"
"strings"
"io/ioutil"
"path/filepath"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"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"
"github.com/joho/godotenv"
)

type CompilationResult struct {
Contracts map[string]struct {
ABI json.RawMessage
Bin string
Metadata string
} `json:"contracts"`
}

func compileContract(filePath string) (string, []byte, error) {
cmd := exec.Command("solc", "--combined-json", "abi,bin", filePath)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return "", nil, fmt.Errorf("failed to compile contract: %v", err)
}
var result CompilationResult
err = json.Unmarshal(out.Bytes(), &result)
if err != nil {
return "", nil, fmt.Errorf("failed to parse solc output: %v", err)
}
for _, contract := range result.Contracts {
abiBytes, err := json.Marshal(contract.ABI)
if err != nil {
return "", nil, fmt.Errorf("failed to marshal ABI: %v", err)
}
bin := contract.Bin
return bin, abiBytes, nil
}

return "", nil, fmt.Errorf("no contract found in solc output")
}

func loadFromFile(filename string) (string, error) {
data, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(data), nil
}

func saveToFile(filename string, data []byte) error {
return ioutil.WriteFile(filename, data, 0644)
}

func main() {
err := godotenv.Load()
if err != nil {
log.Fatalf("Error loading .env file")
}
infuraURL := os.Getenv("INFURA_URL")
privateKeyHex := os.Getenv("PRIVATE_KEY")
client, err := ethclient.Dial(infuraURL)
if err != nil {
log.Fatal(err)
}
fmt.Println("we have a connection")
privateKeyHex = strings.TrimPrefix(privateKeyHex, "0x")
privateKey, err := crypto.HexToECDSA(privateKeyHex)
if err != nil {
log.Fatalf("Failed to parse private key: %v", err)
}
publicKey := privateKey.Public()
publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
log.Fatal("Failed to cast public key to ECDSA")
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
fmt.Printf("Using address: %s\n", fromAddress.Hex())
balance, err := client.BalanceAt(context.Background(), fromAddress, nil)
if err != nil {
log.Fatalf("Failed to get balance: %v", err)
}
fmt.Printf("Account balance: %s\n", balance.String())
nonce, err := client.PendingNonceAt(context.Background(), fromAddress)
if err != nil {
log.Fatalf("Failed to get nonce: %v", err)
}
fmt.Printf("Using nonce: %d\n", nonce)
gasPrice, err := client.SuggestGasPrice(context.Background())
if err != nil {
log.Fatalf("Failed to get gas price: %v", err)
}
fmt.Printf("Using gas price: %s\n", gasPrice.String())
bin, abiBytes, err := compileContract("contracts/Greeter.sol")
if err != nil {
log.Fatalf("Failed to compile contract: %v", err)
}
cacheDir := "cache"
if _, err := os.Stat(cacheDir); os.IsNotExist(err) {
err = os.Mkdir(cacheDir, 0755)
if err != nil {
log.Fatalf("Failed to create cache directory: %v", err)
}
}
err = saveToFile(filepath.Join(cacheDir, "Greeter.bin"), []byte(bin))
if err != nil {
log.Fatalf("Failed to save bytecode: %v", err)
}
err = saveToFile(filepath.Join(cacheDir, "Greeter.abi"), abiBytes)
if err != nil {
log.Fatalf("Failed to save ABI: %v", err)
}
fmt.Println("Saved ABI and bytecode to cache files.")
parsedABI, err := abi.JSON(bytes.NewReader(abiBytes))
if err != nil {
log.Fatalf("Failed to parse ABI: %v", err)
}
bytecode := common.FromHex(bin)
auth := bind.NewKeyedTransactor(privateKey)
auth.Nonce = big.NewInt(int64(nonce))
auth.Value = big.NewInt(0)
auth.GasLimit = uint64(3000000)
auth.GasPrice = gasPrice
totalCost := new(big.Int).Mul(big.NewInt(int64(auth.GasLimit)), gasPrice)
fmt.Printf("Total transaction cost: %s\n", totalCost.String())

if balance.Cmp(totalCost) < 0 {
log.Fatalf("Insufficient funds: balance %s, needed %s", balance.String(), totalCost.String())
}
initialGreeting := "Hello, World!"
fmt.Printf("Transaction details:\n")
fmt.Printf(" From Address: %s\n", fromAddress.Hex())
fmt.Printf(" Nonce: %d\n", nonce)
fmt.Printf(" Gas Price: %s\n", gasPrice.String())
fmt.Printf(" Gas Limit: %d\n", auth.GasLimit)
fmt.Printf(" Initial Greeting: %s\n", initialGreeting)
addressFilePath := "cache/address"
var contractAddress common.Address
if _, err := os.Stat(addressFilePath); err == nil {
address, err := loadFromFile(addressFilePath)
if err != nil {
log.Fatalf("Failed to load contract address: %v", err)
}
contractAddress = common.HexToAddress(address)
fmt.Printf("Contract already deployed at address: %s\n", address)
} else {
address, tx, _, err := bind.DeployContract(auth, parsedABI, bytecode, client, initialGreeting)
if err != nil {
log.Fatalf("Failed to deploy contract: %v", err)
}
contractAddress = address

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

err = saveToFile(addressFilePath, []byte(address.Hex()))
if err != nil {
log.Fatalf("Failed to save contract address: %v", err)
}
fmt.Println("Saved contract address to file.")
}
interactWithContract(client, contractAddress, privateKey, parsedABI)
}

func interactWithContract(client *ethclient.Client, contractAddress common.Address, privateKey *ecdsa.PrivateKey, parsedABI abi.ABI) {
chainId := big.NewInt(296)
auth, err := bind.NewKeyedTransactorWithChainID(privateKey, chainId)
auth.GasLimit = uint64(3000000)
newGreeting := "Hello, Ethereum!"
fmt.Printf("Setting new greeting to: %s\n", newGreeting)

boundContract := bind.NewBoundContract(contractAddress, parsedABI, client, client, client)

setGreetingTx, err := boundContract.Transact(auth, "setGreeting", newGreeting)
if err != nil {
log.Fatalf("Failed to set greeting: %v", err)
}
fmt.Printf("Set greeting transaction hash: %s\n", setGreetingTx.Hash().Hex())
callMsg := ethereum.CallMsg{
To: &contractAddress,
Data: common.FromHex("0xcfae3217"), // greeting method selector.
}

res, err := client.CallContract(context.Background(), callMsg, nil)
if err != nil {
log.Fatalf("Failed to get greeting: %v", err)
}
length := new(big.Int).SetBytes(res[32:64]).Int64()
greeting := string(res[64 : 64+length])
greeting = strings.TrimRight(greeting, "\x00")

log.Printf("Greeting: %s", greeting)
}
27 changes: 27 additions & 0 deletions contracts/Greeter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

contract Greeter {
string private greeting;

event GreetingSet(string greeting);

//This constructor assigns the initial greeting and emit GreetingSet event
constructor(string memory _greeting) {
greeting = _greeting;

emit GreetingSet(_greeting);
}

//This function returns the current value stored in greeting variable
function greet() public view returns (string memory) {
return greeting;
}

//This function sets the new greeting msg from the one passed down as parameter and emit event
function setGreeting(string memory _greeting) public {
greeting = _greeting;

emit GreetingSet(_greeting);
}
}
34 changes: 34 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
module hedera-golang-example-project

go 1.22.3

require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/bits-and-blooms/bitset v1.10.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/consensys/gnark-crypto v0.12.1 // indirect
github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.14.3 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/uint256 v1.2.4 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
github.com/supranational/blst v0.3.11 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/tools v0.20.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)
65 changes: 65 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ=
github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI=
github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M=
github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY=
github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI=
github.com/crate-crypto/go-kzg-4844 v1.0.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/go-ethereum v1.14.3 h1:5zvnAqLtnCZrU9uod1JCvHWJbPMURzYFHfc2eHz4PHA=
github.com/ethereum/go-ethereum v1.14.3/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU=
github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU=
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4=
github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=

0 comments on commit c0fc3f1

Please sign in to comment.