Repository: NethermindEth/juno
Pull request #101 (2022-03-14 10:13:21)

Feeder Getaway Client 
## Changes:
- Add client for Starknet Feeder Getaway Connection, based on this [starknet gateway client](https://github.com/starkware-libs/cairo-lang/blob/ed6cf8d6cec50a6ad95fa36d1eb4a7f48538019e/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py#L21)

## Types of changes

What types of changes does your code introduce?
_Put an `x` in the boxes that apply_

- [x] New feature (non-breaking change which adds functionality)

## Testing
**Requires testing**

- [x] Yes

**In case you checked yes, did you write tests??**

- [x] Yes


codecov[bot]: # [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) Report
> Merging [#101](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (9380d70) into [main](https://codecov.io/gh/NethermindEth/juno/commit/ded51cdbcabdcba24ee307addcb3f9e448ff2b98?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (ded51cd) will **decrease** coverage by `24.19%`.
> The diff coverage is `22.08%`.

[![Impacted file tree graph](https://codecov.io/gh/NethermindEth/juno/pull/101/graphs/tree.svg?width=650&height=150&src=pr&token=HwJpZ8kijD&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth)](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth)

```diff
@@             Coverage Diff              @@
##              main     #101       +/-   ##
============================================
- Coverage   100.00%   75.80%   -24.20%     
============================================
  Files            9       11        +2     
  Lines          364      525      +161     
============================================
+ Hits           364      398       +34     
- Misses           0      124      +124     
- Partials         0        3        +3     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `75.80% <22.08%> (-24.20%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) | Coverage Δ | |
|---|---|---|
| [pkg/gateway/types.go](https://codecov.io/gh/NethermindEth/juno/pull/101/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL2dhdGV3YXkvdHlwZXMuZ28=) | `0.00% <0.00%> (ø)` | |
| [pkg/gateway/client.go](https://codecov.io/gh/NethermindEth/juno/pull/101/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL2dhdGV3YXkvY2xpZW50Lmdv) | `21.29% <21.29%> (ø)` | |
| [internal/config/config.go](https://codecov.io/gh/NethermindEth/juno/pull/101/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-aW50ZXJuYWwvY29uZmlnL2NvbmZpZy5nbw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Last update [ded51cd...9380d70](https://codecov.io/gh/NethermindEth/juno/pull/101?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth).


Patch:
diff --git a/.github/workflows/juno-build.yml b/.github/workflows/juno-build.yml
index b48dadb6..5549048e 100644
--- a/.github/workflows/juno-build.yml
+++ b/.github/workflows/juno-build.yml
@@ -5,7 +5,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go: [ '1.17' ]
+        go: [ '1.18' ]
     name: Juno with Go ${{ matrix.go }}
     steps:
       - uses: actions/checkout@v2
diff --git a/Makefile b/Makefile
index 8316c2de..eac9e475 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,7 @@ test-cover: ## tests with coverage
 	go tool cover -html=coverage/coverage.out -o coverage/coverage.html
 
 install-deps: ## Install some project dependencies
-	git clone https://github.com/DemerzelSolutions/courtney
+	git clone https://github.com/stdevMac/courtney
 	(cd courtney && go get  ./... && go build courtney.go)
 	go get ./...
 
diff --git a/README.md b/README.md
index 90900d68..dd027a0b 100644
--- a/README.md
+++ b/README.md
@@ -26,16 +26,25 @@ Run the following command.
 
 ## Configuration
 
-**juno** uses a configuration file named **juno.yaml** that is located at:
+**juno** uses a configuration file named **juno.yaml** that is located in the following places depending on the operating system.
 
-`$XDG_CONFIG_HOME/juno/` for Unix (`$HOME/.config/juno/` if $XDG_CONFIG_HOME is not set), or
-`%AppData%/juno/` for Windows.
+- **macOS** - `$HOME/Library/Application Support/juno/`.
+- Other **Unix** systems - `$XDG_CONFIG_HOME/juno/` or `$HOME/.config/juno/` if the `$XDG_CONFIG_HOME` variable is not set.
+- **Windows** -  `%AppData%/juno/`.
 
-It generally looks like the following and a default will be generated if one does not exist. 
+It generally looks like the following and a default will be generated if one does not exist.
+
+The following is an example on how it would look on a macOS system (replace `$HOME` with a full path to the home directory).
 
 ```yaml
+db_path: $HOME/Library/Application Support/juno
+ethereum:
+  enabled: true
+  node: "ethereum_archive_node"
 rpc:
-  enabled: false
+  enabled: true
   port: 8080
-db_path: $HOME/.juno/data
+starknet:
+  enabled: true
+  feeder_gateway: "https://alpha-mainnet.starknet.io"
 ```
diff --git a/cmd/juno/cli/root.go b/cmd/juno/cli/root.go
index a4efe5db..e77ce577 100644
--- a/cmd/juno/cli/root.go
+++ b/cmd/juno/cli/root.go
@@ -50,8 +50,8 @@ var (
 
 			// Subscribe the RPC client to the main loop if it is enabled in
 			// the config.
-			if config.Runtime.Rpc.Enabled {
-				s := rpc.NewServer(":" + strconv.Itoa(config.Runtime.Rpc.Port))
+			if config.Runtime.RPC.Enabled {
+				s := rpc.NewServer(":" + strconv.Itoa(config.Runtime.RPC.Port))
 				handler.Add("RPC", s.ListenAndServe, s.Close)
 			}
 
@@ -104,8 +104,8 @@ func initConfig() {
 	errpkg.CheckFatal(err, "Unable to unmarshal runtime config instance.")
 	log.Default.With(
 		"Database Path", config.Runtime.DbPath,
-		"Rpc Port", config.Runtime.Rpc.Port,
-		"Rpc Enabled", config.Runtime.Rpc.Enabled,
+		"Rpc Port", config.Runtime.RPC.Port,
+		"Rpc Enabled", config.Runtime.RPC.Enabled,
 	).Info("Config values.")
 }
 
diff --git a/docs/docs/feeder-gateway.mdx b/docs/docs/feeder-gateway.mdx
index e08becc1..9ae3560b 100644
--- a/docs/docs/feeder-gateway.mdx
+++ b/docs/docs/feeder-gateway.mdx
@@ -36,4 +36,6 @@ start to get the state in Layer 1.
 
 For this connection, you will need to add to the configuration, a params, `starknet_network`, that should be allocated
 at some point around [here](https://github.com/NethermindEth/juno/blob/main/internal/config/config.go#L23) and in case
-of property don't exist, throw an error, just like starknet cli app.
\ No newline at end of file
+of property don't exist, throw an error, just like starknet cli app.
+
+Fixed in this [PR](https://github.com/NethermindEth/juno/pull/101)
\ No newline at end of file
diff --git a/docs/docs/sidebars.js b/docs/docs/sidebars.js
index 29e2901e..3b40920e 100644
--- a/docs/docs/sidebars.js
+++ b/docs/docs/sidebars.js
@@ -1,9 +1,7 @@
 module.exports = {
     docs: [
         {
-            "Welcome": [
-                'welcome',
-            ],
+            "Welcome": ['welcome'],
         },
         {
             "Running": [
@@ -16,11 +14,28 @@ module.exports = {
                 'getting-started-juno',
                 'getting-started-starknet',
                 'getting-started-go',
+            ]
+        },
+        {
+            "Contribute": [
+                {
+                    "Juno": [
+                        'contribution-juno'
+                    ]
+                },
+                {
+                    "Docs": [
+                        'contribution-guide',
+                        'cheatsheet'
+                    ]
+                },
             ],
         },
         {
-            "Future Implementations": [
+            "Implemented features": [
                 'feeder-gateway',
+            ],
+            "Future Implementations": [
                 'cairo-integration',
                 'starknet-cryptography',
                 'starknet-state',
diff --git a/docs/docusaurus.config.js b/docs/docusaurus.config.js
index d203f814..e3528343 100644
--- a/docs/docusaurus.config.js
+++ b/docs/docusaurus.config.js
@@ -49,7 +49,7 @@ const darkCodeTheme = require('prism-react-renderer/themes/dracula');
           },
           items: [
             {
-              href: 'https://github.com/NethermindEth/Juno/docs',
+              href: 'https://github.com/NethermindEth/juno/',
               label: 'GitHub',
               position: 'right',
             },
diff --git a/go.mod b/go.mod
index a1254750..3cc8b4e1 100644
--- a/go.mod
+++ b/go.mod
@@ -3,11 +3,13 @@ module github.com/NethermindEth/juno
 go 1.18
 
 require (
+	github.com/bxcodec/faker v2.0.1+incompatible
 	github.com/ethereum/go-ethereum v1.10.16
 	github.com/goccy/go-json v0.9.5
 	github.com/iancoleman/strcase v0.2.0
 	github.com/spf13/cobra v1.3.0
 	github.com/spf13/viper v1.10.1
+	github.com/stretchr/testify v1.7.0
 	github.com/torquem-ch/mdbx-go v0.22.15
 	go.uber.org/zap v1.21.0
 	golang.org/x/crypto v0.0.0-20220314234724-5d542ad81a58
@@ -16,6 +18,7 @@ require (
 
 require (
 	github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/deckarep/golang-set v1.8.0 // indirect
 	github.com/fsnotify/fsnotify v1.5.1 // indirect
 	github.com/go-ole/go-ole v1.2.1 // indirect
@@ -26,6 +29,7 @@ require (
 	github.com/magiconair/properties v1.8.5 // indirect
 	github.com/mitchellh/mapstructure v1.4.3 // indirect
 	github.com/pelletier/go-toml v1.9.4 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect
 	github.com/spf13/afero v1.6.0 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
@@ -36,8 +40,9 @@ require (
 	github.com/tklauser/numcpus v0.2.2 // indirect
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
-	golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
+	golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
 	golang.org/x/text v0.3.7 // indirect
 	gopkg.in/ini.v1 v1.66.2 // indirect
 	gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
 )
diff --git a/go.sum b/go.sum
index 44754e18..2896cb50 100644
--- a/go.sum
+++ b/go.sum
@@ -111,6 +111,8 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa
 github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
 github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA=
+github.com/bxcodec/faker v2.0.1+incompatible/go.mod h1:BNzfpVdTwnFJ6GtfYTcQu6l6rHShT+veBxNCnjCx5XM=
 github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -796,8 +798,8 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 h1:nhht2DYV/Sn3qOayu8lM+cU1ii9sTLUeBQwQQfUHtrs=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
+golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/internal/config/config.go b/internal/config/config.go
index a6786247..1b34c4de 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -21,8 +21,9 @@ type rpcConfig struct {
 
 // Config represents the juno configuration.
 type Config struct {
-	Rpc    rpcConfig `yaml:"rpc" mapstructure:"rpc"`
-	DbPath string    `yaml:"db_path" mapstructure:"db_path"`
+	RPC     rpcConfig `yaml:"rpc" mapstructure:"rpc"`
+	DbPath  string    `yaml:"db_path" mapstructure:"db_path"`
+	Network string    `yaml:"starknet_network" mapstructure:"starknet_network"`
 }
 
 var (
@@ -44,6 +45,8 @@ var (
 // Runtime is the runtime configuration of the application.
 var Runtime *Config
 
+const goerli = "http://alpha4.starknet.io"
+
 func init() {
 	// Set user config directory.
 	d, err := os.UserConfigDir()
@@ -56,7 +59,7 @@ func init() {
 		switch runtime.GOOS {
 		case "windows":
 			// On Windows ConfigDir and DataDir share the same path. See:
-			// https://stackoverflow.com/questions/43853548/xdg-basedir-directories-for-windows
+			// https://stackoverflow.com/questions/43853548/xdg-basedir-directories-for-windows.
 			return Dir, nil
 		case "darwin", "dragonfly", "freebsd", "illumos", "ios", "linux", "netbsd",
 			"openbsd", "solaris":
@@ -68,6 +71,7 @@ func init() {
 	errpkg.CheckFatal(err, "Unable to get user data directory.")
 }
 
+// New creates a new configuration file with default values.
 func New() {
 	f := filepath.Join(Dir, "juno.yaml")
 	log.Default.With("Path", f).Info("Creating default config.")
@@ -78,8 +82,9 @@ func New() {
 		errpkg.CheckFatal(err, "Failed to create Config directory.")
 	}
 	data, err := yaml.Marshal(&Config{
-		Rpc:    rpcConfig{Enabled: false, Port: 8080},
-		DbPath: Dir,
+		RPC:     rpcConfig{Enabled: false, Port: 8080},
+		DbPath:  Dir,
+		Network: goerli,
 	})
 	errpkg.CheckFatal(err, "Failed to marshal Config instance to byte data.")
 	err = os.WriteFile(f, data, 0644)
diff --git a/internal/log/log.go b/internal/log/log.go
index da18a9c2..ded8dce2 100644
--- a/internal/log/log.go
+++ b/internal/log/log.go
@@ -1,7 +1,11 @@
 // Package log provides a logger.
 package log
 
-import "go.uber.org/zap"
+import (
+	"log"
+
+	"go.uber.org/zap"
+)
 
 // Default is the default logger. It is a "sugared" variant of the zap
 // logger.
@@ -10,7 +14,8 @@ var Default *zap.SugaredLogger
 func init() {
 	logger, err := zap.NewDevelopment()
 	if err != nil {
-		panic(err)
+		// notest
+		log.Fatalln("failed to initialise application logger")
 	}
 	Default = logger.Sugar()
 }
diff --git a/pkg/feeder/feeder.go b/pkg/feeder/feeder.go
new file mode 100644
index 00000000..0ddd68be
--- /dev/null
+++ b/pkg/feeder/feeder.go
@@ -0,0 +1,387 @@
+// Package feeder represent a client for the Feeder Gateway connection.
+// For more details of the implementation, see this client https://github.com/starkware-libs/cairo-lang/blob/master/src/starkware/starknet/services/api/feeder_gateway/feeder_gateway_client.py.
+package feeder
+
+import (
+	"bytes"
+	"encoding/json"
+	"io"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/NethermindEth/juno/internal/errpkg"
+	"github.com/NethermindEth/juno/internal/log"
+)
+
+//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . FeederHttpClient
+type HttpClient interface {
+	Do(*http.Request) (*http.Response, error)
+}
+
+// Client represents a client for the StarkNet feeder gateway.
+type Client struct {
+	httpClient *HttpClient
+
+	BaseURL            *url.URL
+	BaseAPI, UserAgent string
+}
+
+// NewClient returns a new Client.
+func NewClient(baseURL, baseAPI string, client *HttpClient) *Client {
+	u, err := url.Parse(baseURL)
+	errpkg.CheckFatal(err, "Bad base URL.")
+	if client == nil {
+		var p HttpClient
+		c := http.Client{
+			Timeout: 50 * time.Second,
+		}
+		p = &c
+		client = &p
+	}
+	return &Client{BaseURL: u, BaseAPI: baseAPI, httpClient: client}
+}
+
+func formattedBlockIdentifier(blockHash, blockNumber string) map[string]string {
+	if len(blockHash) == 0 && len(blockNumber) == 0 {
+		// notest
+		return nil
+	}
+	if len(blockHash) == 0 {
+		return map[string]string{"blockNumber": blockNumber}
+	}
+	return map[string]string{"blockHash": blockHash}
+}
+
+func TxnIdentifier(txHash, txId string) map[string]string {
+	if len(txHash) == 0 && len(txId) == 0 {
+		// notest
+		return nil
+	}
+
+	if len(txHash) == 0 {
+		return map[string]string{"transactionId": txId}
+	}
+	return map[string]string{"transactionHash": txHash}
+
+}
+
+// newRequest creates a new request based on params and returns an
+// error otherwise.
+func (c *Client) newRequest(method, path string, query map[string]string, body any) (*http.Request, error) {
+	rel := &url.URL{Path: c.BaseAPI + path}
+	u := c.BaseURL.ResolveReference(rel)
+	var buf io.ReadWriter
+	if body != nil {
+		buf = new(bytes.Buffer)
+		err := json.NewEncoder(buf).Encode(body)
+		if err != nil {
+			return nil, err
+		}
+	}
+	req, err := http.NewRequest(method, u.String(), buf)
+	if err != nil {
+		return nil, err
+	}
+	if body != nil {
+		req.Header.Set("Content-Type", "application/json")
+	}
+	if query != nil {
+		q := req.URL.Query()
+		for k, v := range query {
+			q.Add(k, v)
+		}
+		req.URL.RawQuery = q.Encode()
+	}
+
+	req.Header.Set("Accept", "application/json")
+	req.Header.Set("User-Agent", c.UserAgent)
+	return req, nil
+}
+
+// do executes a request and waits for response and returns an error
+// otherwise.
+func (c *Client) do(req *http.Request, v any) (*http.Response, error) {
+	res, err := (*c.httpClient).Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer func(Body io.ReadCloser) {
+		err := Body.Close()
+		if err != nil {
+			// notest
+			log.Default.With("Error", err).Error("Error closing body of response.")
+			return
+		}
+	}(res.Body)
+	b, err := io.ReadAll(res.Body)
+	if err != nil {
+		log.Default.With("Error", err).Debug("Error reading response.")
+		return nil, err
+	}
+	err = json.Unmarshal(b, v)
+	return res, err
+}
+
+// GetContractAddresses creates a new request to get contract addresses
+// from the gateway.
+func (c Client) GetContractAddresses() (ContractAddresses, error) {
+	log.Default.With("Gateway URL", c.BaseURL).Info("Getting contract address from gateway.")
+	req, err := c.newRequest("GET", "/get_contract_addresses", nil, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return ContractAddresses{}, err
+	}
+	var res ContractAddresses
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return ContractAddresses{}, err
+	}
+	return res, err
+}
+
+// CallContract creates a new request to call a contract in the gateway.
+func (c Client) CallContract(invokeFunc InvokeFunction, blockHash, blockNumber string) (map[string][]string, error) {
+	req, err := c.newRequest("POST", "/call_contract", formattedBlockIdentifier(blockHash, blockNumber), invokeFunc)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return nil, err
+	}
+	var res map[string][]string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return nil, err
+	}
+	return res, err
+}
+
+// GetBlock creates a new request to get a block from the gateway.
+func (c Client) GetBlock(blockHash, blockNumber string) (StarknetBlock, error) {
+	req, err := c.newRequest("GET", "/get_block", formattedBlockIdentifier(blockHash, blockNumber), nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return StarknetBlock{}, err
+	}
+	var res StarknetBlock
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return StarknetBlock{}, err
+	}
+	return res, err
+}
+
+// GetStateUpdate creates a new request to get the contract addresses
+// from the gateway.
+func (c Client) GetStateUpdate(blockHash, blockNumber string) (StateUpdateResponse, error) {
+	req, err := c.newRequest("GET", "/get_state_update", formattedBlockIdentifier(blockHash, blockNumber), nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return StateUpdateResponse{}, err
+	}
+
+	var res StateUpdateResponse
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return StateUpdateResponse{}, err
+	}
+	return res, err
+}
+
+// GetCode creates a new request to get the code of the contract
+// address.
+func (c Client) GetCode(contractAddress, blockHash, blockNumber string) ([]string, error) {
+	blockIdentifier := formattedBlockIdentifier(blockHash, blockNumber)
+	if blockIdentifier == nil {
+		// notest
+		blockIdentifier = map[string]string{}
+	}
+	blockIdentifier["contractAddress"] = contractAddress
+	req, err := c.newRequest("GET", "/get_code", blockIdentifier, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return nil, err
+	}
+	var res []string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return nil, err
+	}
+	return res, err
+}
+
+// GetFullContract creates a new request to get the full state of a
+// contract.
+func (c Client) GetFullContract(contractAddress, blockHash, blockNumber string) (any, error) {
+	blockIdentifier := formattedBlockIdentifier(blockHash, blockNumber)
+	if blockIdentifier == nil {
+		// notest
+		blockIdentifier = map[string]string{}
+	}
+	blockIdentifier["contractAddress"] = contractAddress
+
+	req, err := c.newRequest("GET", "/get_full_contract", blockIdentifier, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return nil, err
+	}
+	var res any
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return nil, err
+	}
+	return res, err
+}
+
+// GetStorageAt creates a new request to get contract storage.
+func (c Client) GetStorageAt(contractAddress, key, blockHash, blockNumber string) (string, error) {
+	blockIdentifier := formattedBlockIdentifier(blockHash, blockNumber)
+	if blockIdentifier == nil {
+		// notest
+		blockIdentifier = map[string]string{}
+	}
+	blockIdentifier["contractAddress"] = contractAddress
+	blockIdentifier["key"] = key
+
+	req, err := c.newRequest("GET", "/get_storage_at",
+		blockIdentifier, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return "", err
+	}
+	var res string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return "", err
+	}
+	return res, err
+}
+
+// GetTransactionStatus creates a new request to get the transaction
+// status.
+func (c Client) GetTransactionStatus(txHash, txID string) (any, error) {
+	req, err := c.newRequest("GET", "/get_transaction_status", TxnIdentifier(txHash, txID), nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return nil, err
+	}
+	var res any
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return nil, err
+	}
+	return res, err
+}
+
+// GetTransaction creates a new request to get a TransactionInfo.
+func (c Client) GetTransaction(txHash, txID string) (TransactionInfo, error) {
+	req, err := c.newRequest("GET", "/get_transaction", TxnIdentifier(txHash, txID), nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return TransactionInfo{}, err
+	}
+	var res TransactionInfo
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return TransactionInfo{}, err
+	}
+	return res, err
+}
+
+// GetTransactionReceipt creates a new request to get a
+// TransactionReceipt.
+func (c Client) GetTransactionReceipt(txHash, txID string) (TransactionReceipt, error) {
+	req, err := c.newRequest("GET", "/get_transaction_receipt", TxnIdentifier(txHash, txID), nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return TransactionReceipt{}, err
+	}
+	var res TransactionReceipt
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return TransactionReceipt{}, err
+	}
+	return res, err
+}
+
+// GetBlockHashById creates a new request to get block hash by on ID.
+func (c Client) GetBlockHashById(blockID string) (string, error) {
+	req, err := c.newRequest(
+		"GET", "/get_block_hash_by_id", map[string]string{"blockId": blockID}, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return "", err
+	}
+	var res string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return "", err
+	}
+	return res, err
+}
+
+// GetBlockIDByHash creates a new request to get the block ID by hash.
+func (c Client) GetBlockIDByHash(blockHash string) (string, error) {
+	req, err := c.newRequest(
+		"GET", "/get_block_id_by_hash", map[string]string{"blockHash": blockHash}, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return "", err
+	}
+	var res string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return "", err
+	}
+	return res, err
+}
+
+// GetTransactionHashByID creates a new request to get a transaction
+// hash by ID.
+func (c Client) GetTransactionHashByID(txID string) (string, error) {
+	req, err := c.newRequest(
+		"GET", "/get_transaction_hash_by_id",
+		map[string]string{"transactionId": txID}, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return "", err
+	}
+	var res string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Error connecting to the gateway.")
+		return "", err
+	}
+	return res, err
+}
+
+// GetTransactionIDByHash creates a new request to get a transaction ID
+// by hash.
+func (c Client) GetTransactionIDByHash(txHash string) (string, error) {
+	req, err := c.newRequest(
+		"GET", "/get_transaction_id_by_hash",
+		map[string]string{"transactionHash": txHash}, nil)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).Error("Unable to create a request for get_contract_addresses.")
+		return "", err
+	}
+	var res string
+	_, err = c.do(req, &res)
+	if err != nil {
+		log.Default.With("Error", err, "Gateway URL", c.BaseURL).
+			Error("Error connecting to the gateway.")
+		return "", err
+	}
+	return res, err
+}
diff --git a/pkg/feeder/feederfakes/fake_http_client.go b/pkg/feeder/feederfakes/fake_http_client.go
new file mode 100644
index 00000000..7f9b0538
--- /dev/null
+++ b/pkg/feeder/feederfakes/fake_http_client.go
@@ -0,0 +1,118 @@
+// Code generated by counterfeiter. DO NOT EDIT.
+package feederfakes
+
+// notest
+import (
+	"net/http"
+	"sync"
+
+	"github.com/NethermindEth/juno/pkg/feeder"
+)
+
+type FakeHttpClient struct {
+	DoStub        func(*http.Request) (*http.Response, error)
+	doMutex       sync.RWMutex
+	doArgsForCall []struct {
+		arg1 *http.Request
+	}
+	doReturns struct {
+		result1 *http.Response
+		result2 error
+	}
+	doReturnsOnCall map[int]struct {
+		result1 *http.Response
+		result2 error
+	}
+	invocations      map[string][][]interface{}
+	invocationsMutex sync.RWMutex
+}
+
+func (fake *FakeHttpClient) Do(arg1 *http.Request) (*http.Response, error) {
+	fake.doMutex.Lock()
+	ret, specificReturn := fake.doReturnsOnCall[len(fake.doArgsForCall)]
+	fake.doArgsForCall = append(fake.doArgsForCall, struct {
+		arg1 *http.Request
+	}{arg1})
+	stub := fake.DoStub
+	fakeReturns := fake.doReturns
+	fake.recordInvocation("Do", []interface{}{arg1})
+	fake.doMutex.Unlock()
+	if stub != nil {
+		return stub(arg1)
+	}
+	if specificReturn {
+		return ret.result1, ret.result2
+	}
+	return fakeReturns.result1, fakeReturns.result2
+}
+
+func (fake *FakeHttpClient) DoCallCount() int {
+	fake.doMutex.RLock()
+	defer fake.doMutex.RUnlock()
+	return len(fake.doArgsForCall)
+}
+
+func (fake *FakeHttpClient) DoCalls(stub func(*http.Request) (*http.Response, error)) {
+	fake.doMutex.Lock()
+	defer fake.doMutex.Unlock()
+	fake.DoStub = stub
+}
+
+func (fake *FakeHttpClient) DoArgsForCall(i int) *http.Request {
+	fake.doMutex.RLock()
+	defer fake.doMutex.RUnlock()
+	argsForCall := fake.doArgsForCall[i]
+	return argsForCall.arg1
+}
+
+func (fake *FakeHttpClient) DoReturns(result1 *http.Response, result2 error) {
+	fake.doMutex.Lock()
+	defer fake.doMutex.Unlock()
+	fake.DoStub = nil
+	fake.doReturns = struct {
+		result1 *http.Response
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeHttpClient) DoReturnsOnCall(i int, result1 *http.Response, result2 error) {
+	fake.doMutex.Lock()
+	defer fake.doMutex.Unlock()
+	fake.DoStub = nil
+	if fake.doReturnsOnCall == nil {
+		fake.doReturnsOnCall = make(map[int]struct {
+			result1 *http.Response
+			result2 error
+		})
+	}
+	fake.doReturnsOnCall[i] = struct {
+		result1 *http.Response
+		result2 error
+	}{result1, result2}
+}
+
+func (fake *FakeHttpClient) Invocations() map[string][][]interface{} {
+	fake.invocationsMutex.RLock()
+	defer fake.invocationsMutex.RUnlock()
+	fake.doMutex.RLock()
+	defer fake.doMutex.RUnlock()
+	copiedInvocations := map[string][][]interface{}{}
+	for key, value := range fake.invocations {
+		copiedInvocations[key] = value
+	}
+	return copiedInvocations
+}
+
+func (fake *FakeHttpClient) recordInvocation(key string, args []interface{}) {
+	fake.invocationsMutex.Lock()
+	defer fake.invocationsMutex.Unlock()
+	if fake.invocations == nil {
+		fake.invocations = map[string][][]interface{}{}
+	}
+	if fake.invocations[key] == nil {
+		fake.invocations[key] = [][]interface{}{}
+	}
+	fake.invocations[key] = append(fake.invocations[key], args)
+}
+
+var _ feeder.HttpClient = new(FakeHttpClient)
diff --git a/pkg/feeder/types.go b/pkg/feeder/types.go
new file mode 100644
index 00000000..59704e9b
--- /dev/null
+++ b/pkg/feeder/types.go
@@ -0,0 +1,227 @@
+package feeder
+
+// notest
+import "github.com/NethermindEth/juno/pkg/rpc"
+
+type (
+	// TxnType represents the type of each transaction.
+	TxnType int
+	// ChainID represent the chain identifier.
+	ChainID string
+	// XXX: Document.
+	// TODO: replace with real hash representation.
+	Hash string
+)
+
+// Represent the types of transactions
+const (
+	Deploy TxnType = iota
+	InitializeBlockInfo
+	Invoke
+)
+
+// Represent the identifiers of the
+const (
+	Mainnet ChainID = "MAINNET"
+	Testnet ChainID = "Goerli"
+)
+
+// ContractAddresses represent the response for Starknet contract
+// address details.
+type ContractAddresses struct {
+	GpsStatementVerifier string `json:"GpsStatementVerifier"`
+	Starknet             string `json:"Starknet"`
+}
+
+// StarknetGeneralConfig represent StarkNet general configuration.
+type StarknetGeneralConfig struct {
+	ChainID                             ChainID `json:"chain_id"`
+	ContractStorageCommitmentTreeHeight int64   `json:"contract_storage_commitment_tree_height"`
+	GlobalStateCommitmentTreeHeight     int64   `json:"global_state_commitment_tree_height"`
+	InvokeTxMaxNSteps                   int64   `json:"invoke_tx_max_n_steps"`
+	// StarkNet sequencer address.
+	SequencerAddress int64 `json:"sequencer_address"`
+	// Height of Patricia tree of the transaction commitment in a block.
+	TxCommitmentTreeHeight int `json:"tx_commitment_tree_height"`
+	// Height of Patricia tree of the event commitment in a block.
+	EventCommitmentTreeHeight int `json:"event_commitment_tree_height"`
+	// A mapping from a Cairo usage resource to its coefficient in this
+	// transaction fee calculation.
+	CairoUsageResourceFeeWeights map[string]float64 `json:"cairo_usage_resource_fee_weights"`
+}
+
+// Transaction StarkNet transaction base interface
+type Transaction interface {
+	TransactionType() TxnType
+	CalculateHash(config StarknetGeneralConfig) Hash
+}
+
+// InvokeFunction represents a transaction in the StarkNet network that
+// is an invocation of a Cairo contract function.
+type InvokeFunction struct {
+	ContractAddress int `json:"contract_address"`
+	// A field element that encodes the signature of the called function.
+	EntryPointSelector int   `json:"entry_point_selector"`
+	Calldata           []int `json:"calldata"`
+	// Additional information given by the caller that represents the
+	// signature of the transaction. The exact way this field is handled
+	// is defined by the called contract's function, like calldata.
+	Signature []int `json:"signature"`
+}
+
+// TransactionType returns the TxnType related to InvokeFunction
+func (i InvokeFunction) TransactionType() TxnType {
+	return Invoke
+}
+
+// XXX: Document.
+// CalculateHash returns the hash of the tra
+func (i InvokeFunction) CalculateHash(config StarknetGeneralConfig) Hash {
+	// TODO: implement this
+	return Hash(config.ChainID)
+}
+
+// TxnSpecificInfo represent a StarkNet transaction information.
+type TxnSpecificInfo struct {
+	Calldata           []string `json:"calldata"`
+	ContractAddress    string   `json:"contract_address"`
+	EntryPointSelector string   `json:"entry_point_selector"`
+	EntryPointType     string   `json:"entry_point_type"`
+	Signature          []string `json:"signature"`
+	TransactionHash    string   `json:"transaction_hash"`
+	Type               TxnType  `json:"type"`
+}
+
+// L1ToL2Message Represents a StarkNet L1-to-L2 message.
+type L1ToL2Message struct {
+	FromAddress string  `json:"from_address"`
+	ToAddress   string  `json:"to_address"`
+	Selector    int64   `json:"selector"`
+	Payload     []int64 `json:"payload"`
+	Nonce       int64   `json:"nonce"`
+}
+
+// L2ToL1Message Represents a StarkNet L2-to-L1 message.
+type L2ToL1Message struct {
+	FromAddress string  `json:"from_address"`
+	ToAddress   string  `json:"to_address"`
+	Payload     []int64 `json:"payload"`
+}
+
+// Event Represents a StarkNet event; contains all the fields that will
+// be included in the block hash.
+type Event struct {
+	FromAddress string  `json:"from_address"`
+	Keys        []int64 `json:"keys"`
+	Data        []int64 `json:"data"`
+}
+
+// ExecutionResources Indicates how many steps the program should run,
+// how many memory cells are used from each builtin, and how many holes
+// there are in the memory address space.
+type ExecutionResources struct {
+	NSteps                 int64            `json:"n_steps"`
+	BuiltinInstanceCounter map[string]int64 `json:"builtin_instance_counter"`
+	NMemoryHoles           map[string]int64 `json:"n_memory_holes"`
+}
+
+// TransactionExecution Represents a receipt of an executed transaction.
+type TransactionExecution struct {
+	// The index of the transaction within the block.
+	TransactionIndex int64 `json:"transaction_index"`
+	// A unique identifier of the transaction.
+	TransactionHash string `json:"transaction_hash"`
+	// L1-to-L2 messages.
+	L1ToL2ConsumedMessage L1ToL2Message `json:"l1_to_l2_consumed_message"`
+	// L2-to-L1 messages.
+	L2ToL1Messages []L2ToL1Message `json:"l2_to_l1_messages"`
+	// Events emitted during the execution of the transaction.
+	Events []Event `json:"events"`
+	// The resources needed by the transaction.
+	ExecutionResources ExecutionResources `json:"execution_resources"`
+}
+
+// StarknetBlock Represents a response StarkNet block.
+type StarknetBlock struct {
+	BlockHash           string               `json:"block_hash"`
+	ParentBlockHash     string               `json:"parent_block_hash"`
+	BlockNumber         string               `json:"block_number"`
+	StateRoot           string               `json:"state_root"`
+	Status              rpc.BlockStatus      `json:"status"`
+	Transactions        TxnSpecificInfo      `json:"transactions"`
+	Timestamp           int64                `json:"timestamp"`
+	TransactionReceipts TransactionExecution `json:"transaction_receipts"`
+}
+
+// TransactionFailureReason store reason of failure in transactions.
+type TransactionFailureReason struct {
+	TxID     int64  `json:"tx_id"`
+	Code     string `json:"code"`
+	ErrorMsg string `json:"error_message"`
+}
+
+// XXX: Document.
+// TransactionInfo store all the transaction Inf
+type TransactionInfo struct {
+	// The status of a transaction, see TransactionStatus.
+	Status rpc.TxnStatus
+	// The reason for the transaction failure, if applicable.
+	TransactionFailureReason TransactionFailureReason `json:"transaction_failure_reason"`
+	// The unique identifier of the block on the active chain containing
+	// the transaction.
+	BlockHash string `json:"block_hash"`
+	// The sequence number of the block corresponding to block_hash, which
+	// is the number of blocks prior to it in the active chain.
+	BlockNumber string `json:"block_number"`
+	//	The index of the transaction within the block corresponding to
+	// block_hash.
+	TransactionIndex int64           `json:"transaction_index"`
+	Transaction      TxnSpecificInfo `json:"transaction"`
+}
+
+// TransactionInBlockInfo represents the information regarding a
+// transaction that appears in a block.
+type TransactionInBlockInfo struct {
+	// The status of a transaction, see TransactionStatus.
+	Status rpc.TxnStatus
+	// The reason for the transaction failure, if applicable.
+	TransactionFailureReason TransactionFailureReason `json:"transaction_failure_reason"`
+	// The unique identifier of the block on the active chain containing
+	// the transaction.
+	BlockHash string `json:"block_hash"`
+	// The sequence number of the block corresponding to block_hash, which
+	// is the number of blocks prior to it in the active chain.
+	BlockNumber string `json:"block_number"`
+	//	The index of the transaction within the block corresponding to
+	// block_hash.
+	TransactionIndex int64 `json:"transaction_index"`
+}
+
+// TransactionReceipt represents a receipt of a StarkNet transaction;
+// i.e., the information regarding its execution and the block it
+// appears in.
+type TransactionReceipt struct {
+	TransactionExecution
+	TransactionInBlockInfo
+}
+
+// KV represents a key-value pair.
+type KV struct {
+	Key   string `json:"key"`
+	Value string `json:"value"`
+}
+
+// StateUpdateResponse represents the response of a StarkNet state
+// update.
+type StateUpdateResponse struct {
+	BlockHash string `json:"block_hash"`
+	NewRoot   string `json:"new_root"`
+	OldRoot   string `json:"old_root"`
+	StateDiff struct {
+		DeployedContracts []struct {
+			Address      string `json:"address"`
+			ContractHash string `json:"contract_hash"`
+		} `json:"deployed_contracts"`
+		StorageDiffs map[string][]KV `json:"storage_diffs"`
+	} `json:"state_diff"`
+}
diff --git a/pkg/rpc/handle.go b/pkg/rpc/handle.go
index 68e5d250..514b4a82 100644
--- a/pkg/rpc/handle.go
+++ b/pkg/rpc/handle.go
@@ -26,7 +26,7 @@ var (
 type Handler interface {
 	ServeJSONRPC(
 		c context.Context, params *json.RawMessage,
-	) (result interface{}, err *Error)
+	) (result any, err *Error)
 }
 
 // HandlerFunc type is an adapter that allows the use of Go functions as
@@ -34,12 +34,12 @@ type Handler interface {
 // signature, HandlerFunc(f) is a JSON-RPC.Handler that calls f.
 type HandlerFunc func(
 	c context.Context, params *json.RawMessage,
-) (result interface{}, err *Error)
+) (result any, err *Error)
 
 // ServeJSONRPC calls a function f(w, r).
 func (f HandlerFunc) ServeJSONRPC(
 	c context.Context, params *json.RawMessage,
-) (result interface{}, err *Error) {
+) (result any, err *Error) {
 	// notest
 	return f(c, params)
 }
@@ -207,7 +207,6 @@ func parseArgSlice(
 				log.Default.With("Type", t, "Tag", tag).Debug("Parsing parameter.")
 				if strings.Contains(tag, "true") && fields.Field(i).IsZero() {
 					// notest
-					// XXX: Highlight which missing field this is.
 					return args, errors.New("missing required field")
 				}
 			}
@@ -228,7 +227,7 @@ func callFunc(
 	receiver reflect.Value,
 	hasContext bool,
 	errResponsePosition int,
-) (res interface{}, errRes error) {
+) (res any, errRes error) {
 	log.Default.With("Method", method).Info("Calling RPC function.")
 	// Create the argument slice.
 	fullArgs := make([]reflect.Value, 0, 2+len(args))
@@ -243,7 +242,9 @@ func callFunc(
 	// Catch panic while running the callback.
 	defer func() {
 		// notest
-		if err := recover(); err != nil {
+		err := recover()
+		val := reflect.ValueOf(err)
+		if val.IsValid() && !val.IsNil() {
 			const size = 64 << 10
 			buf := make([]byte, size)
 			buf = buf[:runtime.Stack(buf, false)]
@@ -299,8 +300,6 @@ func (h *HandlerJsonRpc) InvokeMethod(
 		// types of the function that is going to be called.
 		args, err = parseArgs(*r.Params, argTypes)
 		if err != nil {
-			// XXX: Use more robust error handling instead of just matching
-			// against a substring.
 			if err == errorTooManyArgs {
 				log.Default.Info("Searching for overload...")
 				structToCall = reflect.ValueOf(h.StructRpc)


Test patch:
diff --git a/.github/workflows/juno-test.yml b/.github/workflows/juno-test.yml
index fbee2044..bf1d127e 100644
--- a/.github/workflows/juno-test.yml
+++ b/.github/workflows/juno-test.yml
@@ -12,7 +12,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        go: ['1.17']
+        go: ['1.18']
     steps:
       - uses: actions/checkout@v2
       - name: set up go
diff --git a/pkg/feeder/tests/feeder_test.go b/pkg/feeder/tests/feeder_test.go
new file mode 100644
index 00000000..79f9c53b
--- /dev/null
+++ b/pkg/feeder/tests/feeder_test.go
@@ -0,0 +1,283 @@
+// package feeder_test
+package tests
+
+// NOTE: feederfakes creates an import cycle so testing has to be in a
+// different package.
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"reflect"
+	"testing"
+
+	"github.com/NethermindEth/juno/pkg/feeder"
+	"github.com/NethermindEth/juno/pkg/feeder/feederfakes"
+	"github.com/bxcodec/faker"
+	"github.com/stretchr/testify/assert"
+)
+
+var httpClient = &feederfakes.FakeHttpClient{}
+var client *feeder.Client
+
+func init() {
+	var p feeder.HttpClient
+	p = httpClient
+	client = feeder.NewClient("https:/local", "/feeder_gateway/", &p)
+}
+
+func generateResponse(body string) *http.Response {
+	return &http.Response{
+		Status:        "200 OK",
+		StatusCode:    200,
+		Proto:         "HTTP/1.1",
+		ProtoMajor:    1,
+		ProtoMinor:    1,
+		Body:          ioutil.NopCloser(bytes.NewBufferString(body)),
+		ContentLength: int64(len(body)),
+		Header:        make(http.Header, 0),
+	}
+}
+
+func StructFaker(a interface{}) (string, error) {
+	s := reflect.ValueOf(a)
+	err := faker.FakeData(&s)
+	if err != nil {
+		return "", err
+	}
+	body, err := json.Marshal(s)
+	if err != nil {
+		return "", err
+	}
+	return string(body), nil
+}
+
+func TestClient(t *testing.T) {
+	_ = feeder.NewClient("https:/local", "/feeder_gateway/", nil)
+}
+
+func TestGetContractAddress(t *testing.T) {
+	// XXX: Use raw string literal to formal JSON.
+	body := "{\"GpsStatementVerifier\":\"0x47312450B3Ac8b5b8e247a6bB6d523e7605bDb60\",\"Starknet\":\"0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4\"}\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig feeder.ContractAddresses
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	contractAddresses, err := client.GetContractAddresses()
+	if err != nil {
+		t.Fatal()
+		return
+	}
+	assert.Equal(t, cOrig, contractAddresses, "Contract Address don't match")
+}
+
+func TestCallContract(t *testing.T) {
+	a := make(map[string][]string)
+	body, err := json.Marshal(a)
+
+	httpClient.DoReturns(generateResponse(string(body)), nil)
+	contractResponse, err := client.CallContract(feeder.InvokeFunction{}, "", "latest")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, a, contractResponse, "CallContract response don't match")
+}
+
+func TestGetBlock(t *testing.T) {
+	a := feeder.StarknetBlock{}
+	body, err := StructFaker(a)
+	if err != nil {
+		t.Fatal()
+	}
+	httpClient.DoReturns(generateResponse(body), nil)
+	starknetBlock, err := client.GetBlock("", "latest")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, a, starknetBlock, "StarknetBlock don't match")
+}
+
+func TestGetStateUpdate(t *testing.T) {
+	// XXX: Use raw string literal to formal JSON.
+	body := "{\"block_hash\": \"0x6c9a1403d0d573ff7ce46b5ac0fba02b289bed60e26cacbc08f335e0a75fbbe\", \"new_root\": \"070d8a4d9843d6fb1a2b0564334133fc00a70b0ec9b9ccd791489c1c17a4a963\", \"old_root\": \"0025b10263a0ce4f984d313e8df3fae4dd473e6164b0c5e261a9af35892eafed\", \"state_diff\": {\"storage_diffs\": {\"0x5e4cac746d8709c776e901bcd5d23b2c2f696a0c850d47738bcbbddb1845ce5\": [], \"0x7091a8bbcb0f313b54af52c20f9b159258dca539d3caf0f7f007aae9b2252a9\": [{\"key\": \"0xf920571b9f85bdd92a867cfdc73319d0f8836f0e69e06e4c5566b6203f75cc\", \"value\": \"0x90aa7a9203bff78bfb24f0753c180a33d4bad95b1f4f510b36b00993815704\"}, {\"key\": \"0x1ccc09c8a19948e048de7add6929589945e25f22059c7345aaf7837188d8d05\", \"value\": \"0x1275efe83de1d12dce4bf30c44833afc7922b98f5cf9bcccc7cf941f4a3d814\"}], \"0x7d75ba4ac1ba55e1592375486d7937c1c55aab8fc3215de1429824d1c77da7a\": [{\"key\": \"0xf920571b9f85bdd92a867cfdc73319d0f8836f0e69e06e4c5566b6203f75cc\", \"value\": \"0x90aa7a9203bff78bfb24f0753c180a33d4bad95b1f4f510b36b00993815704\"}, {\"key\": \"0x1ccc09c8a19948e048de7add6929589945e25f22059c7345aaf7837188d8d05\", \"value\": \"0x5fcde101109bfe594ea7d47ec511213bf1a74d68840a1b36db7b649821a0e69\"}], \"0x4cf0f19cf486a762ea864077d368d8215f7d220b784a3360da52312e759ca66\": [{\"key\": \"0x37501df619c4fc4e96f6c0243f55e3abe7d1aca7db9af8f3740ba3696b3fdac\", \"value\": \"0x9\"}], \"0xec9b5d89811008b9a87145fd95f88827fbf0b8c273a7a7a51ad3523cdf9f55\": [{\"key\": \"0x5\", \"value\": \"0x0\"}, {\"key\": \"0x31d93a9cf0c6f9756c8323917128dd1fdf100f5fc3148f652204056ba88e26e\", \"value\": \"0x7e5\"}], \"0x7394cbe418daa16e42b87ba67372d4ab4a5df0b05c6e554d158458ce245bc10\": [{\"key\": \"0x7c68b6f31d543ce2476fc12f2411efd82f69d7d591505b7837ecff577125d87\", \"value\": \"0xa2a15d09519be00000\"}, {\"key\": \"0x1557182e4359a1f0c6301278e8f5b35a776ab58d39892581e357578fb287836\", \"value\": \"0x78118d2063b6ace88899ed8e4cfb5f3f\"}], \"0x741a8ac043f744786b0a61a7cb29238b5fba637484348a2ff30e9b1276ba41f\": [{\"key\": \"0x31d93a9cf0c6f9756c8323917128dd1fdf100f5fc3148f652204056ba88e26e\", \"value\": \"0x7c7\"}], \"0x11d0bd2a59aec732a27c3532decaa00db297c9d832f32eeefd35d27285302f2\": [{\"key\": \"0x5\", \"value\": \"0x64\"}]}, \"deployed_contracts\": [{\"address\": \"0x5e4cac746d8709c776e901bcd5d23b2c2f696a0c850d47738bcbbddb1845ce5\", \"contract_hash\": \"02864c45bd4ba3e66d8f7855adcadf07205c88f43806ffca664f1f624765207e\"}, {\"address\": \"0x7091a8bbcb0f313b54af52c20f9b159258dca539d3caf0f7f007aae9b2252a9\", \"contract_hash\": \"071c3c99f5cf76fc19945d4b8b7d34c7c5528f22730d56192b50c6bbfd338a64\"}, {\"address\": \"0x7d75ba4ac1ba55e1592375486d7937c1c55aab8fc3215de1429824d1c77da7a\", \"contract_hash\": \"071c3c99f5cf76fc19945d4b8b7d34c7c5528f22730d56192b50c6bbfd338a64\"}]}}\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig feeder.StateUpdateResponse
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	getStateUpdate, err := client.GetStateUpdate("hash", "")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, getStateUpdate, "State Update response don't match")
+}
+
+func TestGetCode(t *testing.T) {
+	body := "[\"one\",\"two\",\"three\"]\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig []string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	getCode, err := client.GetCode("hash", "", "latest")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, getCode, "GetCode response don't match")
+}
+
+func TestGetFullContract(t *testing.T) {
+	body := "[\"hash\"]\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig []interface{}
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	getFullContract, err := client.GetFullContract("hash", "hash", "")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, getFullContract, "GetFullContract response don't match")
+}
+
+func TestGetStorageAt(t *testing.T) {
+	body := "\"hash\"\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	getStorageAt, err := client.GetStorageAt("hash", "key", "hash", "")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, getStorageAt, "GetStorageAt response don't match")
+}
+
+func TestGetTransactionStatus(t *testing.T) {
+	body := "[\"TxnOk\"]\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig []interface{}
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	getTransactionStatus, err := client.GetTransactionStatus("hash", "")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, getTransactionStatus, "GetTransactionStatus response don't match")
+}
+
+func TestGetTransaction(t *testing.T) {
+	a := feeder.TransactionInfo{}
+	err := faker.FakeData(&a)
+	if err != nil {
+		t.Fatal()
+	}
+	body, err := json.Marshal(a)
+	if err != nil {
+		t.Fatal()
+	}
+	httpClient.DoReturns(generateResponse(string(body)), nil)
+	var cOrig feeder.TransactionInfo
+	err = json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	transactionInfo, err := client.GetTransaction("", "id")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, transactionInfo, "GetTransaction response don't match")
+}
+
+func TestGetTransactionReceipt(t *testing.T) {
+	a := feeder.TransactionReceipt{}
+	err := faker.FakeData(&a)
+	if err != nil {
+		t.Fatal()
+	}
+	body, err := json.Marshal(a)
+	if err != nil {
+		t.Fatal()
+	}
+	httpClient.DoReturns(generateResponse(string(body)), nil)
+	var cOrig feeder.TransactionReceipt
+	err = json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	transactionReceipt, err := client.GetTransactionReceipt("", "id")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, transactionReceipt, "GetTransactionReceipt response don't match")
+}
+
+func TestGetBlockHashById(t *testing.T) {
+	body := "\"hash\"\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	blockHash, err := client.GetBlockHashById("id")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, blockHash, "GetBlockHashById response don't match")
+}
+
+func TestGetBlockIdByHash(t *testing.T) {
+	body := "\"id\"\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	blockId, err := client.GetBlockIDByHash("hash")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, blockId, "GetBlockIdByHash response don't match")
+}
+
+func TestGetTransactionHashById(t *testing.T) {
+	body := "\"hash\"\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	transactionHash, err := client.GetTransactionHashByID("hash")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, transactionHash, "GetTransactionHashById response don't match")
+}
+
+func TestGetTransactionIdByHash(t *testing.T) {
+	body := "\"hash\"\n"
+	httpClient.DoReturns(generateResponse(body), nil)
+	var cOrig string
+	err := json.Unmarshal([]byte(body), &cOrig)
+	if err != nil {
+		t.Fatal()
+	}
+	transactionId, err := client.GetTransactionIDByHash("hash")
+	if err != nil {
+		t.Fatal()
+	}
+	assert.Equal(t, cOrig, transactionId, "GetTransactionIdByHash response don't match")
+}
diff --git a/pkg/rpc/client_test.go b/pkg/rpc/client_test.go
index 05b1729b..2ac1fe48 100644
--- a/pkg/rpc/client_test.go
+++ b/pkg/rpc/client_test.go
@@ -14,7 +14,7 @@ func newClient() (*Client, error) {
 func TestClient_GetBlockByHash(t *testing.T) {
 	c, err := newClient()
 	if err != nil {
-		t.Error("failed to initialise new client.")
+		t.Error("Failed to initialise a new client.")
 	}
 	defer c.Close()
 	blockHash := BlockHash("latest")
@@ -22,7 +22,7 @@ func TestClient_GetBlockByHash(t *testing.T) {
 	response, err := c.GetBlockByHash(
 		context.Background(), blockHash, requestedScope)
 	if err != nil {
-		t.Error(err)
+		t.Error("Failed to get block by hash.")
 	}
 	t.Log(response)
 }
@@ -30,7 +30,7 @@ func TestClient_GetBlockByHash(t *testing.T) {
 func TestClient_GetBlockByNumber(t *testing.T) {
 	c, err := newClient()
 	if err != nil {
-		t.Error("failed to initialise new client.")
+		t.Error("Failed to initialise new a client.")
 	}
 	defer c.Close()
 	blockHash := BlockHash("latest")
@@ -38,7 +38,7 @@ func TestClient_GetBlockByNumber(t *testing.T) {
 	response, err := c.GetBlockByNumber(
 		context.Background(), blockHash, requestedScope)
 	if err != nil {
-		t.Error(err)
+		t.Error("Failed to get block by number.")
 	}
 	t.Log(response)
 }
diff --git a/pkg/rpc/rpc_test.go b/pkg/rpc/rpc_test.go
index 83eb275a..119e6b2b 100644
--- a/pkg/rpc/rpc_test.go
+++ b/pkg/rpc/rpc_test.go
@@ -51,7 +51,7 @@ func TestRPCServer(t *testing.T) {
 	if err != nil {
 		fmt.Println(err)
 	}
-	fmt.Println("Successfully Opened users.json")
+	fmt.Println("Successfully opened rpc_tests.json")
 	// defer the closing of our jsonFile so that we can parse it later on
 	defer jsonFile.Close()
 	// read our opened jsonFile as a byte array.