diff --git a/Makefile b/Makefile
index 246e4b3467a83adb317f11ffa6312f109b3788a7..c150585948dbfc38d534d0666f1d19f7d0886341 100644
--- a/Makefile
+++ b/Makefile
@@ -110,6 +110,11 @@ tracker:
 	@echo "Done building."
 	@echo "Run \"$(GOBIN)/tracker\" to run snapshots tracker."
 
+devnettest:
+	$(GOBUILD) -o $(GOBIN)/devnettest ./cmd/devnettest
+	@echo "Done building."
+	@echo "Run \"$(GOBIN)/devnettest\" to launch devnettest."
+
 db-tools:
 	@echo "Building db-tools"
 	rm -rf libmdbx # hub.docker.com setup incorrect gitpath for git modules. Just remove it and re-init submodule.
diff --git a/cmd/devnettest/main.go b/cmd/devnettest/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..6cad3a97b535039b7753bd6e3daa37a5b0442122
--- /dev/null
+++ b/cmd/devnettest/main.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+	"flag"
+	"fmt"
+	"github.com/ledgerwatch/erigon/cmd/devnettest/services"
+)
+
+func main() {
+	var (
+		to            string
+		value         uint64
+		blockNum      string
+		getBalance    bool
+		sendTx        bool
+		txPoolContent bool
+		clearDev      bool
+	)
+
+	flag.StringVar(&to, "to", "", "String Address to send to")
+	flag.Uint64Var(&value, "value", uint64(0), "Uint64 Value to send")
+	flag.StringVar(&blockNum, "block-num", "latest", "String denoting block number")
+	flag.BoolVar(&getBalance, "get-balance", false, "Boolean Flag to determine if API should get balance")
+	flag.BoolVar(&sendTx, "send-tx", false, "Boolean Flag to determine if API should send transaction")
+	flag.BoolVar(&txPoolContent, "txpool-content", false, "Boolean Flag to determine if API should get content of txpool")
+	flag.BoolVar(&clearDev, "clear-dev", false, "Boolean Flag to determine if service should clear /dev after this call")
+	flag.Parse()
+
+	//fmt.Printf("to: %v\n", to)
+	//fmt.Printf("value: %v\n", value)
+	//fmt.Printf("blockNum: %v\n", blockNum)
+	//fmt.Printf("getBalance: %v\n", getBalance)
+	//fmt.Printf("sendTx: %v\n", sendTx)
+	//fmt.Printf("txPoolContent: %v\n", txPoolContent)
+	//fmt.Printf("clearDev: %v\n", clearDev)
+
+	services.ValidateInputs(&getBalance, &sendTx, &txPoolContent, &blockNum, &value, &to)
+
+	services.ParseRequests(&getBalance, &sendTx, &txPoolContent, &clearDev, &blockNum, &value, &to)
+
+	fmt.Print("\n")
+	fmt.Print("Finished processing\n")
+}
diff --git a/cmd/devnettest/requests/request_generator.go b/cmd/devnettest/requests/request_generator.go
new file mode 100644
index 0000000000000000000000000000000000000000..c31fcc551397ce4da7e54ca0b3c36f93dc36342b
--- /dev/null
+++ b/cmd/devnettest/requests/request_generator.go
@@ -0,0 +1,53 @@
+package requests
+
+import (
+	"fmt"
+	"github.com/ledgerwatch/erigon/cmd/rpctest/rpctest"
+	"github.com/ledgerwatch/erigon/common"
+	"net/http"
+	"time"
+)
+
+var (
+	erigonUrl = "http://localhost:8545"
+)
+
+type RequestGenerator struct {
+	reqID  int
+	client *http.Client
+}
+
+func initialiseRequestGenerator() *RequestGenerator {
+	var client = &http.Client{
+		Timeout: time.Second * 600,
+	}
+
+	reqGen := RequestGenerator{
+		client: client,
+	}
+	reqGen.reqID++
+
+	return &reqGen
+}
+
+func (req *RequestGenerator) Erigon(method, body string, response interface{}) rpctest.CallResult {
+	return req.call(erigonUrl, method, body, response)
+}
+
+func (req *RequestGenerator) call(target string, method, body string, response interface{}) rpctest.CallResult {
+	start := time.Now()
+	err := post(req.client, erigonUrl, body, response)
+	return rpctest.CallResult{
+		RequestBody: body,
+		Target:      target,
+		Took:        time.Since(start),
+		RequestID:   req.reqID,
+		Method:      method,
+		Err:         err,
+	}
+}
+
+func (req *RequestGenerator) getBalance(address common.Address, blockNum string) string {
+	const template = `{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x%x","%v"],"id":%d}`
+	return fmt.Sprintf(template, address, blockNum, req.reqID)
+}
diff --git a/cmd/devnettest/requests/requests.go b/cmd/devnettest/requests/requests.go
new file mode 100644
index 0000000000000000000000000000000000000000..59d2ab8b8e0c2859742d427add984b46e7b55e04
--- /dev/null
+++ b/cmd/devnettest/requests/requests.go
@@ -0,0 +1,31 @@
+package requests
+
+import (
+	"crypto/ecdsa"
+	"fmt"
+	"github.com/ledgerwatch/erigon/cmd/rpctest/rpctest"
+	"github.com/ledgerwatch/erigon/common"
+)
+
+func GetBalance(address common.Address, blockNum string) {
+	reqGen := initialiseRequestGenerator()
+	var b rpctest.EthBalance
+
+	res := reqGen.Erigon("eth_getBalance", reqGen.getBalance(address, blockNum), &b)
+	if res.Err != nil {
+		fmt.Printf("Error getting balance: %v\n", res.Err)
+		return
+	}
+
+	fmt.Printf("Balance is: %v\n", b.Balance.ToInt())
+}
+
+func SendTx(from *ecdsa.PrivateKey, to common.Address, value uint64) {
+	fmt.Printf("from is: %v", from)
+	fmt.Printf("to is: %v", to)
+	fmt.Printf("value is: %v", value)
+}
+
+func TxpoolContent() string {
+	return ""
+}
diff --git a/cmd/devnettest/requests/utils.go b/cmd/devnettest/requests/utils.go
new file mode 100644
index 0000000000000000000000000000000000000000..46c5054578946817e3b7ee7f2c1dcfce8372cc53
--- /dev/null
+++ b/cmd/devnettest/requests/utils.go
@@ -0,0 +1,29 @@
+package requests
+
+import (
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+	"time"
+
+	"github.com/ledgerwatch/log/v3"
+)
+
+func post(client *http.Client, url, request string, response interface{}) error {
+	fmt.Printf("Request=%s\n", request)
+	log.Info("Getting", "url", url, "request", request)
+	start := time.Now()
+	r, err := client.Post(url, "application/json", strings.NewReader(request))
+	if err != nil {
+		return err
+	}
+	defer r.Body.Close()
+	if r.StatusCode != 200 {
+		return fmt.Errorf("status %s", r.Status)
+	}
+	decoder := json.NewDecoder(r.Body)
+	err = decoder.Decode(response)
+	log.Info("Got in", "time", time.Since(start).Seconds())
+	return err
+}
diff --git a/cmd/devnettest/services/utility.go b/cmd/devnettest/services/utility.go
new file mode 100644
index 0000000000000000000000000000000000000000..0e0a821756c92d515185298fb2cb1dbf91ef5180
--- /dev/null
+++ b/cmd/devnettest/services/utility.go
@@ -0,0 +1,68 @@
+package services
+
+import (
+	"fmt"
+	"github.com/ledgerwatch/erigon/cmd/devnettest/requests"
+	"github.com/ledgerwatch/erigon/common"
+	"github.com/ledgerwatch/erigon/crypto"
+)
+
+var devnetSignPrivateKey, _ = crypto.HexToECDSA("26e86e45f6fc45ec6e2ecd128cec80fa1d1505e5507dcd2ae58c3130a7a97b48")
+
+func ValidateInputs(getBalance *bool, sendTx *bool, txpoolContent *bool, blockNum *string, value *uint64, to *string) {
+	if !(*getBalance) && !(*sendTx) && !(*txpoolContent) {
+		panic("At least one function flag (get-balance, send-tx, txpool-content) should be true")
+	}
+
+	seen := false
+	for _, val := range []bool{*getBalance, *sendTx, *txpoolContent} {
+		if val {
+			if seen {
+				panic("Only function flag (get-balance, send-tx, txpool-content) can be true at a time")
+			}
+			seen = true
+		}
+	}
+
+	if *value <= 0 {
+		panic("Value must be greater than zero")
+	}
+
+	if *getBalance {
+		if *to == "" {
+			panic("Cannot check balance of empty address")
+		}
+		if *blockNum != "pending" && *blockNum != "latest" && *blockNum != "earliest" {
+			panic("Block number must be 'pending', 'latest' or 'earliest'")
+		}
+	}
+
+	if *sendTx && *to == "" {
+		panic("Cannot send to empty address")
+	}
+
+}
+
+func ParseRequests(getBalance *bool, sendTx *bool, txpoolContent *bool, clearDev *bool, blockNum *string, value *uint64, to *string) {
+	if *getBalance {
+		toAddress := common.HexToAddress(*to)
+		requests.GetBalance(toAddress, *blockNum)
+	}
+
+	if *sendTx {
+		toAddress := common.HexToAddress(*to)
+		requests.SendTx(devnetSignPrivateKey, toAddress, *value)
+	}
+
+	if *txpoolContent {
+		requests.TxpoolContent()
+	}
+
+	if *clearDev {
+		clearDevDB()
+	}
+}
+
+func clearDevDB() {
+	fmt.Printf("Clearing ~/dev\n")
+}