Repository: NethermindEth/juno
Pull request #202 (2022-06-05 20:33:30)

[RPC] Full rpc implementation and refactoring
## Changes:
- Implementation of basic rpc methods
- Refactoring of our rpc package

## Types of changes

What types of changes does your code introduce?
_Put an `x` in the boxes that apply_

- [ ] Bugfix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation Update
- [ ] Code style update (formatting, renaming)
- [x] Refactoring (no functional changes, no api changes)
- [ ] Build related changes
- [ ] Other (please describe): 

## Testing
**Requires testing**

- [x] Yes
- [ ] No

**In case you checked yes, did you write tests??**

- [x] Yes
- [ ] No

codecov[bot]: # [Codecov](https://codecov.io/gh/NethermindEth/juno/pull/202?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) Report
> Merging [#202](https://codecov.io/gh/NethermindEth/juno/pull/202?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (b4271dc) into [main](https://codecov.io/gh/NethermindEth/juno/commit/28c73191590ac1ca32318d6a9cb586a643389a21?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) (28c7319) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##              main      #202   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           48        49    +1     
  Lines         4233      4224    -9     
=========================================
- Hits          4233      4224    -9     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `100.00% <100.00%> (ø)` | |

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/202?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth) | Coverage Δ | |
|---|---|---|
| [pkg/rpc/server.go](https://codecov.io/gh/NethermindEth/juno/pull/202/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL3JwYy9zZXJ2ZXIuZ28=) | `100.00% <ø> (ø)` | |
| [pkg/rpc/api.go](https://codecov.io/gh/NethermindEth/juno/pull/202/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL3JwYy9hcGkuZ28=) | `100.00% <100.00%> (ø)` | |
| [pkg/types/felt.go](https://codecov.io/gh/NethermindEth/juno/pull/202/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL3R5cGVzL2ZlbHQuZ28=) | `100.00% <100.00%> (ø)` | |
| [pkg/crypto/weierstrass/weierstrass.go](https://codecov.io/gh/NethermindEth/juno/pull/202/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth#diff-cGtnL2NyeXB0by93ZWllcnN0cmFzcy93ZWllcnN0cmFzcy5nbw==) | `100.00% <0.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/NethermindEth/juno/pull/202?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/202?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=NethermindEth). Last update [28c7319...b4271dc](https://codecov.io/gh/NethermindEth/juno/pull/202?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/internal/db/block/manager.go b/internal/db/block/manager.go
index 53d76be4..90ac272a 100644
--- a/internal/db/block/manager.go
+++ b/internal/db/block/manager.go
@@ -4,6 +4,7 @@ import (
 	"encoding/binary"
 
 	"github.com/NethermindEth/juno/internal/db"
+	"github.com/NethermindEth/juno/pkg/types"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -19,7 +20,7 @@ func NewManager(database db.Databaser) *Manager {
 
 // GetBlockByHash search the block with the given block hash. If the block does
 // not exist then returns nil. If any error happens, then panic.
-func (manager *Manager) GetBlockByHash(blockHash []byte) *Block {
+func (manager *Manager) GetBlockByHash(blockHash types.BlockHash) *types.Block {
 	// Build the hash key
 	hashKey := buildHashKey(blockHash)
 	// Search on the database
@@ -33,8 +34,7 @@ func (manager *Manager) GetBlockByHash(blockHash []byte) *Block {
 		return nil
 	}
 	// Unmarshal the data
-	block := &Block{}
-	err = proto.Unmarshal(rawResult, block)
+	block, err := unmarshalBlock(rawResult)
 	if err != nil {
 		panic(any(err))
 	}
@@ -43,7 +43,7 @@ func (manager *Manager) GetBlockByHash(blockHash []byte) *Block {
 
 // GetBlockByNumber search the block with the given block number. If the block
 // does not exist then returns nil. If any error happens, then panic.
-func (manager *Manager) GetBlockByNumber(blockNumber uint64) *Block {
+func (manager *Manager) GetBlockByNumber(blockNumber uint64) *types.Block {
 	// Build the number key
 	numberKey := buildNumberKey(blockNumber)
 	// Search for the hash key
@@ -67,8 +67,7 @@ func (manager *Manager) GetBlockByNumber(blockNumber uint64) *Block {
 		return nil
 	}
 	// Unmarshal the data
-	block := &Block{}
-	err = proto.Unmarshal(rawResult, block)
+	block, err := unmarshalBlock(rawResult)
 	if err != nil {
 		panic(any(err))
 	}
@@ -77,12 +76,12 @@ func (manager *Manager) GetBlockByNumber(blockNumber uint64) *Block {
 
 // PutBlock saves the given block with the given hash as key. If any error happens
 // then panic.
-func (manager *Manager) PutBlock(blockHash []byte, block *Block) {
+func (manager *Manager) PutBlock(blockHash types.BlockHash, block *types.Block) {
 	// Build the keys
 	hashKey := buildHashKey(blockHash)
 	numberKey := buildNumberKey(block.BlockNumber)
 	// Encode the block as []byte
-	rawValue, err := proto.Marshal(block)
+	rawValue, err := marshalBlock(block)
 	if err != nil {
 		panic(any(err))
 	}
@@ -103,8 +102,8 @@ func (manager *Manager) Close() {
 	manager.database.Close()
 }
 
-func buildHashKey(blockHash []byte) []byte {
-	return append([]byte("blockHash:"), blockHash...)
+func buildHashKey(blockHash types.BlockHash) []byte {
+	return append([]byte("blockHash:"), blockHash.Bytes()...)
 }
 
 func buildNumberKey(blockNumber uint64) []byte {
@@ -112,3 +111,64 @@ func buildNumberKey(blockNumber uint64) []byte {
 	binary.BigEndian.PutUint64(numberB, blockNumber)
 	return append([]byte("block_number:"), numberB...)
 }
+
+func marshalBlock(block *types.Block) ([]byte, error) {
+	protoBlock := Block{
+		Hash:             block.BlockHash.Bytes(),
+		BlockNumber:      block.BlockNumber,
+		ParentBlockHash:  block.ParentHash.Bytes(),
+		Status:           block.Status.String(),
+		SequencerAddress: block.Sequencer.Bytes(),
+		GlobalStateRoot:  block.NewRoot.Bytes(),
+		OldRoot:          block.OldRoot.Bytes(),
+		AcceptedTime:     block.AcceptedTime,
+		TimeStamp:        block.TimeStamp,
+		TxCount:          block.TxCount,
+		TxCommitment:     block.TxCommitment.Bytes(),
+		TxHashes:         marshalBlockTxHashes(block.TxHashes),
+		EventCount:       block.EventCount,
+		EventCommitment:  block.EventCommitment.Bytes(),
+	}
+	return proto.Marshal(&protoBlock)
+}
+
+func marshalBlockTxHashes(txHashes []types.TransactionHash) [][]byte {
+	out := make([][]byte, len(txHashes))
+	for i, txHash := range txHashes {
+		out[i] = txHash.Bytes()
+	}
+	return out
+}
+
+func unmarshalBlock(data []byte) (*types.Block, error) {
+	var protoBlock Block
+	err := proto.Unmarshal(data, &protoBlock)
+	if err != nil {
+		return nil, err
+	}
+	block := types.Block{
+		BlockHash:       types.BytesToBlockHash(protoBlock.Hash),
+		ParentHash:      types.BytesToBlockHash(protoBlock.ParentBlockHash),
+		BlockNumber:     protoBlock.BlockNumber,
+		Status:          types.StringToBlockStatus(protoBlock.Status),
+		Sequencer:       types.BytesToAddress(protoBlock.SequencerAddress),
+		NewRoot:         types.BytesToFelt(protoBlock.GlobalStateRoot),
+		OldRoot:         types.BytesToFelt(protoBlock.OldRoot),
+		AcceptedTime:    protoBlock.AcceptedTime,
+		TimeStamp:       protoBlock.TimeStamp,
+		TxCount:         protoBlock.TxCount,
+		TxCommitment:    types.BytesToFelt(protoBlock.TxCommitment),
+		TxHashes:        unmarshalBlockTxHashes(protoBlock.TxHashes),
+		EventCount:      protoBlock.EventCount,
+		EventCommitment: types.BytesToFelt(protoBlock.EventCommitment),
+	}
+	return &block, nil
+}
+
+func unmarshalBlockTxHashes(txHashes [][]byte) []types.TransactionHash {
+	out := make([]types.TransactionHash, len(txHashes))
+	for i, txHash := range txHashes {
+		out[i] = types.BytesToTransactionHash(txHash)
+	}
+	return out
+}
diff --git a/internal/db/transaction/manager.go b/internal/db/transaction/manager.go
index 84613e51..b513982e 100644
--- a/internal/db/transaction/manager.go
+++ b/internal/db/transaction/manager.go
@@ -1,8 +1,11 @@
 package transaction
 
 import (
+	"fmt"
+
 	"github.com/NethermindEth/juno/internal/db"
 	"github.com/NethermindEth/juno/internal/log"
+	"github.com/NethermindEth/juno/pkg/types"
 	"google.golang.org/protobuf/proto"
 )
 
@@ -21,9 +24,9 @@ func NewManager(database db.Databaser) *Manager {
 // PutTransaction stores new transactions in the database. This method does not
 // check if the key already exists. In the case, that the key already exists the
 // value is overwritten.
-func (m *Manager) PutTransaction(txHash []byte, tx *Transaction) {
+func (m *Manager) PutTransaction(txHash types.TransactionHash, tx types.IsTransaction) {
 	key := buildTxKey(txHash)
-	rawData, err := proto.Marshal(tx)
+	rawData, err := marshalTransaction(tx)
 	if err != nil {
 		// notest
 		log.Default.With("error", err).Panic("error marshalling Transaction")
@@ -37,7 +40,7 @@ func (m *Manager) PutTransaction(txHash []byte, tx *Transaction) {
 
 // GetTransaction searches in the database for the transaction associated with the
 // given key. If the key does not exist then returns nil.
-func (m *Manager) GetTransaction(txHash []byte) *Transaction {
+func (m *Manager) GetTransaction(txHash types.TransactionHash) types.IsTransaction {
 	key := buildTxKey(txHash)
 	rawData, err := m.database.Get(key)
 	if err != nil {
@@ -49,8 +52,7 @@ func (m *Manager) GetTransaction(txHash []byte) *Transaction {
 		// notest
 		return nil
 	}
-	tx := new(Transaction)
-	err = proto.Unmarshal(rawData, tx)
+	tx, err := unmarshalTransaction(rawData)
 	if err != nil {
 		// notest
 		log.Default.With("error", err).Panicf("unmarshalling error")
@@ -61,9 +63,9 @@ func (m *Manager) GetTransaction(txHash []byte) *Transaction {
 // PutReceipt stores  new transactions receipts in the database. This method
 // does not check if the key already exists. In the case, that the key already
 // exists the value is overwritten.
-func (m *Manager) PutReceipt(txHash []byte, txReceipt *TransactionReceipt) {
+func (m *Manager) PutReceipt(txHash types.TransactionHash, txReceipt *types.TransactionReceipt) {
 	key := buildReceiptKey(txHash)
-	rawData, err := proto.Marshal(txReceipt)
+	rawData, err := marshalTransactionReceipt(txReceipt)
 	if err != nil {
 		// notest
 		log.Default.With("error", err).Panic("error marshaling TransactionReceipt")
@@ -77,7 +79,7 @@ func (m *Manager) PutReceipt(txHash []byte, txReceipt *TransactionReceipt) {
 
 // GetReceipt searches in the database for the transaction receipt associated
 // with the given key. If the key does not exist then returns nil.
-func (m *Manager) GetReceipt(txHash []byte) *TransactionReceipt {
+func (m *Manager) GetReceipt(txHash types.TransactionHash) *types.TransactionReceipt {
 	key := buildReceiptKey(txHash)
 	rawData, err := m.database.Get(key)
 	if err != nil {
@@ -89,13 +91,12 @@ func (m *Manager) GetReceipt(txHash []byte) *TransactionReceipt {
 		// notest
 		return nil
 	}
-	tx := new(TransactionReceipt)
-	err = proto.Unmarshal(rawData, tx)
+	receipt, err := unmarshalTransactionReceipt(rawData)
 	if err != nil {
 		// notest
 		log.Default.With("error", err).Panicf("unmarshalling error")
 	}
-	return tx
+	return receipt
 }
 
 // Close closes the manager, specific the associated database.
@@ -103,10 +104,230 @@ func (m *Manager) Close() {
 	m.database.Close()
 }
 
-func buildTxKey(txHash []byte) []byte {
-	return append([]byte("transaction:"), txHash...)
+func buildTxKey(txHash types.TransactionHash) []byte {
+	key := make([]byte, 54)
+	copy(key[:12], "transaction:")
+	copy(key[12:], txHash.Bytes())
+	return key
+}
+
+func buildReceiptKey(txHash types.TransactionHash) []byte {
+	key := make([]byte, 54)
+	copy(key[:12], "receipt:")
+	copy(key[12:], txHash.Bytes())
+	return key
+}
+
+func marshalTransaction(transaction types.IsTransaction) ([]byte, error) {
+	protoTx := Transaction{
+		Hash: transaction.GetHash().Bytes(),
+	}
+	switch tx := transaction.(type) {
+	case *types.TransactionDeploy:
+		deploy := Deploy{
+			ContractAddressSalt: tx.ContractAddress.Bytes(),
+			ConstructorCallData: marshalFelts(tx.ConstructorCallData),
+		}
+		protoTx.Tx = &Transaction_Deploy{&deploy}
+	case *types.TransactionInvoke:
+		invoke := InvokeFunction{
+			ContractAddress:    tx.ContractAddress.Bytes(),
+			EntryPointSelector: tx.EntryPointSelector.Bytes(),
+			CallData:           marshalFelts(tx.CallData),
+			Signature:          marshalFelts(tx.Signature),
+			MaxFee:             tx.MaxFee.Bytes(),
+		}
+		protoTx.Tx = &Transaction_Invoke{&invoke}
+	}
+	return proto.Marshal(&protoTx)
+}
+
+func unmarshalTransaction(b []byte) (types.IsTransaction, error) {
+	var protoTx Transaction
+	err := proto.Unmarshal(b, &protoTx)
+	if err != nil {
+		return nil, err
+	}
+	if tx := protoTx.GetInvoke(); tx != nil {
+		out := types.TransactionInvoke{
+			Hash:               types.BytesToTransactionHash(protoTx.Hash),
+			ContractAddress:    types.BytesToAddress(tx.ContractAddress),
+			EntryPointSelector: types.BytesToFelt(tx.EntryPointSelector),
+			CallData:           unmarshalFelts(tx.CallData),
+			Signature:          unmarshalFelts(tx.Signature),
+			MaxFee:             types.BytesToFelt(tx.MaxFee),
+		}
+		return &out, nil
+	}
+	if tx := protoTx.GetDeploy(); tx != nil {
+		out := types.TransactionDeploy{
+			Hash:                types.BytesToTransactionHash(protoTx.Hash),
+			ContractAddress:     types.BytesToAddress(tx.ContractAddressSalt),
+			ConstructorCallData: unmarshalFelts(tx.ConstructorCallData),
+		}
+		return &out, nil
+	}
+	// notest
+	return nil, fmt.Errorf("unexpected transaction type")
+}
+
+func marshalTransactionReceipt(receipt *types.TransactionReceipt) ([]byte, error) {
+	protoReceipt := TransactionReceipt{
+		TxHash:          receipt.TxHash.Bytes(),
+		ActualFee:       receipt.ActualFee.Bytes(),
+		Status:          marshalTransactionStatus(receipt.Status),
+		StatusData:      receipt.StatusData,
+		L1OriginMessage: marshalMessageL1ToL2(receipt.L1OriginMessage),
+	}
+	if receipt.MessagesSent != nil {
+		protoReceipt.MessagesSent = make([]*MessageToL1, len(receipt.MessagesSent))
+		for i, message := range receipt.MessagesSent {
+			protoReceipt.MessagesSent[i] = marshalMessageL2ToL1(&message)
+		}
+	}
+	if receipt.Events != nil {
+		protoReceipt.Events = make([]*Event, len(receipt.Events))
+		for i, event := range receipt.Events {
+			protoReceipt.Events[i] = marshalEvent(&event)
+		}
+	}
+	return proto.Marshal(&protoReceipt)
+}
+
+func unmarshalTransactionReceipt(b []byte) (*types.TransactionReceipt, error) {
+	var protoReceipt TransactionReceipt
+	err := proto.Unmarshal(b, &protoReceipt)
+	if err != nil {
+		return nil, err
+	}
+	receipt := &types.TransactionReceipt{
+		TxHash:          types.BytesToTransactionHash(protoReceipt.TxHash),
+		ActualFee:       types.BytesToFelt(protoReceipt.ActualFee),
+		Status:          unmarshalTransactionStatus(protoReceipt.Status),
+		StatusData:      protoReceipt.StatusData,
+		L1OriginMessage: unmarshalMessageL1ToL2(protoReceipt.L1OriginMessage),
+		Events:          nil,
+	}
+	if protoReceipt.MessagesSent != nil {
+		receipt.MessagesSent = make([]types.MessageL2ToL1, len(protoReceipt.MessagesSent))
+		for i, message := range protoReceipt.MessagesSent {
+			receipt.MessagesSent[i] = unmarshalMessageL2ToL1(message)
+		}
+	}
+	if protoReceipt.Events != nil {
+		receipt.Events = make([]types.Event, len(protoReceipt.Events))
+		for i, event := range protoReceipt.Events {
+			receipt.Events[i] = unmarshalEvent(event)
+		}
+	}
+	return receipt, nil
+}
+
+func marshalTransactionStatus(status types.TransactionStatus) Status {
+	// notest
+	switch status {
+	case types.TxStatusUnknown:
+		return Status_UNKNOWN
+	case types.TxStatusReceived:
+		return Status_RECEIVED
+	case types.TxStatusPending:
+		return Status_PENDING
+	case types.TxStatusAcceptedOnL2:
+		return Status_ACCEPTED_ON_L2
+	case types.TxStatusAcceptedOnL1:
+		return Status_ACCEPTED_ON_L1
+	case types.TxStatusRejected:
+		return Status_REJECTED
+	default:
+		return Status_UNKNOWN
+	}
+}
+
+func unmarshalTransactionStatus(status Status) types.TransactionStatus {
+	// notest
+	switch status {
+	case Status_UNKNOWN:
+		return types.TxStatusUnknown
+	case Status_RECEIVED:
+		return types.TxStatusReceived
+	case Status_PENDING:
+		return types.TxStatusPending
+	case Status_ACCEPTED_ON_L2:
+		return types.TxStatusAcceptedOnL2
+	case Status_ACCEPTED_ON_L1:
+		return types.TxStatusAcceptedOnL1
+	case Status_REJECTED:
+		return types.TxStatusRejected
+	default:
+		return types.TxStatusUnknown
+	}
+}
+
+func marshalMessageL2ToL1(message *types.MessageL2ToL1) *MessageToL1 {
+	return &MessageToL1{
+		ToAddress: message.ToAddress.Bytes(),
+		Payload:   marshalFelts(message.Payload),
+	}
+}
+
+func unmarshalMessageL2ToL1(message *MessageToL1) types.MessageL2ToL1 {
+	return types.MessageL2ToL1{
+		ToAddress: types.BytesToEthAddress(message.ToAddress),
+		Payload:   unmarshalFelts(message.Payload),
+	}
+}
+
+func marshalMessageL1ToL2(message *types.MessageL1ToL2) *MessageToL2 {
+	if message == nil {
+		return nil
+	}
+	return &MessageToL2{
+		FromAddress: message.FromAddress.Bytes(),
+		Payload:     marshalFelts(message.Payload),
+	}
+}
+
+func unmarshalMessageL1ToL2(message *MessageToL2) *types.MessageL1ToL2 {
+	if message == nil {
+		return nil
+	}
+	return &types.MessageL1ToL2{
+		FromAddress: types.BytesToEthAddress(message.FromAddress),
+		Payload:     unmarshalFelts(message.Payload),
+	}
+}
+
+func marshalEvent(event *types.Event) *Event {
+	return &Event{
+		FromAddress: event.FromAddress.Bytes(),
+		Keys:        marshalFelts(event.Keys),
+		Data:        marshalFelts(event.Data),
+	}
 }
 
-func buildReceiptKey(txHash []byte) []byte {
-	return append([]byte("receipt:"), txHash...)
+func unmarshalEvent(event *Event) types.Event {
+	return types.Event{
+		FromAddress: types.BytesToAddress(event.FromAddress),
+		Keys:        unmarshalFelts(event.Keys),
+		Data:        unmarshalFelts(event.Data),
+	}
+}
+
+func marshalFelts(felts []types.Felt) [][]byte {
+	out := make([][]byte, len(felts))
+	for i, felt := range felts {
+		out[i] = felt.Bytes()
+	}
+	return out
+}
+
+func unmarshalFelts(bs [][]byte) []types.Felt {
+	if bs == nil {
+		return nil
+	}
+	out := make([]types.Felt, len(bs))
+	for i, b := range bs {
+		out[i] = types.BytesToFelt(b)
+	}
+	return out
 }
diff --git a/internal/db/transaction/transaction.pb.go b/internal/db/transaction/transaction.pb.go
index e66802a1..250c003b 100644
--- a/internal/db/transaction/transaction.pb.go
+++ b/internal/db/transaction/transaction.pb.go
@@ -455,7 +455,7 @@ type MessageToL2 struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	FromAddress string   `protobuf:"bytes,1,opt,name=fromAddress,proto3" json:"fromAddress,omitempty"`
+	FromAddress []byte   `protobuf:"bytes,1,opt,name=fromAddress,proto3" json:"fromAddress,omitempty"`
 	Payload     [][]byte `protobuf:"bytes,2,rep,name=payload,proto3" json:"payload,omitempty"`
 }
 
@@ -491,11 +491,11 @@ func (*MessageToL2) Descriptor() ([]byte, []int) {
 	return file_transaction_proto_rawDescGZIP(), []int{5}
 }
 
-func (x *MessageToL2) GetFromAddress() string {
+func (x *MessageToL2) GetFromAddress() []byte {
 	if x != nil {
 		return x.FromAddress
 	}
-	return ""
+	return nil
 }
 
 func (x *MessageToL2) GetPayload() [][]byte {
@@ -622,7 +622,7 @@ var file_transaction_proto_rawDesc = []byte{
 	0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x70,
 	0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x49, 0x0a, 0x0b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67,
 	0x65, 0x54, 0x6f, 0x4c, 0x32, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72, 0x6f, 0x6d, 0x41, 0x64, 0x64,
-	0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d,
+	0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x66, 0x72, 0x6f, 0x6d,
 	0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f,
 	0x61, 0x64, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61,
 	0x64, 0x22, 0x51, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x66, 0x72,
diff --git a/internal/db/transaction/transaction.proto b/internal/db/transaction/transaction.proto
index 8b4c83c8..5b340460 100644
--- a/internal/db/transaction/transaction.proto
+++ b/internal/db/transaction/transaction.proto
@@ -48,7 +48,7 @@ message MessageToL1 {
 }
 
 message MessageToL2 {
-  string fromAddress = 1;
+  bytes fromAddress = 1;
   repeated bytes payload = 2;
 }
 
diff --git a/internal/services/block.go b/internal/services/block.go
index 2a683759..695298c5 100644
--- a/internal/services/block.go
+++ b/internal/services/block.go
@@ -7,6 +7,7 @@ import (
 	"github.com/NethermindEth/juno/internal/db"
 	"github.com/NethermindEth/juno/internal/db/block"
 	"github.com/NethermindEth/juno/internal/log"
+	"github.com/NethermindEth/juno/pkg/types"
 )
 
 // BlockService is a service to manage the block database. Before
@@ -49,7 +50,7 @@ func (s *blockService) Run() error {
 func (s *blockService) setDefaults() {
 	if s.manager == nil {
 		// notest
-		database := db.NewKeyValueDb(config.DataDir+"/block", 0)
+		database := db.NewKeyValueDb(config.Dir+"/block", 0)
 		s.manager = block.NewManager(database)
 	}
 }
@@ -63,7 +64,7 @@ func (s *blockService) Close(ctx context.Context) {
 
 // GetBlockByHash searches for the block associated with the given block hash.
 // If the block does not exist on the database, then returns nil.
-func (s *blockService) GetBlockByHash(blockHash []byte) *block.Block {
+func (s *blockService) GetBlockByHash(blockHash types.BlockHash) *types.Block {
 	s.AddProcess()
 	defer s.DoneProcess()
 
@@ -76,7 +77,7 @@ func (s *blockService) GetBlockByHash(blockHash []byte) *block.Block {
 
 // GetBlockByNumber searches for the block associated with the given block
 // number. If the block does not exist on the database, then returns nil.
-func (s *blockService) GetBlockByNumber(blockNumber uint64) *block.Block {
+func (s *blockService) GetBlockByNumber(blockNumber uint64) *types.Block {
 	s.AddProcess()
 	defer s.DoneProcess()
 
@@ -90,7 +91,7 @@ func (s *blockService) GetBlockByNumber(blockNumber uint64) *block.Block {
 // StoreBlock stores the given block into the database. The key used to map the
 // block it's the hash of the block. If the database already has a block with
 // the same key, then the value is overwritten.
-func (s *blockService) StoreBlock(blockHash []byte, block *block.Block) {
+func (s *blockService) StoreBlock(blockHash types.BlockHash, block *types.Block) {
 	s.AddProcess()
 	defer s.DoneProcess()
 
diff --git a/internal/services/transactions.go b/internal/services/transactions.go
index 5862391b..2f2ea6d9 100644
--- a/internal/services/transactions.go
+++ b/internal/services/transactions.go
@@ -3,6 +3,8 @@ package services
 import (
 	"context"
 
+	"github.com/NethermindEth/juno/pkg/types"
+
 	"github.com/NethermindEth/juno/internal/config"
 	"github.com/NethermindEth/juno/internal/db"
 	"github.com/NethermindEth/juno/internal/db/transaction"
@@ -49,7 +51,7 @@ func (s *transactionService) Run() error {
 func (s *transactionService) setDefaults() {
 	if s.manager == nil {
 		// notest
-		database := db.NewKeyValueDb(config.DataDir+"/transaction", 0)
+		database := db.NewKeyValueDb(config.Dir+"/transaction", 0)
 		s.manager = transaction.NewManager(database)
 	}
 }
@@ -64,7 +66,7 @@ func (s *transactionService) Close(ctx context.Context) {
 // GetTransaction searches for the transaction associated with the given
 // transaction hash. If the transaction does not exist on the database, then
 // returns nil.
-func (s *transactionService) GetTransaction(txHash []byte) *transaction.Transaction {
+func (s *transactionService) GetTransaction(txHash types.TransactionHash) types.IsTransaction {
 	s.AddProcess()
 	defer s.DoneProcess()
 
@@ -78,7 +80,7 @@ func (s *transactionService) GetTransaction(txHash []byte) *transaction.Transact
 // StoreTransaction stores the given transaction into the database. The key used
 // to map the transaction it's the hash of the transaction. If the database
 // already has a transaction with the same key, then the value is overwritten.
-func (s *transactionService) StoreTransaction(txHash []byte, tx *transaction.Transaction) {
+func (s *transactionService) StoreTransaction(txHash types.TransactionHash, tx types.IsTransaction) {
 	s.AddProcess()
 	defer s.DoneProcess()
 
@@ -90,9 +92,9 @@ func (s *transactionService) StoreTransaction(txHash []byte, tx *transaction.Tra
 }
 
 // GetReceipt searches for the transaction receipt associated with the given
-// transaction hash. If the transaction does not exists on the database, then
+// transaction hash. If the transaction does not exist on the database, then
 // returns nil.
-func (s *transactionService) GetReceipt(txHash []byte) *transaction.TransactionReceipt {
+func (s *transactionService) GetReceipt(txHash types.TransactionHash) *types.TransactionReceipt {
 	s.AddProcess()
 	defer s.DoneProcess()
 
@@ -103,7 +105,7 @@ func (s *transactionService) GetReceipt(txHash []byte) *transaction.TransactionR
 
 // StoreReceipt stores the given transaction receipt into the database. If the
 // database already has a receipt with the same key, the value is overwritten.
-func (s *transactionService) StoreReceipt(txHash []byte, receipt *transaction.TransactionReceipt) {
+func (s *transactionService) StoreReceipt(txHash types.TransactionHash, receipt *types.TransactionReceipt) {
 	s.AddProcess()
 	defer s.DoneProcess()
 
diff --git a/pkg/common/hex.go b/pkg/common/hex.go
new file mode 100644
index 00000000..b302964e
--- /dev/null
+++ b/pkg/common/hex.go
@@ -0,0 +1,18 @@
+package common
+
+func isHexCharacter(c byte) bool {
+	return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
+}
+
+func IsHex(s string) bool {
+	if has0xPrefix(s) {
+		s = s[2:]
+	}
+
+	for i := 0; i < len(s); i++ {
+		if !isHexCharacter(s[i]) {
+			return false
+		}
+	}
+	return true
+}
diff --git a/pkg/rpc/api.go b/pkg/rpc/api.go
new file mode 100644
index 00000000..fd2c7965
--- /dev/null
+++ b/pkg/rpc/api.go
@@ -0,0 +1,298 @@
+package rpc
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"github.com/NethermindEth/juno/internal/services"
+	"github.com/NethermindEth/juno/pkg/types"
+
+	"github.com/NethermindEth/juno/internal/log"
+)
+
+// Echo replies with the same message.
+func (HandlerRPC) Echo(c context.Context, message string) (string, error) {
+	return message, nil
+}
+
+// EchoErr replies with the same message as an error.
+func (HandlerRPC) EchoErr(c context.Context, message string) (string, error) {
+	return "", fmt.Errorf("%s", message)
+}
+
+// StarknetCall represents the handler of "starknet_call" rpc call.
+func (HandlerRPC) StarknetCall(
+	c context.Context, request FunctionCall, blockHash BlockHashOrTag,
+) (ResultCall, error) {
+	return []string{"Response", "of", "starknet_call"}, nil
+}
+
+func getBlockByTag(ctx context.Context, blockTag BlockTag, scope RequestedScope) (*BlockResponse, error) {
+	// notest
+	// TODO: Implement get block by tag
+	return &BlockResponse{}, nil
+}
+
+func getBlockByHash(ctx context.Context, blockHash types.BlockHash, scope RequestedScope) (*BlockResponse, error) {
+	log.Default.With("blockHash", blockHash, "scope", scope).Info("StarknetGetBlockByHash")
+	dbBlock := services.BlockService.GetBlockByHash(blockHash)
+	if dbBlock == nil {
+		// notest
+		// TODO: Send custom error for not found. Maybe sent InvalidBlockHash?
+		return nil, errors.New("block not found")
+	}
+	response := NewBlockResponse(dbBlock, scope)
+	return response, nil
+}
+
+func getBlockByHashOrTag(ctx context.Context, blockHashOrTag BlockHashOrTag, scope RequestedScope) (*BlockResponse, error) {
+	if blockHashOrTag.Hash != nil {
+		return getBlockByHash(ctx, *blockHashOrTag.Hash, scope)
+	}
+	// notest
+	if blockHashOrTag.Tag != nil {
+		return getBlockByTag(ctx, *blockHashOrTag.Tag, scope)
+	}
+	return nil, ErrInvalidRequest()
+}
+
+// StarknetGetBlockByHash represent the handler for getting a block by
+// its hash.
+func (HandlerRPC) StarknetGetBlockByHash(ctx context.Context, blockHashOrTag BlockHashOrTag) (*BlockResponse, error) {
+	// notest
+	return getBlockByHashOrTag(ctx, blockHashOrTag, ScopeTxnHash)
+}
+
+// StarknetGetBlockByHashOpt represent the handler for getting a block
+// by its hash.
+func (HandlerRPC) StarknetGetBlockByHashOpt(ctx context.Context, blockHashOrTag BlockHashOrTag, scope RequestedScope) (*BlockResponse, error) {
+	return getBlockByHashOrTag(ctx, blockHashOrTag, scope)
+}
+
+func getBlockByNumber(ctx context.Context, blockNumber uint64, scope RequestedScope) (*BlockResponse, error) {
+	log.Default.With("blockNumber", blockNumber, "scope", scope).Info("StarknetGetBlockNyNumber")
+	dbBlock := services.BlockService.GetBlockByNumber(blockNumber)
+	if dbBlock == nil {
+		// notest
+		return nil, errors.New("block not found")
+	}
+	response := NewBlockResponse(dbBlock, scope)
+	return response, nil
+}
+
+func getBlockByNumberOrTag(ctx context.Context, blockNumberOrTag BlockNumberOrTag, scope RequestedScope) (*BlockResponse, error) {
+	if number := blockNumberOrTag.Number; number != nil {
+		return getBlockByNumber(ctx, *number, scope)
+	}
+	// notest
+	if tag := blockNumberOrTag.Tag; tag != nil {
+		return getBlockByTag(ctx, *tag, scope)
+	}
+	// TODO: Send bad request error
+	return nil, errors.New("bad request")
+}
+
+// type bNumber string `json:"int,int,omitempty"`
+
+// StarknetGetBlockByNumber represent the handler for getting a block by
+// its number.
+func (HandlerRPC) StarknetGetBlockByNumber(ctx context.Context, blockNumberOrTag BlockNumberOrTag) (*BlockResponse, error) {
+	// notest
+	return getBlockByNumberOrTag(ctx, blockNumberOrTag, ScopeTxnHash)
+}
+
+// StarknetGetBlockByNumberOpt represent the handler for getting a block
+// by its number.
+func (HandlerRPC) StarknetGetBlockByNumberOpt(ctx context.Context, blockNumberOrTag BlockNumberOrTag, scope RequestedScope) (*BlockResponse, error) {
+	return getBlockByNumberOrTag(ctx, blockNumberOrTag, scope)
+}
+
+// StarknetGetBlockTransactionCountByHash represent the handler for
+// getting block transaction count by the blocks hash.
+func (HandlerRPC) StarknetGetBlockTransactionCountByHash(
+	c context.Context, blockHash BlockHashOrTag,
+) (BlockTransactionCount, error) {
+	return BlockTransactionCount{}, nil
+}
+
+// StarknetGetBlockTransactionCountByNumber Get the number of
+// transactions in a block given a block number (height).
+func (HandlerRPC) StarknetGetBlockTransactionCountByNumber(
+	c context.Context, blockNumber interface{},
+) (BlockTransactionCount, error) {
+	return BlockTransactionCount{}, nil
+}
+
+// StarknetGetStateUpdateByHash represent the handler for getting the
+// information about the result of executing the requested block.
+func (HandlerRPC) StarknetGetStateUpdateByHash(
+	c context.Context, blockHash BlockHashOrTag,
+) (StateUpdate, error) {
+	return StateUpdate{}, nil
+}
+
+// StarknetGetStorageAt Get the value of the storage at the given
+// address and key.
+func (HandlerRPC) StarknetGetStorageAt(
+	c context.Context,
+	contractAddress Address,
+	key Felt,
+	blockHash BlockHashOrTag,
+) (Felt, error) {
+	if hash := blockHash.Hash; hash != nil {
+		block := services.BlockService.GetBlockByHash(*blockHash.Hash)
+		if block == nil {
+			// notest
+			return "", fmt.Errorf("block not found")
+		}
+		storage := services.StateService.GetStorage(string(contractAddress), block.BlockNumber)
+		if storage == nil {
+			// notest
+			return "", fmt.Errorf("storage not found")
+		}
+		return Felt(storage.Storage[string(key)]), nil
+	}
+	// notest
+	if tag := blockHash.Tag; tag != nil {
+		// TODO: Get by tag
+		return "", fmt.Errorf("unimplmented search by block tag")
+	}
+	return "", fmt.Errorf("invalid block hash or tag")
+}
+
+// StarknetGetTransactionByHash Get the details and status of a
+// submitted transaction.
+func (HandlerRPC) StarknetGetTransactionByHash(
+	c context.Context, transactionHash types.TransactionHash,
+) (*Txn, error) {
+	tx := services.TransactionService.GetTransaction(transactionHash)
+	if tx == nil {
+		// notest
+		// TODO: return not found error
+		return &Txn{}, nil
+	}
+	return NewTxn(tx), nil
+}
+
+// StarknetGetTransactionByBlockHashAndIndex Get the details of the
+// transaction given by the identified block and index in that block. If
+// no transaction is found, a null value is returned.
+func (HandlerRPC) StarknetGetTransactionByBlockHashAndIndex(c context.Context, blockHashOrTag BlockHashOrTag, index int) (*Txn, error) {
+	if blockHash := blockHashOrTag.Hash; blockHash != nil {
+		block := services.BlockService.GetBlockByHash(*blockHash)
+		if index < 0 || len(block.TxHashes) <= index {
+			// notest
+			return nil, fmt.Errorf("invalid index %d", index)
+		}
+		txHash := block.TxHashes[index]
+		txn := services.TransactionService.GetTransaction(txHash)
+		return NewTxn(txn), nil
+	}
+	// notest
+	if tag := blockHashOrTag.Tag; tag != nil {
+		// TODO: search block by tag
+		return &Txn{}, nil
+	}
+	// TODO: return invalid param error
+	return nil, errors.New("invalid blockHashOrtTag param")
+}
+
+// StarknetGetTransactionByBlockNumberAndIndex Get the details of the
+// transaction given by the identified block and index in that block. If
+// no transaction is found, null is returned.
+func (HandlerRPC) StarknetGetTransactionByBlockNumberAndIndex(ctx context.Context, blockNumberOrTag BlockNumberOrTag, index int) (*Txn, error) {
+	if blockNumber := blockNumberOrTag.Number; blockNumber != nil {
+		block := services.BlockService.GetBlockByNumber(*blockNumber)
+		if index < 0 || len(block.TxHashes) <= index {
+			// notest
+			return nil, fmt.Errorf("invalid index %d", index)
+		}
+		txHash := block.TxHashes[index]
+		txn := services.TransactionService.GetTransaction(txHash)
+		return NewTxn(txn), nil
+	}
+	// notest
+	if tag := blockNumberOrTag.Tag; tag != nil {
+		// TODO: search block by tag
+		return &Txn{}, nil
+	}
+	// TODO: return invalid param error
+	return nil, errors.New("invalid blockHashOrtTag param")
+}
+
+// StarknetGetTransactionReceipt Get the transaction receipt by the
+// transaction hash.
+func (HandlerRPC) StarknetGetTransactionReceipt(
+	c context.Context, transactionHash TxnHash,
+) (TxnReceipt, error) {
+	return TxnReceipt{}, nil
+}
+
+// StarknetGetCode Get the code of a specific contract
+func (HandlerRPC) StarknetGetCode(
+	c context.Context, contractAddress types.Address,
+) (CodeResult, error) {
+	abi := services.AbiService.GetAbi(contractAddress.Hex())
+	if abi == nil {
+		// notest
+		return CodeResult{}, fmt.Errorf("abi not found")
+	}
+	code := services.StateService.GetCode(contractAddress.Bytes())
+	if code == nil {
+		// notest
+		return CodeResult{}, fmt.Errorf("code not found")
+	}
+	marshalledAbi, err := json.Marshal(abi)
+	if err != nil {
+		return CodeResult{}, err
+	}
+	bytecode := make([]Felt, len(code.Code))
+	for i, b := range code.Code {
+		bytecode[i] = Felt(types.BytesToFelt(b).Hex())
+	}
+	return CodeResult{Abi: string(marshalledAbi), Bytecode: bytecode}, nil
+}
+
+// StarknetBlockNumber Get the most recent accepted block number
+func (HandlerRPC) StarknetBlockNumber(c context.Context) (BlockNumber, error) {
+	return 0, nil
+}
+
+// StarknetChainId Return the currently configured StarkNet chain id
+func (HandlerRPC) StarknetChainId(c context.Context) (ChainID, error) {
+	return "Here the ChainID", nil
+}
+
+// StarknetPendingTransactions Returns the transactions in the
+// transaction pool, recognized by this sequencer.
+func (HandlerRPC) StarknetPendingTransactions(
+	c context.Context,
+) ([]Txn, error) {
+	return nil, nil
+}
+
+// StarknetProtocolVersion Returns the current starknet protocol version
+// identifier, as supported by this sequencer.
+func (HandlerRPC) StarknetProtocolVersion(
+	c context.Context,
+) (ProtocolVersion, error) {
+	return "Here the Protocol Version", nil
+}
+
+// StarknetSyncing Returns an object about the sync status, or false if
+// the node is not syncing.
+func (HandlerRPC) StarknetSyncing(
+	c context.Context,
+) (SyncStatus, error) {
+	return SyncStatus{}, nil
+}
+
+// StarknetGetEvents Returns all event objects matching the conditions
+// in the provided filter.
+func (HandlerRPC) StarknetGetEvents(
+	c context.Context, r EventRequest,
+) (EventResponse, error) {
+	return EventResponse{}, nil
+}
diff --git a/pkg/rpc/handle.go b/pkg/rpc/handle.go
index 514b4a82..b3a1a84d 100644
--- a/pkg/rpc/handle.go
+++ b/pkg/rpc/handle.go
@@ -350,17 +350,14 @@ func (h *HandlerJsonRpc) InvokeMethod(
 	// (Methods should always return and report an error).
 	errPos := checkReturn(fn.Type)
 	// Call the function
-	resFromCall, err := callFunc(
-		c, r.Method, args, fn.Func, structToCall, hasContext, errPos)
-	if err != nil {
-		// notest
-		log.Default.With(
-			"Method", r.Method, "Params", r.Params,
-		).Error("Internal error occurred while calling the function.")
-		res.Error = ErrInternal()
+	if resFromCall, err := callFunc(c, r.Method, args, fn.Func, structToCall, hasContext, errPos); err != nil {
+		log.Default.With("Method", r.Method).Info("Request returned error.")
+		res.Error = &Error{Message: err.Error()}
 		res.Result = nil
+	} else {
+		log.Default.With("Method", r.Method).Info("Request successful.")
+		res.Error = nil
+		res.Result = resFromCall
 	}
-	log.Default.With("Method", r.Method).Info("Request successful.")
-	res.Result = resFromCall
 	return res
 }
diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go
index cd506f1c..e85d0009 100644
--- a/pkg/rpc/server.go
+++ b/pkg/rpc/server.go
@@ -60,175 +60,3 @@ func (s *Server) Close(ctx context.Context) {
 	default:
 	}
 }
-
-// Echo replies with the same message.
-func (HandlerRPC) Echo(c context.Context, message string) (string, error) {
-	return message, nil
-}
-
-// StarknetCall represents the handler of "starknet_call" rpc call.
-func (HandlerRPC) StarknetCall(
-	c context.Context, request FunctionCall, blockHash BlockHashOrTag,
-) (ResultCall, error) {
-	return []string{"Response", "of", "starknet_call"}, nil
-}
-
-// StarknetGetBlockByHash represent the handler for getting a block by
-// its hash.
-func (HandlerRPC) StarknetGetBlockByHash(
-	c context.Context, blockHash BlockHashOrTag,
-) (BlockResponse, error) {
-	// TODO See if is possible to support overhead without another method
-	return BlockResponse{BlockHash: string(blockHash)}, nil
-}
-
-// StarknetGetBlockByHashOpt represent the handler for getting a block
-// by its hash.
-func (HandlerRPC) StarknetGetBlockByHashOpt(
-	c context.Context, blockHash BlockHashOrTag, requestedScope RequestedScope,
-) (BlockResponse, error) {
-	// TODO See if is possible to support overhead without another method
-	return BlockResponse{
-		BlockHash:  string(blockHash),
-		ParentHash: string(requestedScope),
-	}, nil
-}
-
-// type bNumber string `json:"int,int,omitempty"`
-
-// StarknetGetBlockByNumber represent the handler for getting a block by
-// its number.
-func (HandlerRPC) StarknetGetBlockByNumber(
-	c context.Context, blockNumber interface{},
-) (BlockResponse, error) {
-	// TODO See if is possible to support overhead without another method
-	log.Default.With("Block Number", blockNumber).Info("Calling StarknetGetBlockByNumber")
-	return BlockResponse{}, nil
-}
-
-// StarknetGetBlockByNumberOpt represent the handler for getting a block
-// by its number.
-func (HandlerRPC) StarknetGetBlockByNumberOpt(
-	c context.Context, blockNumber interface{}, requestedScope RequestedScope,
-) (BlockResponse, error) {
-	// TODO See if is possible to support overhead without another method
-	return BlockResponse{}, nil
-}
-
-// StarknetGetBlockTransactionCountByHash represent the handler for
-// getting block transaction count by the blocks hash.
-func (HandlerRPC) StarknetGetBlockTransactionCountByHash(
-	c context.Context, blockHash BlockHashOrTag,
-) (BlockTransactionCount, error) {
-	return BlockTransactionCount{}, nil
-}
-
-// StarknetGetBlockTransactionCountByNumber Get the number of
-// transactions in a block given a block number (height).
-func (HandlerRPC) StarknetGetBlockTransactionCountByNumber(
-	c context.Context, blockNumber interface{},
-) (BlockTransactionCount, error) {
-	return BlockTransactionCount{}, nil
-}
-
-// StarknetGetStateUpdateByHash represent the handler for getting the
-// information about the result of executing the requested block.
-func (HandlerRPC) StarknetGetStateUpdateByHash(
-	c context.Context, blockHash BlockHashOrTag,
-) (StateUpdate, error) {
-	return StateUpdate{}, nil
-}
-
-// StarknetGetStorageAt Get the value of the storage at the given
-// address and key.
-func (HandlerRPC) StarknetGetStorageAt(
-	c context.Context,
-	contractAddress Address,
-	key Felt,
-	blockHash BlockHashOrTag,
-) (Felt, error) {
-	return "Storage", nil
-}
-
-// StarknetGetTransactionByHash Get the details and status of a
-// submitted transaction.
-func (HandlerRPC) StarknetGetTransactionByHash(
-	c context.Context, transactionHash TxnHash,
-) (Txn, error) {
-	return Txn{}, nil
-}
-
-// StarknetGetTransactionByBlockHashAndIndex Get the details of the
-// transaction given by the identified block and index in that block. If
-// no transaction is found, a null value is returned.
-func (HandlerRPC) StarknetGetTransactionByBlockHashAndIndex(
-	c context.Context, blockHash BlockHashOrTag, index uint64,
-) (Txn, error) {
-	return Txn{}, nil
-}
-
-// StarknetGetTransactionByBlockNumberAndIndex Get the details of the
-// transaction given by the identified block and index in that block. If
-// no transaction is found, null is returned.
-func (HandlerRPC) StarknetGetTransactionByBlockNumberAndIndex(
-	c context.Context, blockNumber BlockNumberOrTag, index uint64,
-) (Txn, error) {
-	return Txn{}, nil
-}
-
-// StarknetGetTransactionReceipt Get the transaction receipt by the
-// transaction hash.
-func (HandlerRPC) StarknetGetTransactionReceipt(
-	c context.Context, transactionHash TxnHash,
-) (TxnReceipt, error) {
-	return TxnReceipt{}, nil
-}
-
-// StarknetGetCode Get the code of a specific contract
-func (HandlerRPC) StarknetGetCode(
-	c context.Context, contractAddress Address,
-) (CodeResult, error) {
-	return CodeResult{}, nil
-}
-
-// StarknetBlockNumber Get the most recent accepted block number
-func (HandlerRPC) StarknetBlockNumber(c context.Context) (BlockNumber, error) {
-	return 0, nil
-}
-
-// StarknetChainId Return the currently configured StarkNet chain id
-func (HandlerRPC) StarknetChainId(c context.Context) (ChainID, error) {
-	return "Here the ChainID", nil
-}
-
-// StarknetPendingTransactions Returns the transactions in the
-// transaction pool, recognized by this sequencer.
-func (HandlerRPC) StarknetPendingTransactions(
-	c context.Context,
-) ([]Txn, error) {
-	return nil, nil
-}
-
-// StarknetProtocolVersion Returns the current starknet protocol version
-// identifier, as supported by this sequencer.
-func (HandlerRPC) StarknetProtocolVersion(
-	c context.Context,
-) (ProtocolVersion, error) {
-	return "Here the Protocol Version", nil
-}
-
-// StarknetSyncing Returns an object about the sync status, or false if
-// the node is not syncing.
-func (HandlerRPC) StarknetSyncing(
-	c context.Context,
-) (SyncStatus, error) {
-	return SyncStatus{}, nil
-}
-
-// StarknetGetEvents Returns all event objects matching the conditions
-// in the provided filter.
-func (HandlerRPC) StarknetGetEvents(
-	c context.Context, r EventRequest,
-) (EventResponse, error) {
-	return EventResponse{}, nil
-}
diff --git a/pkg/rpc/types.go b/pkg/rpc/types.go
index 2c03e2a0..10144496 100644
--- a/pkg/rpc/types.go
+++ b/pkg/rpc/types.go
@@ -1,10 +1,22 @@
 package rpc
 
-// BlockStatus const
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+
+	"github.com/NethermindEth/juno/internal/services"
+
+	"github.com/NethermindEth/juno/pkg/common"
+	"github.com/NethermindEth/juno/pkg/types"
+)
+
+type RequestedScope string
+
 const (
-	TxnHashStatus      RequestedScope = "TXN_HASH"
-	FullTxns           RequestedScope = "FULL_TXNS"
-	FullTxnAndReceipts RequestedScope = "FULL_TXN_AND_RECEIPTS"
+	ScopeTxnHash            RequestedScope = "TXN_HASH"
+	ScopeFullTxns           RequestedScope = "FULL_TXNS"
+	ScopeFullTxnAndReceipts RequestedScope = "FULL_TXN_AND_RECEIPTS"
 )
 
 const (
@@ -39,11 +51,11 @@ var (
 // FunctionCall represents the details of the function call
 type FunctionCall struct {
 	// ContractAddress Is a field element of 251 bits. Represented as up to 64 hex digits
-	ContractAddress string `json:"contract_address"`
+	ContractAddress types.Address `json:"contract_address"`
 	// EntryPointSelector Is a field element of 251 bits. Represented as up to 64 hex digits
-	EntryPointSelector string `json:"entry_point_selector"`
+	EntryPointSelector types.Felt `json:"entry_point_selector"`
 	// CallData are the parameters passed to the function
-	CallData []string `json:"calldata"`
+	CallData []types.Felt `json:"calldata"`
 }
 
 // BlockHash Is a field element of 251 bits. Represented as up to 64 hex digits
@@ -52,6 +64,13 @@ type BlockHash string
 // BlockTag Is a tag specifying a dynamic reference to a block
 type BlockTag string
 
+const (
+	BlocktagLatest  BlockTag = "latest"
+	BlocktagPending BlockTag = "pending"
+)
+
+var blockTags = []BlockTag{BlocktagLatest, BlocktagPending}
+
 // Felt represent aN field element. Represented as up to 63 hex digits and leading 4 bits zeroed.
 type Felt string
 
@@ -64,10 +83,73 @@ type ChainID string
 // ProtocolVersion StarkNet protocol version, given in hex representation.
 type ProtocolVersion string
 
-type BlockNumberOrTag interface{}
+type BlockNumberOrTag struct {
+	Number *uint64
+	Tag    *BlockTag
+}
+
+func (x *BlockNumberOrTag) UnmarshalJSON(data []byte) error {
+	decoder := json.NewDecoder(bytes.NewBuffer(data))
+	token, err := decoder.Token()
+	if err != nil {
+		return err
+	}
+	switch t := token.(type) {
+	case float64:
+		blockNumber := uint64(t)
+		if blockNumber < 0 {
+			// notest
+			return errors.New("invalid block number")
+		}
+		*x = BlockNumberOrTag{Number: &blockNumber}
+	case string:
+		// notest
+		for _, tag := range blockTags {
+			if t == string(tag) {
+				*x = BlockNumberOrTag{Tag: &tag}
+				break
+			}
+		}
+	default:
+		// notest
+		return errors.New("unexpected token type")
+	}
+	return nil
+}
 
 // BlockHashOrTag The hash (id) of the requested block or a block tag, for the block referencing the state or call the transaction on.
-type BlockHashOrTag string
+type BlockHashOrTag struct {
+	Hash *types.BlockHash
+	Tag  *BlockTag
+}
+
+func (x *BlockHashOrTag) UnmarshalJSON(data []byte) error {
+	decoder := json.NewDecoder(bytes.NewBuffer(data))
+	token, err := decoder.Token()
+	if err != nil {
+		return err
+	}
+	switch t := token.(type) {
+	case string:
+		for _, tag := range blockTags {
+			if t == string(tag) {
+				*x = BlockHashOrTag{}
+				x.Tag = &tag
+				return nil
+			}
+		}
+		if common.IsHex(t) {
+			hash := types.HexToBlockHash(t)
+			*x = BlockHashOrTag{
+				Hash: &hash,
+			}
+		}
+	default:
+		// notest
+		return errors.New("unexpected token type")
+	}
+	return nil
+}
 
 // RequestRPC Represent the calls a function in a contract and returns the return value.  Using this call will not create a transaction; hence, will not change the state
 type RequestRPC struct {
@@ -125,7 +207,57 @@ type Txn struct {
 	// The function the transaction invokes
 	FunctionCall
 	// The hash identifying the transaction
-	TxnHash TxnHash `json:"txn_hash"`
+	TxnHash types.TransactionHash `json:"txn_hash"`
+	MaxFee  types.Felt            `json:"max_fee"`
+}
+
+func NewTxn(transaction types.IsTransaction) *Txn {
+	switch tx := transaction.(type) {
+	case *types.TransactionInvoke:
+		return &Txn{
+			FunctionCall: FunctionCall{
+				ContractAddress:    tx.ContractAddress,
+				EntryPointSelector: tx.EntryPointSelector,
+				CallData:           tx.CallData,
+			},
+			TxnHash: tx.GetHash(),
+			MaxFee:  tx.MaxFee,
+		}
+	default:
+		return &Txn{}
+	}
+}
+
+type TxnAndReceipt struct {
+	Txn
+	TxnReceipt
+}
+
+func (x *TxnAndReceipt) MarshalJSON() ([]byte, error) {
+	type TxnAndReceipt struct {
+		TxnHash            types.TransactionHash   `json:"txn_hash"`
+		MaxFee             types.Felt              `json:"max_fee"`
+		ContractAddress    types.Address           `json:"contract_address"`
+		EntryPointSelector types.Felt              `json:"entry_point_selector"`
+		CallData           []types.Felt            `json:"call_data"`
+		Status             types.TransactionStatus `json:"status,omitempty"`
+		StatusData         string                  `json:"status_data,omitempty"`
+		MessagesSent       []*MsgToL1              `json:"message_sent,omitempty"`
+		L1OriginMessage    *MsgToL2                `json:"l_1_origin_message,omitempty"`
+		Events             []*Event                `json:"events,omitempty"`
+	}
+	var enc TxnAndReceipt
+	enc.TxnHash = x.Txn.TxnHash
+	enc.MaxFee = x.MaxFee
+	enc.ContractAddress = x.ContractAddress
+	enc.EntryPointSelector = x.EntryPointSelector
+	enc.CallData = x.CallData
+	enc.Status = x.Status
+	enc.StatusData = x.StatusData
+	enc.MessagesSent = x.MessagesSent
+	enc.L1OriginMessage = x.L1OriginMessage
+	enc.Events = x.Events
+	return json.Marshal(enc)
 }
 
 type TxnStatus string
@@ -135,16 +267,30 @@ type EthAddress string
 
 type MsgToL1 struct {
 	// The target L1 address the message is sent to
-	ToAddress Felt `json:"to_address"`
+	ToAddress types.EthAddress `json:"to_address"`
 	// The Payload of the message
-	Payload Felt `json:"payload"`
+	Payload []types.Felt `json:"payload"`
+}
+
+func NewMsgToL1(msg *types.MessageL2ToL1) *MsgToL1 {
+	return &MsgToL1{
+		ToAddress: msg.ToAddress,
+		Payload:   msg.Payload,
+	}
 }
 
 type MsgToL2 struct {
 	// The originating L1 contract that sent the message
-	FromAddress EthAddress `json:"from_address"`
+	FromAddress types.EthAddress `json:"from_address"`
 	// The Payload of the message. The call data to the L1 handler
-	Payload []Felt `json:"payload"`
+	Payload []types.Felt `json:"payload"`
+}
+
+func NewMsgToL2(msg *types.MessageL1ToL2) *MsgToL2 {
+	return &MsgToL2{
+		FromAddress: msg.FromAddress,
+		Payload:     msg.Payload,
+	}
 }
 
 // EventFilter represent an event filter/query
@@ -157,14 +303,24 @@ type EventFilter struct {
 
 // EventContent represent the content of an Event
 type EventContent struct {
-	Keys []Felt `json:"keys"`
-	Data []Felt `json:"data"`
+	Keys []types.Felt `json:"keys"`
+	Data []types.Felt `json:"data"`
 }
 
 // Event represent a StarkNet Event
 type Event struct {
 	EventContent
-	FromAddress Address `json:"from_address"`
+	FromAddress types.Address `json:"from_address"`
+}
+
+func NewEvent(event *types.Event) *Event {
+	return &Event{
+		EventContent: EventContent{
+			event.Keys,
+			event.Data,
+		},
+		FromAddress: event.FromAddress,
+	}
 }
 
 // EmittedEvent Represent Event information decorated with metadata on where it was emitted
@@ -176,12 +332,36 @@ type EmittedEvent struct {
 
 // TxnReceipt Receipt of the transaction
 type TxnReceipt struct {
-	TxnHash         TxnHash   `json:"txn_hash"`
-	Status          TxnStatus `json:"status"`
-	StatusData      string    `json:"status_data"`
-	MessagesSent    []MsgToL1 `json:"messages_sent"`
-	L1OriginMessage MsgToL2   `json:"l1_origin_message"`
-	Events          Event     `json:"events"`
+	TxnHash         types.TransactionHash   `json:"txn_hash,omitempty"`
+	Status          types.TransactionStatus `json:"status,omitempty"`
+	StatusData      string                  `json:"status_data,omitempty"`
+	MessagesSent    []*MsgToL1              `json:"messages_sent,omitempty"`
+	L1OriginMessage *MsgToL2                `json:"l1_origin_message,omitempty"`
+	Events          []*Event                `json:"events,omitempty"`
+}
+
+func NewTxnReceipt(receipt *types.TransactionReceipt) *TxnReceipt {
+	out := &TxnReceipt{
+		TxnHash:    receipt.TxHash,
+		Status:     receipt.Status,
+		StatusData: receipt.StatusData,
+	}
+	if len(receipt.MessagesSent) != 0 {
+		out.MessagesSent = make([]*MsgToL1, len(receipt.MessagesSent))
+		for i, msg := range receipt.MessagesSent {
+			out.MessagesSent[i] = NewMsgToL1(&msg)
+		}
+	}
+	if receipt.L1OriginMessage != nil {
+		out.L1OriginMessage = NewMsgToL2(receipt.L1OriginMessage)
+	}
+	if len(receipt.Events) != 0 {
+		out.Events = make([]*Event, len(receipt.Events))
+		for i, e := range receipt.Events {
+			out.Events[i] = NewEvent(&e)
+		}
+	}
+	return out
 }
 
 // CodeResult The code and ABI for the requested contract
@@ -219,26 +399,67 @@ type Transactions struct{}
 
 type BlockResponse struct {
 	// A field element of 251 bits. Represented as up to 64 hex digits
-	BlockHash string `json:"block_hash"`
+	BlockHash types.BlockHash `json:"block_hash"`
 	// The hash of this block's parent
-	ParentHash string `json:"parent_hash"`
+	ParentHash types.BlockHash `json:"parent_hash"`
 	// The block number (its height)
 	BlockNumber uint64 `json:"block_number"`
 	// The status of the block
-	Status BlockStatus `json:"status"`
+	Status types.BlockStatus `json:"status"`
 	// The identity of the sequencer submitting this block
-	Sequencer string `json:"sequencer"`
+	Sequencer types.Address `json:"sequencer"`
 	// The new global state root
-	NewRoot Felt `json:"new_root"`
+	NewRoot types.Felt `json:"new_root"`
 	// The previous global state root
-	OldRoot string `json:"old_root"`
+	OldRoot types.Felt `json:"old_root"`
 	// When the block was accepted on L1. Formatted as...
-	AcceptedTime uint64 `json:"accepted_time"`
+	AcceptedTime int64 `json:"accepted_time"`
 	// Transactions in the Block
-	Transactions []Transactions `json:"transactions"`
+	Transactions interface{} `json:"transactions"`
 }
 
-type RequestedScope string
+func NewBlockResponse(block *types.Block, scope RequestedScope) *BlockResponse {
+	response := &BlockResponse{
+		BlockHash:    block.BlockHash,
+		ParentHash:   block.ParentHash,
+		BlockNumber:  block.BlockNumber,
+		Status:       block.Status,
+		Sequencer:    block.Sequencer,
+		NewRoot:      block.NewRoot,
+		OldRoot:      block.OldRoot,
+		AcceptedTime: block.AcceptedTime,
+	}
+	switch scope {
+	case ScopeTxnHash:
+		response.Transactions = block.TxHashes
+	case ScopeFullTxns:
+		txns := make([]*Txn, len(block.TxHashes))
+		for i, txHash := range block.TxHashes {
+			transaction := services.TransactionService.GetTransaction(txHash)
+			txn := NewTxn(transaction)
+			txns[i] = txn
+		}
+		response.Transactions = txns
+	case ScopeFullTxnAndReceipts:
+		txns := make([]*TxnAndReceipt, len(block.TxHashes))
+		for i, txHash := range block.TxHashes {
+			txnAndReceipt := &TxnAndReceipt{}
+			transaction := services.TransactionService.GetTransaction(txHash)
+			if transaction != nil {
+				txn := NewTxn(transaction)
+				txnAndReceipt.Txn = *txn
+			}
+			receipt := services.TransactionService.GetReceipt(txHash)
+			if receipt != nil {
+				r := NewTxnReceipt(receipt)
+				txnAndReceipt.TxnReceipt = *r
+			}
+			txns[i] = txnAndReceipt
+		}
+		response.Transactions = txns
+	}
+	return response
+}
 
 type StarkNetHash struct{}
 
diff --git a/pkg/types/address.go b/pkg/types/address.go
new file mode 100644
index 00000000..451281b8
--- /dev/null
+++ b/pkg/types/address.go
@@ -0,0 +1,41 @@
+package types
+
+import (
+	"encoding/json"
+)
+
+type Address Felt
+
+func BytesToAddress(b []byte) Address {
+	return Address(BytesToFelt(b))
+}
+
+func HexToAddress(s string) Address {
+	return Address(HexToFelt(s))
+}
+
+func (a *Address) Felt() Felt {
+	return Felt(*a)
+}
+
+func (a Address) MarshalJSON() ([]byte, error) {
+	return json.Marshal(a.Felt())
+}
+
+func (a *Address) UnmarshalJSON(data []byte) error {
+	felt := a.Felt()
+	err := felt.UnmarshalJSON(data)
+	if err != nil {
+		return err
+	}
+	*a = Address(felt)
+	return nil
+}
+
+func (a *Address) Bytes() []byte {
+	return a.Felt().Bytes()
+}
+
+func (a Address) Hex() string {
+	return a.Felt().Hex()
+}
diff --git a/pkg/types/block.go b/pkg/types/block.go
new file mode 100644
index 00000000..23663dde
--- /dev/null
+++ b/pkg/types/block.go
@@ -0,0 +1,99 @@
+package types
+
+import (
+	"encoding/json"
+)
+
+type BlockStatus int32
+
+const (
+	BlockStatusUnknown BlockStatus = iota
+	BlockStatusPending
+	BlockStatusProven
+	BlockStatusAcceptedOnL2
+	BlockStatusAcceptedOnL1
+	BlockStatusRejected
+)
+
+var (
+	BlockStatusName = map[BlockStatus]string{
+		BlockStatusUnknown:      "UNKNOWN",
+		BlockStatusPending:      "PENDING",
+		BlockStatusProven:       "PROVEN",
+		BlockStatusAcceptedOnL2: "ACCEPTED_ON_L2",
+		BlockStatusAcceptedOnL1: "ACCEPTED_ON_L1",
+		BlockStatusRejected:     "REJECTED",
+	}
+	BlockStatusValue = map[string]BlockStatus{
+		"UNKNOWN":        BlockStatusUnknown,
+		"PENDING":        BlockStatusPending,
+		"PROVEN":         BlockStatusProven,
+		"ACCEPTED_ON_L2": BlockStatusAcceptedOnL2,
+		"ACCEPTED_ON_L1": BlockStatusAcceptedOnL1,
+		"REJECTED":       BlockStatusRejected,
+	}
+)
+
+func StringToBlockStatus(s string) BlockStatus {
+	blockStatus, ok := BlockStatusValue[s]
+	if !ok {
+		// notest
+		return BlockStatusUnknown
+	}
+	return blockStatus
+}
+
+func (b BlockStatus) String() string {
+	return BlockStatusName[b]
+}
+
+func (b BlockStatus) MarshalJSON() ([]byte, error) {
+	return json.Marshal(b.String())
+}
+
+type BlockHash Felt
+
+func BytesToBlockHash(b []byte) BlockHash {
+	return BlockHash(BytesToFelt(b))
+}
+
+func HexToBlockHash(s string) BlockHash {
+	return BlockHash(HexToFelt(s))
+}
+
+func (b BlockHash) MarshalJSON() ([]byte, error) {
+	return json.Marshal(b.Felt())
+}
+
+func (b *BlockHash) Bytes() []byte {
+	return b.Felt().Bytes()
+}
+
+func (b *BlockHash) Felt() Felt {
+	return Felt(*b)
+}
+
+func (b BlockHash) Hex() string {
+	return Felt(b).Hex()
+}
+
+type BlockTag string
+
+type Block struct {
+	BlockHash    BlockHash   `json:"bloch_hash"`
+	ParentHash   BlockHash   `json:"parent_hash"`
+	BlockNumber  uint64      `json:"block_number"`
+	Status       BlockStatus `json:"status"`
+	Sequencer    Address     `json:"sequencer"`
+	NewRoot      Felt        `json:"new_root,omitempty"`
+	OldRoot      Felt        `json:"old_root"`
+	AcceptedTime int64       `json:"accepted_time"`
+	TimeStamp    int64       `json:"time_stamp"`
+
+	TxCount      uint64 `json:"tx_count"`
+	TxCommitment Felt   `json:"tx_commitment"`
+	TxHashes     []TransactionHash
+
+	EventCount      uint64 `json:"event_count"`
+	EventCommitment Felt   `json:"event_commitment"`
+}
diff --git a/pkg/types/eth.go b/pkg/types/eth.go
new file mode 100644
index 00000000..f6b894d9
--- /dev/null
+++ b/pkg/types/eth.go
@@ -0,0 +1,25 @@
+package types
+
+import (
+	"encoding/json"
+
+	eth "github.com/ethereum/go-ethereum/common"
+)
+
+type EthAddress eth.Address
+
+func HexToEthAddress(s string) EthAddress {
+	return EthAddress(eth.HexToAddress(s))
+}
+
+func BytesToEthAddress(b []byte) EthAddress {
+	return EthAddress(eth.BytesToAddress(b))
+}
+
+func (a EthAddress) MarshalJSON() ([]byte, error) {
+	return json.Marshal(eth.Address(a))
+}
+
+func (a EthAddress) Bytes() []byte {
+	return eth.Address(a).Bytes()
+}
diff --git a/pkg/types/event.go b/pkg/types/event.go
new file mode 100644
index 00000000..f573b983
--- /dev/null
+++ b/pkg/types/event.go
@@ -0,0 +1,7 @@
+package types
+
+type Event struct {
+	FromAddress Address
+	Keys        []Felt
+	Data        []Felt
+}
diff --git a/pkg/common/felt.go b/pkg/types/felt.go
similarity index 56%
rename from pkg/common/felt.go
rename to pkg/types/felt.go
index 2451cbd0..00723da3 100644
--- a/pkg/common/felt.go
+++ b/pkg/types/felt.go
@@ -1,10 +1,14 @@
-package common
+package types
 
 import (
+	"bytes"
 	"encoding/hex"
 	"encoding/json"
+	"errors"
 	"math/big"
 	"strings"
+
+	"github.com/NethermindEth/juno/pkg/common"
 )
 
 const (
@@ -12,6 +16,10 @@ const (
 	FeltLength = 32
 )
 
+type IsFelt interface {
+	Felt() Felt
+}
+
 type Felt [FeltLength]byte
 
 func BytesToFelt(b []byte) Felt {
@@ -25,7 +33,7 @@ func BigToFelt(b *big.Int) Felt {
 }
 
 func HexToFelt(s string) Felt {
-	return BytesToFelt(FromHex(s))
+	return BytesToFelt(common.FromHex(s))
 }
 
 func (f Felt) Bytes() []byte {
@@ -39,7 +47,11 @@ func (f Felt) Big() *big.Int {
 func (f Felt) Hex() string {
 	enc := make([]byte, len(f)*2)
 	hex.Encode(enc, f[:])
-	return "0x" + strings.TrimLeft(string(enc), "0")
+	s := strings.TrimLeft(string(enc), "0")
+	if s == "" {
+		s = "0"
+	}
+	return "0x" + s
 }
 
 func (f Felt) String() string {
@@ -56,3 +68,21 @@ func (f *Felt) SetBytes(b []byte) {
 func (f Felt) MarshalJSON() ([]byte, error) {
 	return json.Marshal(f.Hex())
 }
+
+func (f *Felt) UnmarshalJSON(data []byte) error {
+	decoder := json.NewDecoder(bytes.NewBuffer(data))
+	token, err := decoder.Token()
+	if err != nil {
+		return err
+	}
+	switch t := token.(type) {
+	case string:
+		if !common.IsHex(t) {
+			return errors.New("invalid hexadecimal string")
+		}
+		*f = HexToFelt(t)
+	default:
+		return errors.New("unexpected token type")
+	}
+	return nil
+}
diff --git a/pkg/types/message.go b/pkg/types/message.go
new file mode 100644
index 00000000..471856ea
--- /dev/null
+++ b/pkg/types/message.go
@@ -0,0 +1,11 @@
+package types
+
+type MessageL2ToL1 struct {
+	ToAddress EthAddress
+	Payload   []Felt
+}
+
+type MessageL1ToL2 struct {
+	FromAddress EthAddress
+	Payload     []Felt
+}
diff --git a/pkg/types/transaction.go b/pkg/types/transaction.go
new file mode 100644
index 00000000..eb80ad63
--- /dev/null
+++ b/pkg/types/transaction.go
@@ -0,0 +1,121 @@
+package types
+
+import (
+	"encoding/json"
+)
+
+type TransactionHash Felt
+
+func BytesToTransactionHash(b []byte) TransactionHash {
+	return TransactionHash(BytesToFelt(b))
+}
+
+func HexToTransactionHash(s string) TransactionHash {
+	return TransactionHash(HexToFelt(s))
+}
+
+func (t TransactionHash) Felt() Felt {
+	return Felt(t)
+}
+
+func (t TransactionHash) Bytes() []byte {
+	return t.Felt().Bytes()
+}
+
+func (t TransactionHash) MarshalJSON() ([]byte, error) {
+	return json.Marshal(Felt(t))
+}
+
+func (t TransactionHash) String() string {
+	return Felt(t).String()
+}
+
+func (t *TransactionHash) UnmarshalJSON(data []byte) error {
+	f := Felt{}
+	err := f.UnmarshalJSON(data)
+	if err != nil {
+		return err
+	}
+	*t = TransactionHash(f)
+	return nil
+}
+
+type IsTransaction interface {
+	GetHash() TransactionHash
+}
+
+type TransactionDeploy struct {
+	Hash                TransactionHash
+	ContractAddress     Address
+	ConstructorCallData []Felt
+}
+
+func (tx *TransactionDeploy) GetHash() TransactionHash {
+	return tx.Hash
+}
+
+type TransactionInvoke struct {
+	Hash               TransactionHash `json:"txn_hash"`
+	ContractAddress    Address         `json:"contract_address"`
+	EntryPointSelector Felt            `json:"entry_point_selector"`
+	CallData           []Felt          `json:"calldata"`
+	Signature          []Felt          `json:"-"`
+	MaxFee             Felt            `json:"max_fee"`
+}
+
+func (tx *TransactionInvoke) GetHash() TransactionHash {
+	return tx.Hash
+}
+
+type TransactionStatus int64
+
+const (
+	TxStatusUnknown TransactionStatus = iota
+	TxStatusNotReceived
+	TxStatusReceived
+	TxStatusPending
+	TxStatusRejected
+	TxStatusAcceptedOnL2
+	TxStatusAcceptedOnL1
+)
+
+var (
+	TxStatusName = map[TransactionStatus]string{
+		TxStatusUnknown:      "UNKNOWN",
+		TxStatusNotReceived:  "NOT_RECEIVED",
+		TxStatusReceived:     "RECEIVED",
+		TxStatusPending:      "PENDING",
+		TxStatusRejected:     "REJECTED",
+		TxStatusAcceptedOnL2: "ACCEPTED_ON_L2",
+		TxStatusAcceptedOnL1: "ACCEPTED_ON_L1",
+	}
+	TxStatusValue = map[string]TransactionStatus{
+		"UNKNOWN":        TxStatusUnknown,
+		"NOT_RECEIVED":   TxStatusNotReceived,
+		"RECEIVED":       TxStatusReceived,
+		"PENDING":        TxStatusPending,
+		"REJECTED":       TxStatusRejected,
+		"ACCEPTED_ON_L2": TxStatusAcceptedOnL2,
+		"ACCEPTED_ON_L1": TxStatusAcceptedOnL1,
+	}
+)
+
+func (s TransactionStatus) String() string {
+	// notest
+	return TxStatusName[s]
+}
+
+type TransactionReceipt struct {
+	TxHash          TransactionHash
+	ActualFee       Felt
+	Status          TransactionStatus
+	StatusData      string
+	MessagesSent    []MessageL2ToL1
+	L1OriginMessage *MessageL1ToL2
+	Events          []Event
+}
+
+type TransactionWithReceipt struct {
+	IsTransaction
+	TransactionReceipt
+}


Test patch:
diff --git a/internal/db/block/block_test.go b/internal/db/block/block_test.go
index 424ef71a..f1e39515 100644
--- a/internal/db/block/block_test.go
+++ b/internal/db/block/block_test.go
@@ -1,48 +1,45 @@
 package block
 
 import (
-	"bytes"
-	"encoding/hex"
-	"fmt"
-	"math/big"
+	"reflect"
 	"testing"
 
 	"github.com/NethermindEth/juno/internal/db"
-	"google.golang.org/protobuf/proto"
+	"github.com/NethermindEth/juno/pkg/types"
 )
 
-func TestManager_PutBlock(t *testing.T) {
-	blocks := []*Block{
+func TestManager(t *testing.T) {
+	blocks := []*types.Block{
 		{
-			Hash:             fromHexString("43950c9e3565cba1f2627b219d4863380f93a8548818ce26019d1bd5eebb0fb"),
-			BlockNumber:      2175,
-			ParentBlockHash:  fromHexString("f8fe26de3ce9ee4d543b1152deb2ce549e589524d79598227761d6006b74a9"),
-			Status:           "ACCEPTED_ON_L2",
-			SequencerAddress: fromHexString("0"),
-			GlobalStateRoot:  fromHexString("6a42d697b5b735eef03bb71841ed5099d57088f7b5eec8e356fe2601d5ba08f"),
-			OldRoot:          fromHexString("1d932dcf7da6c4f7605117cf514d953147161ab2d8f762dcebbb6dad427e519"),
-			AcceptedTime:     1652492749,
-			TimeStamp:        1652488132,
-			TxCount:          2,
-			TxCommitment:     fromHexString("0"),
-			EventCount:       19,
-			EventCommitment:  fromHexString("0"),
-			TxHashes: [][]byte{
-				fromHexString("5ce76214481ebb29f912cb5d31abdff34fd42217f5ece9dda76d9fcfd62dc73"),
-				fromHexString("4ff16b7673da1f4c4b114d28e0e1a366bd61b702eca3e21882da6c8939e60a2"),
+			BlockHash:    types.HexToBlockHash("43950c9e3565cba1f2627b219d4863380f93a8548818ce26019d1bd5eebb0fb"),
+			ParentHash:   types.HexToBlockHash("f8fe26de3ce9ee4d543b1152deb2ce549e589524d79598227761d6006b74a9"),
+			BlockNumber:  2175,
+			Status:       types.BlockStatusAcceptedOnL2,
+			Sequencer:    types.HexToAddress("0"),
+			NewRoot:      types.HexToFelt("6a42d697b5b735eef03bb71841ed5099d57088f7b5eec8e356fe2601d5ba08f"),
+			OldRoot:      types.HexToFelt("1d932dcf7da6c4f7605117cf514d953147161ab2d8f762dcebbb6dad427e519"),
+			AcceptedTime: 1652492749,
+			TimeStamp:    1652488132,
+			TxCount:      2,
+			TxCommitment: types.HexToFelt("0"),
+			TxHashes: []types.TransactionHash{
+				types.HexToTransactionHash("5ce76214481ebb29f912cb5d31abdff34fd42217f5ece9dda76d9fcfd62dc73"),
+				types.HexToTransactionHash("4ff16b7673da1f4c4b114d28e0e1a366bd61b702eca3e21882da6c8939e60a2"),
 			},
+			EventCount:      19,
+			EventCommitment: types.HexToFelt("0"),
 		},
 	}
 	manager := NewManager(db.NewKeyValueDb(t.TempDir(), 0))
 	for _, block := range blocks {
-		key := block.Hash
+		key := block.BlockHash
 		manager.PutBlock(key, block)
 		// Get block by hash
 		returnedBlock := manager.GetBlockByHash(key)
 		if returnedBlock == nil {
-			t.Errorf("unexpected nil after search for block with hash %s", hex.EncodeToString(block.Hash))
+			t.Errorf("unexpected nil after search for block with hash %s", block.BlockHash)
 		}
-		if !equalData(t, block, returnedBlock) {
+		if !reflect.DeepEqual(block, returnedBlock) {
 			t.Errorf("block")
 		}
 		// Get block by number
@@ -50,29 +47,9 @@ func TestManager_PutBlock(t *testing.T) {
 		if returnedBlock == nil {
 			t.Errorf("unexpected nil after search for block with number %d", block.BlockNumber)
 		}
-		if !equalData(t, block, returnedBlock) {
+		if !reflect.DeepEqual(block, returnedBlock) {
 			t.Errorf("block")
 		}
 	}
 	manager.Close()
 }
-
-func equalData(t *testing.T, a, b *Block) bool {
-	aData, err := proto.Marshal(a)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	bData, err := proto.Marshal(b)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	return bytes.Compare(aData, bData) == 0
-}
-
-func fromHexString(s string) []byte {
-	number, ok := new(big.Int).SetString(s, 16)
-	if !ok {
-		panic(any(fmt.Errorf("error pasing hex-string: %s", s)))
-	}
-	return number.Bytes()
-}
diff --git a/internal/db/transaction/manager_test.go b/internal/db/transaction/manager_test.go
index 2967424b..6eb78f06 100644
--- a/internal/db/transaction/manager_test.go
+++ b/internal/db/transaction/manager_test.go
@@ -1,79 +1,70 @@
 package transaction
 
 import (
-	"bytes"
-	"encoding/hex"
+	"reflect"
 	"testing"
 
+	"github.com/NethermindEth/juno/pkg/types"
+
 	"github.com/NethermindEth/juno/internal/db"
-	"google.golang.org/protobuf/proto"
 )
 
-var txs = []*Transaction{
-	{
-		Hash: decodeString("49eb3544a95587518b0d2a32b9e456cb05b32e0085ebc0bcecb8ef2e15dc3a2"),
-		Tx: &Transaction_Invoke{Invoke: &InvokeFunction{
-			ContractAddress:    decodeString("7e1b2de3dc9e3cf83278452786c23b384cf77a66c3073f94ab451ed0029b5af"),
-			EntryPointSelector: decodeString("317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
-			CallData: [][]byte{
-				decodeString("1d24663eb96a2b9a568b88ff520d04779c03ae8ce3087aa55bea1a34f07c6f7"),
-				decodeString("2"),
-				decodeString("1ab006325ae5196978cfcaefd3d748e8583079ebb33b402537a4b4c174e16c6"),
-				decodeString("77575e21a0326adb26b58aaa1ef7139ba8e3164ab54411dbf9a5809b8d6ea8"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+var txs = []types.IsTransaction{
+	&types.TransactionInvoke{
+		Hash:               types.HexToTransactionHash("0x49eb3544a95587518b0d2a32b9e456cb05b32e0085ebc0bcecb8ef2e15dc3a2"),
+		ContractAddress:    types.HexToAddress("0x7e1b2de3dc9e3cf83278452786c23b384cf77a66c3073f94ab451ed0029b5af"),
+		EntryPointSelector: types.HexToFelt("0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x1d24663eb96a2b9a568b88ff520d04779c03ae8ce3087aa55bea1a34f07c6f7"),
+			types.HexToFelt("0x2"),
+			types.HexToFelt("0x1ab006325ae5196978cfcaefd3d748e8583079ebb33b402537a4b4c174e16c6"),
+			types.HexToFelt("0x77575e21a0326adb26b58aaa1ef7139ba8e3164ab54411dbf9a5809b8d6ea8"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("50398c6ec05a07642e5bd52c656e1650f3b057361283ecbb19d4062199e4626"),
-		Tx: &Transaction_Invoke{Invoke: &InvokeFunction{
-			ContractAddress:    decodeString("3e875a858f9a0229e4a59cb72a4086d324b9b2148242694f2dd12d59d993b62"),
-			EntryPointSelector: decodeString("27c3334165536f239cfd400ed956eabff55fc60de4fb56728b6a4f6b87db01c"),
-			CallData: [][]byte{
-				decodeString("4d6f00affbeb6239fe0eb3eb4afefddbaea71533c152f44a1cdd113c1fdeade"),
-				decodeString("33ce93a3eececa5c9fc70da05f4aff3b00e1820b79587924d514bc76788991a"),
-				decodeString("1"),
-				decodeString("0"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+	&types.TransactionInvoke{
+		Hash:               types.HexToTransactionHash("0x50398c6ec05a07642e5bd52c656e1650f3b057361283ecbb19d4062199e4626"),
+		ContractAddress:    types.HexToAddress("0x3e875a858f9a0229e4a59cb72a4086d324b9b2148242694f2dd12d59d993b62"),
+		EntryPointSelector: types.HexToFelt("0x27c3334165536f239cfd400ed956eabff55fc60de4fb56728b6a4f6b87db01c"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x4d6f00affbeb6239fe0eb3eb4afefddbaea71533c152f44a1cdd113c1fdeade"),
+			types.HexToFelt("0x33ce93a3eececa5c9fc70da05f4aff3b00e1820b79587924d514bc76788991a"),
+			types.HexToFelt("0x1"),
+			types.HexToFelt("0x0"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("1209ae3031dd69ef8ab4507dc4cc2c478d9a0414cb42225ce223670dee5cdcf"),
-		Tx: &Transaction_Invoke{Invoke: &InvokeFunction{
-			ContractAddress:    decodeString("764c36cfdc456e1f3565441938f958badcc0ce8f20b7ed5819af30ed18f245"),
-			EntryPointSelector: decodeString("317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
-			CallData: [][]byte{
-				decodeString("7e24f78ee360727e9fdd55e2b847202057724bf1f7e5bc25ff78f7760b6895b"),
-				decodeString("2"),
-				decodeString("51378ba07a08230eab5af933c8e1bd905bc9436bf96ab5f173010eb022eb2a4"),
-				decodeString("5f8a361ec261cb4b34d4481803903bb9b8e5c8768e24099aa85ad7f3e8f13b8"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+	&types.TransactionInvoke{
+		Hash: types.HexToTransactionHash("0x1209ae3031dd69ef8ab4507dc4cc2c478d9a0414cb42225ce223670dee5cdcf"),
+
+		ContractAddress:    types.HexToAddress("0x764c36cfdc456e1f3565441938f958badcc0ce8f20b7ed5819af30ed18f245"),
+		EntryPointSelector: types.HexToFelt("0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x7e24f78ee360727e9fdd55e2b847202057724bf1f7e5bc25ff78f7760b6895b"),
+			types.HexToFelt("0x2"),
+			types.HexToFelt("0x51378ba07a08230eab5af933c8e1bd905bc9436bf96ab5f173010eb022eb2a4"),
+			types.HexToFelt("0x5f8a361ec261cb4b34d4481803903bb9b8e5c8768e24099aa85ad7f3e8f13b8"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("e0a2e45a80bb827967e096bcf58874f6c01c191e0a0530624cba66a508ae75"),
-		Tx: &Transaction_Deploy{Deploy: &Deploy{
-			ContractAddressSalt: decodeString("546c86dc6e40a5e5492b782d8964e9a4274ff6ecb16d31eb09cee45a3564015"),
-			ConstructorCallData: [][]byte{
-				decodeString("06cf6c2f36d36b08e591e4489e92ca882bb67b9c39a3afccf011972a8de467f0"),
-				decodeString("7ab344d88124307c07b56f6c59c12f4543e9c96398727854a322dea82c73240"),
-			},
-		}},
+	&types.TransactionDeploy{
+		Hash:            types.HexToTransactionHash("0xe0a2e45a80bb827967e096bcf58874f6c01c191e0a0530624cba66a508ae75"),
+		ContractAddress: types.HexToAddress("0x546c86dc6e40a5e5492b782d8964e9a4274ff6ecb16d31eb09cee45a3564015"),
+		ConstructorCallData: []types.Felt{
+			types.HexToFelt("06cf6c2f36d36b08e591e4489e92ca882bb67b9c39a3afccf011972a8de467f0"),
+			types.HexToFelt("7ab344d88124307c07b56f6c59c12f4543e9c96398727854a322dea82c73240"),
+		},
 	},
-	{
-		Hash: decodeString("12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
-		Tx: &Transaction_Deploy{Deploy: &Deploy{
-			ContractAddressSalt: decodeString("12afa0f342ece0468ca9810f0ea59f9c7204af32d1b8b0d318c4f2fe1f384e"),
-			ConstructorCallData: [][]byte{
-				decodeString("cfc2e2866fd08bfb4ac73b70e0c136e326ae18fc797a2c090c8811c695577e"),
-				decodeString("5f1dd5a5aef88e0498eeca4e7b2ea0fa7110608c11531278742f0b5499af4b3"),
-			},
-		}},
+	&types.TransactionDeploy{
+		Hash:            types.HexToTransactionHash("0x12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
+		ContractAddress: types.HexToAddress("0x12afa0f342ece0468ca9810f0ea59f9c7204af32d1b8b0d318c4f2fe1f384e"),
+		ConstructorCallData: []types.Felt{
+			types.HexToFelt("0xcfc2e2866fd08bfb4ac73b70e0c136e326ae18fc797a2c090c8811c695577e"),
+			types.HexToFelt("0x5f1dd5a5aef88e0498eeca4e7b2ea0fa7110608c11531278742f0b5499af4b3"),
+		},
 	},
 }
 
@@ -81,7 +72,7 @@ func TestManager_PutTransaction(t *testing.T) {
 	database := db.NewKeyValueDb(t.TempDir(), 0)
 	manager := NewManager(database)
 	for _, tx := range txs {
-		manager.PutTransaction(tx.Hash, tx)
+		manager.PutTransaction(tx.GetHash(), tx)
 	}
 	manager.Close()
 }
@@ -91,62 +82,64 @@ func TestManager_GetTransaction(t *testing.T) {
 	manager := NewManager(database)
 	// Insert all the transactions
 	for _, tx := range txs {
-		manager.PutTransaction(tx.Hash, tx)
+		manager.PutTransaction(tx.GetHash(), tx)
 	}
 	// Get all the transactions and compare
 	for _, tx := range txs {
-		outTx := manager.GetTransaction(tx.Hash)
-
-		if !equalMessage(t, tx, outTx) {
+		outTx := manager.GetTransaction(tx.GetHash())
+		if !reflect.DeepEqual(tx, outTx) {
 			t.Errorf("transaction not equal after Put/Get operations")
 		}
 	}
 	manager.Close()
 }
 
-func decodeString(s string) []byte {
-	x, _ := hex.DecodeString(s)
-	return x
-}
-
-var receipts = []*TransactionReceipt{
+var receipts = []*types.TransactionReceipt{
 	{
-		TxHash:       decodeString("7932de7ec535bfd45e2951a35c06e13d22188cb7eb7b7cc43454ee63df78aff"),
-		ActualFee:    decodeString("0"),
-		Status:       0,
-		StatusData:   "",
-		MessagesSent: nil,
-		L1OriginMessage: &MessageToL2{
-			FromAddress: "0x659a00c33263d9254Fed382dE81349426C795BB6",
-			Payload: [][]byte{
-				decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-				decodeString("2616da7c393d14000"),
-				decodeString("0"),
-				decodeString("b9d83d298d46c4dd73618f19a2a40084ce36476a"),
+		TxHash:     types.HexToTransactionHash("0x7932de7ec535bfd45e2951a35c06e13d22188cb7eb7b7cc43454ee63df78aff"),
+		ActualFee:  types.HexToFelt("0x0"),
+		Status:     types.TxStatusAcceptedOnL2,
+		StatusData: "",
+		MessagesSent: []types.MessageL2ToL1{
+			{
+				ToAddress: types.HexToEthAddress("0x8fccde9ae0ca4da714b8f27d4aaf6edfa8ce92a4"),
+				Payload: []types.Felt{
+					types.HexToFelt("0xc"),
+					types.HexToFelt("0x22"),
+				},
+			},
+		},
+		L1OriginMessage: &types.MessageL1ToL2{
+			FromAddress: types.HexToEthAddress("0x659a00c33263d9254Fed382dE81349426C795BB6"),
+			Payload: []types.Felt{
+				types.HexToFelt("0x68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+				types.HexToFelt("0x2616da7c393d14000"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0xb9d83d298d46c4dd73618f19a2a40084ce36476a"),
 			},
 		},
-		Events: []*Event{
+		Events: []types.Event{
 			{
-				FromAddress: decodeString("da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"),
-				Keys: [][]byte{
-					decodeString("99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"),
+				FromAddress: types.HexToAddress("0xda114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"),
+				Keys: []types.Felt{
+					types.HexToFelt("99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"),
 				},
-				Data: [][]byte{
-					decodeString("0"),
-					decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-					decodeString("2616da7c393d14000"),
-					decodeString("0"),
+				Data: []types.Felt{
+					types.HexToFelt("0"),
+					types.HexToFelt("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+					types.HexToFelt("2616da7c393d14000"),
+					types.HexToFelt("0"),
 				},
 			},
 			{
-				FromAddress: decodeString("1108cdbe5d82737b9057590adaf97d34e74b5452f0628161d237746b6fe69e"),
-				Keys: [][]byte{
-					decodeString("221e5a5008f7a28564f0eaa32cdeb0848d10657c449aed3e15d12150a7c2db3"),
+				FromAddress: types.HexToAddress("0x1108cdbe5d82737b9057590adaf97d34e74b5452f0628161d237746b6fe69e"),
+				Keys: []types.Felt{
+					types.HexToFelt("0x221e5a5008f7a28564f0eaa32cdeb0848d10657c449aed3e15d12150a7c2db3"),
 				},
-				Data: [][]byte{
-					decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-					decodeString("2616da7c393d14000"),
-					decodeString("0"),
+				Data: []types.Felt{
+					types.HexToFelt("0x68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+					types.HexToFelt("0x2616da7c393d14000"),
+					types.HexToFelt("0x0"),
 				},
 			},
 		},
@@ -171,21 +164,9 @@ func TestManager_GetReceipt(t *testing.T) {
 	for _, receipt := range receipts {
 		outReceipt := manager.GetReceipt(receipt.TxHash)
 
-		if !equalMessage(t, receipt, outReceipt) {
+		if !reflect.DeepEqual(receipt, outReceipt) {
 			t.Errorf("receipt not equal after Put/Get operations")
 		}
 	}
 	manager.Close()
 }
-
-func equalMessage(t *testing.T, a, b proto.Message) bool {
-	aRaw, err := proto.Marshal(a)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	bRaw, err := proto.Marshal(b)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	return bytes.Compare(aRaw, bRaw) == 0
-}
diff --git a/internal/services/block_test.go b/internal/services/block_test.go
index 14f8a598..248c349e 100644
--- a/internal/services/block_test.go
+++ b/internal/services/block_test.go
@@ -1,38 +1,34 @@
 package services
 
 import (
-	"bytes"
 	"context"
-	"encoding/hex"
-	"fmt"
-	"math/big"
+	"reflect"
 	"testing"
 
 	"github.com/NethermindEth/juno/internal/db"
-	"github.com/NethermindEth/juno/internal/db/block"
-	"google.golang.org/protobuf/proto"
+	"github.com/NethermindEth/juno/pkg/types"
 )
 
 func TestService(t *testing.T) {
-	blocks := []*block.Block{
+	blocks := []*types.Block{
 		{
-			Hash:             fromHexString("43950c9e3565cba1f2627b219d4863380f93a8548818ce26019d1bd5eebb0fb"),
-			BlockNumber:      2175,
-			ParentBlockHash:  fromHexString("f8fe26de3ce9ee4d543b1152deb2ce549e589524d79598227761d6006b74a9"),
-			Status:           "ACCEPTED_ON_L2",
-			SequencerAddress: fromHexString("0"),
-			GlobalStateRoot:  fromHexString("6a42d697b5b735eef03bb71841ed5099d57088f7b5eec8e356fe2601d5ba08f"),
-			OldRoot:          fromHexString("1d932dcf7da6c4f7605117cf514d953147161ab2d8f762dcebbb6dad427e519"),
-			AcceptedTime:     1652492749,
-			TimeStamp:        1652488132,
-			TxCount:          2,
-			TxCommitment:     fromHexString("0"),
-			EventCount:       19,
-			EventCommitment:  fromHexString("0"),
-			TxHashes: [][]byte{
-				fromHexString("5ce76214481ebb29f912cb5d31abdff34fd42217f5ece9dda76d9fcfd62dc73"),
-				fromHexString("4ff16b7673da1f4c4b114d28e0e1a366bd61b702eca3e21882da6c8939e60a2"),
+			BlockHash:    types.HexToBlockHash("43950c9e3565cba1f2627b219d4863380f93a8548818ce26019d1bd5eebb0fb"),
+			ParentHash:   types.HexToBlockHash("f8fe26de3ce9ee4d543b1152deb2ce549e589524d79598227761d6006b74a9"),
+			BlockNumber:  2175,
+			Status:       types.BlockStatusAcceptedOnL2,
+			Sequencer:    types.HexToAddress("0"),
+			NewRoot:      types.HexToFelt("6a42d697b5b735eef03bb71841ed5099d57088f7b5eec8e356fe2601d5ba08f"),
+			OldRoot:      types.HexToFelt("1d932dcf7da6c4f7605117cf514d953147161ab2d8f762dcebbb6dad427e519"),
+			AcceptedTime: 1652492749,
+			TimeStamp:    1652488132,
+			TxCount:      2,
+			TxCommitment: types.HexToFelt("0"),
+			TxHashes: []types.TransactionHash{
+				types.HexToTransactionHash("5ce76214481ebb29f912cb5d31abdff34fd42217f5ece9dda76d9fcfd62dc73"),
+				types.HexToTransactionHash("4ff16b7673da1f4c4b114d28e0e1a366bd61b702eca3e21882da6c8939e60a2"),
 			},
+			EventCount:      19,
+			EventCommitment: types.HexToFelt("0"),
 		},
 	}
 	BlockService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
@@ -41,14 +37,14 @@ func TestService(t *testing.T) {
 		t.Errorf("error starting the service: %s", err)
 	}
 	for _, b := range blocks {
-		key := b.Hash
+		key := b.BlockHash
 		BlockService.StoreBlock(key, b)
 		// Get block by hash
 		returnedBlock := BlockService.GetBlockByHash(key)
 		if returnedBlock == nil {
-			t.Errorf("unexpected nil after search for block with hash %s", hex.EncodeToString(b.Hash))
+			t.Errorf("unexpected nil after search for block with hash %s", key)
 		}
-		if !equalData(t, b, returnedBlock) {
+		if !reflect.DeepEqual(b, returnedBlock) {
 			t.Errorf("b")
 		}
 		// Get block by number
@@ -56,29 +52,9 @@ func TestService(t *testing.T) {
 		if returnedBlock == nil {
 			t.Errorf("unexpected nil after search for block with number %d", b.BlockNumber)
 		}
-		if !equalData(t, b, returnedBlock) {
+		if !reflect.DeepEqual(b, returnedBlock) {
 			t.Errorf("b")
 		}
 	}
 	BlockService.Close(context.Background())
 }
-
-func equalData(t *testing.T, a, b *block.Block) bool {
-	aData, err := proto.Marshal(a)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	bData, err := proto.Marshal(b)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	return bytes.Compare(aData, bData) == 0
-}
-
-func fromHexString(s string) []byte {
-	number, ok := new(big.Int).SetString(s, 16)
-	if !ok {
-		panic(any(fmt.Errorf("error pasing hex-string: %s", s)))
-	}
-	return number.Bytes()
-}
diff --git a/internal/services/transactions_test.go b/internal/services/transactions_test.go
index 3bca82bd..e82d854d 100644
--- a/internal/services/transactions_test.go
+++ b/internal/services/transactions_test.go
@@ -1,80 +1,71 @@
 package services
 
 import (
-	"bytes"
 	"context"
+	"reflect"
 	"testing"
 
+	"github.com/NethermindEth/juno/pkg/types"
+
 	"github.com/NethermindEth/juno/internal/db"
-	"github.com/NethermindEth/juno/internal/db/transaction"
-	"google.golang.org/protobuf/proto"
 )
 
-var txs = []*transaction.Transaction{
-	{
-		Hash: decodeString("49eb3544a95587518b0d2a32b9e456cb05b32e0085ebc0bcecb8ef2e15dc3a2"),
-		Tx: &transaction.Transaction_Invoke{Invoke: &transaction.InvokeFunction{
-			ContractAddress:    decodeString("7e1b2de3dc9e3cf83278452786c23b384cf77a66c3073f94ab451ed0029b5af"),
-			EntryPointSelector: decodeString("317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
-			CallData: [][]byte{
-				decodeString("1d24663eb96a2b9a568b88ff520d04779c03ae8ce3087aa55bea1a34f07c6f7"),
-				decodeString("2"),
-				decodeString("1ab006325ae5196978cfcaefd3d748e8583079ebb33b402537a4b4c174e16c6"),
-				decodeString("77575e21a0326adb26b58aaa1ef7139ba8e3164ab54411dbf9a5809b8d6ea8"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+var txs = []types.IsTransaction{
+	&types.TransactionInvoke{
+		Hash:               types.HexToTransactionHash("0x49eb3544a95587518b0d2a32b9e456cb05b32e0085ebc0bcecb8ef2e15dc3a2"),
+		ContractAddress:    types.HexToAddress("0x7e1b2de3dc9e3cf83278452786c23b384cf77a66c3073f94ab451ed0029b5af"),
+		EntryPointSelector: types.HexToFelt("0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x1d24663eb96a2b9a568b88ff520d04779c03ae8ce3087aa55bea1a34f07c6f7"),
+			types.HexToFelt("0x2"),
+			types.HexToFelt("0x1ab006325ae5196978cfcaefd3d748e8583079ebb33b402537a4b4c174e16c6"),
+			types.HexToFelt("0x77575e21a0326adb26b58aaa1ef7139ba8e3164ab54411dbf9a5809b8d6ea8"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("50398c6ec05a07642e5bd52c656e1650f3b057361283ecbb19d4062199e4626"),
-		Tx: &transaction.Transaction_Invoke{Invoke: &transaction.InvokeFunction{
-			ContractAddress:    decodeString("3e875a858f9a0229e4a59cb72a4086d324b9b2148242694f2dd12d59d993b62"),
-			EntryPointSelector: decodeString("27c3334165536f239cfd400ed956eabff55fc60de4fb56728b6a4f6b87db01c"),
-			CallData: [][]byte{
-				decodeString("4d6f00affbeb6239fe0eb3eb4afefddbaea71533c152f44a1cdd113c1fdeade"),
-				decodeString("33ce93a3eececa5c9fc70da05f4aff3b00e1820b79587924d514bc76788991a"),
-				decodeString("1"),
-				decodeString("0"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+	&types.TransactionInvoke{
+		Hash:               types.HexToTransactionHash("0x50398c6ec05a07642e5bd52c656e1650f3b057361283ecbb19d4062199e4626"),
+		ContractAddress:    types.HexToAddress("0x3e875a858f9a0229e4a59cb72a4086d324b9b2148242694f2dd12d59d993b62"),
+		EntryPointSelector: types.HexToFelt("0x27c3334165536f239cfd400ed956eabff55fc60de4fb56728b6a4f6b87db01c"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x4d6f00affbeb6239fe0eb3eb4afefddbaea71533c152f44a1cdd113c1fdeade"),
+			types.HexToFelt("0x33ce93a3eececa5c9fc70da05f4aff3b00e1820b79587924d514bc76788991a"),
+			types.HexToFelt("0x1"),
+			types.HexToFelt("0x0"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("1209ae3031dd69ef8ab4507dc4cc2c478d9a0414cb42225ce223670dee5cdcf"),
-		Tx: &transaction.Transaction_Invoke{Invoke: &transaction.InvokeFunction{
-			ContractAddress:    decodeString("764c36cfdc456e1f3565441938f958badcc0ce8f20b7ed5819af30ed18f245"),
-			EntryPointSelector: decodeString("317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
-			CallData: [][]byte{
-				decodeString("7e24f78ee360727e9fdd55e2b847202057724bf1f7e5bc25ff78f7760b6895b"),
-				decodeString("2"),
-				decodeString("51378ba07a08230eab5af933c8e1bd905bc9436bf96ab5f173010eb022eb2a4"),
-				decodeString("5f8a361ec261cb4b34d4481803903bb9b8e5c8768e24099aa85ad7f3e8f13b8"),
-			},
-			Signature: nil,
-			MaxFee:    decodeString("0"),
-		}},
+	&types.TransactionInvoke{
+		Hash: types.HexToTransactionHash("0x1209ae3031dd69ef8ab4507dc4cc2c478d9a0414cb42225ce223670dee5cdcf"),
+
+		ContractAddress:    types.HexToAddress("0x764c36cfdc456e1f3565441938f958badcc0ce8f20b7ed5819af30ed18f245"),
+		EntryPointSelector: types.HexToFelt("0x317eb442b72a9fae758d4fb26830ed0d9f31c8e7da4dbff4e8c59ea6a158e7f"),
+		CallData: []types.Felt{
+			types.HexToFelt("0x7e24f78ee360727e9fdd55e2b847202057724bf1f7e5bc25ff78f7760b6895b"),
+			types.HexToFelt("0x2"),
+			types.HexToFelt("0x51378ba07a08230eab5af933c8e1bd905bc9436bf96ab5f173010eb022eb2a4"),
+			types.HexToFelt("0x5f8a361ec261cb4b34d4481803903bb9b8e5c8768e24099aa85ad7f3e8f13b8"),
+		},
+		Signature: nil,
+		MaxFee:    types.HexToFelt("0x0"),
 	},
-	{
-		Hash: decodeString("e0a2e45a80bb827967e096bcf58874f6c01c191e0a0530624cba66a508ae75"),
-		Tx: &transaction.Transaction_Deploy{Deploy: &transaction.Deploy{
-			ContractAddressSalt: decodeString("546c86dc6e40a5e5492b782d8964e9a4274ff6ecb16d31eb09cee45a3564015"),
-			ConstructorCallData: [][]byte{
-				decodeString("06cf6c2f36d36b08e591e4489e92ca882bb67b9c39a3afccf011972a8de467f0"),
-				decodeString("7ab344d88124307c07b56f6c59c12f4543e9c96398727854a322dea82c73240"),
-			},
-		}},
+	&types.TransactionDeploy{
+		Hash:            types.HexToTransactionHash("0xe0a2e45a80bb827967e096bcf58874f6c01c191e0a0530624cba66a508ae75"),
+		ContractAddress: types.HexToAddress("0x546c86dc6e40a5e5492b782d8964e9a4274ff6ecb16d31eb09cee45a3564015"),
+		ConstructorCallData: []types.Felt{
+			types.HexToFelt("06cf6c2f36d36b08e591e4489e92ca882bb67b9c39a3afccf011972a8de467f0"),
+			types.HexToFelt("7ab344d88124307c07b56f6c59c12f4543e9c96398727854a322dea82c73240"),
+		},
 	},
-	{
-		Hash: decodeString("12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
-		Tx: &transaction.Transaction_Deploy{Deploy: &transaction.Deploy{
-			ContractAddressSalt: decodeString("12afa0f342ece0468ca9810f0ea59f9c7204af32d1b8b0d318c4f2fe1f384e"),
-			ConstructorCallData: [][]byte{
-				decodeString("cfc2e2866fd08bfb4ac73b70e0c136e326ae18fc797a2c090c8811c695577e"),
-				decodeString("5f1dd5a5aef88e0498eeca4e7b2ea0fa7110608c11531278742f0b5499af4b3"),
-			},
-		}},
+	&types.TransactionDeploy{
+		Hash:            types.HexToTransactionHash("0x12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
+		ContractAddress: types.HexToAddress("0x12afa0f342ece0468ca9810f0ea59f9c7204af32d1b8b0d318c4f2fe1f384e"),
+		ConstructorCallData: []types.Felt{
+			types.HexToFelt("0xcfc2e2866fd08bfb4ac73b70e0c136e326ae18fc797a2c090c8811c695577e"),
+			types.HexToFelt("0x5f1dd5a5aef88e0498eeca4e7b2ea0fa7110608c11531278742f0b5499af4b3"),
+		},
 	},
 }
 
@@ -88,7 +79,7 @@ func TestTransactionService_StoreTransaction(t *testing.T) {
 	}
 
 	for _, tx := range txs {
-		TransactionService.StoreTransaction(tx.Hash, tx)
+		TransactionService.StoreTransaction(tx.GetHash(), tx)
 	}
 	TransactionService.Close(context.Background())
 }
@@ -103,13 +94,13 @@ func TestManager_GetTransaction(t *testing.T) {
 	}
 	// Insert all the transactions
 	for _, tx := range txs {
-		TransactionService.StoreTransaction(tx.Hash, tx)
+		TransactionService.StoreTransaction(tx.GetHash(), tx)
 	}
 	// Get all the transactions and compare
 	for _, tx := range txs {
-		outTx := TransactionService.GetTransaction(tx.Hash)
+		outTx := TransactionService.GetTransaction(tx.GetHash())
 
-		if !equalMessage(t, tx, outTx) {
+		if !reflect.DeepEqual(tx, outTx) {
 			t.Errorf("transaction not equal after Put/Get operations")
 		}
 	}
@@ -120,44 +111,44 @@ func resetTransactionService() {
 	TransactionService = transactionService{}
 }
 
-var receipts = []*transaction.TransactionReceipt{
+var receipts = []*types.TransactionReceipt{
 	{
-		TxHash:       decodeString("7932de7ec535bfd45e2951a35c06e13d22188cb7eb7b7cc43454ee63df78aff"),
-		ActualFee:    decodeString("0"),
-		Status:       0,
+		TxHash:       types.HexToTransactionHash("0x7932de7ec535bfd45e2951a35c06e13d22188cb7eb7b7cc43454ee63df78aff"),
+		ActualFee:    types.HexToFelt("0x0"),
+		Status:       types.TxStatusAcceptedOnL2,
 		StatusData:   "",
 		MessagesSent: nil,
-		L1OriginMessage: &transaction.MessageToL2{
-			FromAddress: "0x659a00c33263d9254Fed382dE81349426C795BB6",
-			Payload: [][]byte{
-				decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-				decodeString("2616da7c393d14000"),
-				decodeString("0"),
-				decodeString("b9d83d298d46c4dd73618f19a2a40084ce36476a"),
+		L1OriginMessage: &types.MessageL1ToL2{
+			FromAddress: types.HexToEthAddress("0x659a00c33263d9254Fed382dE81349426C795BB6"),
+			Payload: []types.Felt{
+				types.HexToFelt("0x68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+				types.HexToFelt("0x2616da7c393d14000"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0xb9d83d298d46c4dd73618f19a2a40084ce36476a"),
 			},
 		},
-		Events: []*transaction.Event{
+		Events: []types.Event{
 			{
-				FromAddress: decodeString("da114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"),
-				Keys: [][]byte{
-					decodeString("99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"),
+				FromAddress: types.HexToAddress("0xda114221cb83fa859dbdb4c44beeaa0bb37c7537ad5ae66fe5e0efd20e6eb3"),
+				Keys: []types.Felt{
+					types.HexToFelt("99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9"),
 				},
-				Data: [][]byte{
-					decodeString("0"),
-					decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-					decodeString("2616da7c393d14000"),
-					decodeString("0"),
+				Data: []types.Felt{
+					types.HexToFelt("0"),
+					types.HexToFelt("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+					types.HexToFelt("2616da7c393d14000"),
+					types.HexToFelt("0"),
 				},
 			},
 			{
-				FromAddress: decodeString("1108cdbe5d82737b9057590adaf97d34e74b5452f0628161d237746b6fe69e"),
-				Keys: [][]byte{
-					decodeString("221e5a5008f7a28564f0eaa32cdeb0848d10657c449aed3e15d12150a7c2db3"),
+				FromAddress: types.HexToAddress("0x1108cdbe5d82737b9057590adaf97d34e74b5452f0628161d237746b6fe69e"),
+				Keys: []types.Felt{
+					types.HexToFelt("0x221e5a5008f7a28564f0eaa32cdeb0848d10657c449aed3e15d12150a7c2db3"),
 				},
-				Data: [][]byte{
-					decodeString("68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
-					decodeString("2616da7c393d14000"),
-					decodeString("0"),
+				Data: []types.Felt{
+					types.HexToFelt("0x68a443797ed3eb691347e1d69e6480d1c3ad37acb0d6b1d17c311600002f3d6"),
+					types.HexToFelt("0x2616da7c393d14000"),
+					types.HexToFelt("0x0"),
 				},
 			},
 		},
@@ -192,21 +183,9 @@ func TestManager_GetReceipt(t *testing.T) {
 	for _, receipt := range receipts {
 		outReceipt := TransactionService.GetReceipt(receipt.TxHash)
 
-		if !equalMessage(t, receipt, outReceipt) {
+		if !reflect.DeepEqual(receipt, outReceipt) {
 			t.Errorf("receipt not equal after Put/Get operations")
 		}
 	}
 	TransactionService.Close(context.Background())
 }
-
-func equalMessage(t *testing.T, a, b proto.Message) bool {
-	aRaw, err := proto.Marshal(a)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	bRaw, err := proto.Marshal(b)
-	if err != nil {
-		t.Errorf("marshal error: %s", err)
-	}
-	return bytes.Compare(aRaw, bRaw) == 0
-}
diff --git a/pkg/common/hex_test.go b/pkg/common/hex_test.go
new file mode 100644
index 00000000..4acfb419
--- /dev/null
+++ b/pkg/common/hex_test.go
@@ -0,0 +1,29 @@
+package common
+
+import "testing"
+
+func TestIsHex(t *testing.T) {
+	tests := [...]struct {
+		Input string
+		Want  bool
+	}{
+		{
+			"0x7932de7ec535bfd45e2951a35c06e13d22188cb7eb7b7cc43454ee63df78aff", true,
+		},
+		{
+			"0x8fccde9ae0ca4da714b8f27d4aaf6edfa8ce92a4", true,
+		},
+		{
+			"0x0", true,
+		},
+		{
+			"0xzxc", false,
+		},
+	}
+	for _, test := range tests {
+		out := IsHex(test.Input)
+		if out != test.Want {
+			t.Errorf("IsHex(%s) = %v, want: %v", test.Input, out, test.Want)
+		}
+	}
+}
diff --git a/pkg/rpc/api_test.go b/pkg/rpc/api_test.go
new file mode 100644
index 00000000..67d02e6c
--- /dev/null
+++ b/pkg/rpc/api_test.go
@@ -0,0 +1,596 @@
+package rpc
+
+import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"testing"
+
+	"github.com/NethermindEth/juno/internal/db"
+	"github.com/NethermindEth/juno/internal/db/abi"
+	"github.com/NethermindEth/juno/internal/db/state"
+	"github.com/NethermindEth/juno/internal/services"
+	"github.com/NethermindEth/juno/pkg/types"
+)
+
+var (
+	testFelt1 types.Felt = types.HexToFelt("0x0000000000000000000000000000000000000000000000000000000000000001")
+	testFelt2 types.Felt = types.HexToFelt("0x0000000000000000000000000000000000000000000000000000000000000002")
+	testFelt3 types.Felt = types.HexToFelt("0x0000000000000000000000000000000000000000000000000000000000000003")
+	testFelt4 types.Felt = types.HexToFelt("0x0000000000000000000000000000000000000000000000000000000000000004")
+)
+
+func buildRequest(method string, params ...interface{}) string {
+	request := struct {
+		Jsonrpc string        `json:"jsonrpc"`
+		Method  string        `json:"method"`
+		Params  []interface{} `json:"params"`
+		ID      int           `json:"id"`
+	}{
+		Jsonrpc: "2.0",
+		Method:  method,
+		Params:  params,
+		ID:      1,
+	}
+	buf := new(bytes.Buffer)
+	json.NewEncoder(buf).Encode(request)
+	return buf.String()
+}
+
+func buildResponse(result interface{}) string {
+	response := struct {
+		Jsonrpc string      `json:"jsonrpc"`
+		Result  interface{} `json:"result"`
+		ID      int         `json:"id"`
+	}{
+		Jsonrpc: "2.0",
+		Result:  result,
+		ID:      1,
+	}
+	buf := new(bytes.Buffer)
+	json.NewEncoder(buf).Encode(response)
+	return buf.String()
+}
+
+func TestStarknetGetStorageAt(t *testing.T) {
+	// setup
+	services.BlockService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.BlockService.Run(); err != nil {
+		t.Fatalf("unexpected error starting block service: %s", err)
+	}
+	defer services.BlockService.Close(context.Background())
+
+	blockHash := types.BlockHash(testFelt1)
+	blockNumber := uint64(2175)
+	services.BlockService.StoreBlock(blockHash, &types.Block{
+		BlockHash:       blockHash,
+		BlockNumber:     blockNumber,
+		ParentHash:      types.HexToBlockHash("0xf8fe26de3ce9ee4d543b1152deb2ce549e589524d79598227761d6006b74a9"),
+		Status:          types.BlockStatusAcceptedOnL2,
+		Sequencer:       types.HexToAddress("0x0"),
+		NewRoot:         types.HexToFelt("6a42d697b5b735eef03bb71841ed5099d57088f7b5eec8e356fe2601d5ba08f"),
+		OldRoot:         types.HexToFelt("1d932dcf7da6c4f7605117cf514d953147161ab2d8f762dcebbb6dad427e519"),
+		AcceptedTime:    1652492749,
+		TimeStamp:       1652488132,
+		TxCount:         2,
+		TxCommitment:    types.HexToFelt("0x0"),
+		EventCount:      19,
+		EventCommitment: types.HexToFelt("0x0"),
+		TxHashes: []types.TransactionHash{
+			types.HexToTransactionHash("0x5ce76214481ebb29f912cb5d31abdff34fd42217f5ece9dda76d9fcfd62dc73"),
+			types.HexToTransactionHash("0x4ff16b7673da1f4c4b114d28e0e1a366bd61b702eca3e21882da6c8939e60a2"),
+		},
+	})
+
+	services.StateService.Setup(
+		db.NewKeyValueDb(t.TempDir(), 0),
+		db.NewBlockSpecificDatabase(db.NewKeyValueDb(t.TempDir(), 0)),
+	)
+	if err := services.StateService.Run(); err != nil {
+		t.Fatalf("unexpected error starting state service: %s", err)
+	}
+	defer services.StateService.Close(context.Background())
+
+	address := testFelt2
+	key := testFelt3
+	value := testFelt4
+	storage := &state.Storage{
+		Storage: map[string]string{
+			key.Hex(): value.Hex(),
+		},
+	}
+	services.StateService.StoreStorage(address.Hex(), blockNumber, storage)
+
+	// test
+	testServer(t, []rpcTest{
+		{
+			Request:  buildRequest("starknet_getStorageAt", address.Hex(), key.Hex(), blockHash.Felt().String()),
+			Response: buildResponse(Felt(value.Hex())),
+		},
+	})
+}
+
+func TestStarknetGetCode(t *testing.T) {
+	// setup
+	services.AbiService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.AbiService.Run(); err != nil {
+		t.Fatalf("unexpected error starting abi service: %s", err)
+	}
+	defer services.AbiService.Close(context.Background())
+
+	address := types.Address(testFelt2)
+	abi := &abi.Abi{
+		Functions: []*abi.Function{
+			{
+				Name: "initialize",
+				Inputs: []*abi.Function_Input{
+					{
+						Name: "signer",
+						Type: "felt",
+					},
+					{
+						Name: "guardian",
+						Type: "felt",
+					},
+				},
+				Outputs: nil,
+			},
+		},
+		Events: []*abi.AbiEvent{
+			{
+				Data: []*abi.AbiEvent_Data{
+					{
+						Name: "new_signer",
+						Type: "felt",
+					},
+				},
+				Keys: nil,
+				Name: "signer_changed",
+			},
+		},
+		Structs: []*abi.Struct{
+			{
+				Fields: []*abi.Struct_Field{
+					{
+						Name:   "to",
+						Type:   "felt",
+						Offset: 0,
+					},
+					{
+						Name:   "selector",
+						Type:   "felt",
+						Offset: 1,
+					},
+					{
+						Name:   "data_offset",
+						Type:   "felt",
+						Offset: 2,
+					},
+					{
+						Name:   "data_len",
+						Type:   "felt",
+						Offset: 3,
+					},
+				},
+				Name: "CallArray",
+				Size: 4,
+			},
+		},
+		L1Handlers: []*abi.Function{
+			{
+				Name: "__l1_default__",
+				Inputs: []*abi.Function_Input{
+					{
+						Name: "selector",
+						Type: "felt",
+					},
+				},
+				Outputs: nil,
+			},
+		},
+		Constructor: &abi.Function{
+			Name: "constructor",
+			Inputs: []*abi.Function_Input{
+				{
+					Name: "signer",
+					Type: "felt",
+				},
+			},
+			Outputs: nil,
+		},
+	}
+
+	services.StateService.Setup(
+		db.NewKeyValueDb(t.TempDir(), 0),
+		db.NewBlockSpecificDatabase(db.NewKeyValueDb(t.TempDir(), 0)),
+	)
+	if err := services.StateService.Run(); err != nil {
+		t.Fatalf("unexpected error starting state service: %s", err)
+	}
+
+	services.AbiService.StoreAbi(address.Hex(), abi)
+
+	defer services.StateService.Close(context.Background())
+
+	code := &state.Code{
+		Code: [][]byte{
+			types.HexToFelt("0x1111").Bytes(),
+			types.HexToFelt("0x1112").Bytes(),
+			types.HexToFelt("0x1113").Bytes(),
+			types.HexToFelt("0x1114").Bytes(),
+		},
+	}
+	services.StateService.StoreCode(address.Bytes(), code)
+
+	abiResponse, _ := json.Marshal(abi)
+	codeResponse := make([]Felt, len(code.Code))
+	for i, bcode := range code.Code {
+		codeResponse[i] = Felt(types.BytesToFelt(bcode).Hex())
+	}
+
+	// test
+	testServer(t, []rpcTest{
+		{
+			Request:  buildRequest("starknet_getCode", address.Hex()),
+			Response: buildResponse(CodeResult{Bytecode: codeResponse, Abi: string(abiResponse)}),
+		},
+	})
+}
+
+var (
+	txns = []types.IsTransaction{
+		&types.TransactionInvoke{
+			Hash:               types.HexToTransactionHash("0x4e0e3a35c2d99fce89e0583d042f98cf71d1384554b5771694956a62b9f05fd"),
+			ContractAddress:    types.HexToAddress("0x3585c0f52144b1db7bfb8f644182324704c2ac1cc3470957bd097ead7ef2aa4"),
+			EntryPointSelector: types.HexToFelt("0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"),
+			CallData: []types.Felt{
+				types.HexToFelt("0x1"),
+				types.HexToFelt("0x6a09ccb1caaecf3d9683efe335a667b2169a409d19c589ba1eb771cd210af75"),
+				types.HexToFelt("0x2f0b3c5710379609eb5495f1ecd348cb28167711b73609fe565a72734550354"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0x3"),
+				types.HexToFelt("0x3"),
+				types.HexToFelt("0x3585c0f52144b1db7bfb8f644182324704c2ac1cc3470957bd097ead7ef2aa4"),
+				types.HexToFelt("0x2c72fe176204c4f35400000"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0x0"),
+			},
+			Signature: []types.Felt{
+				types.HexToFelt("0x6cc4394a1db343b0f1a391e697469a96e9da126d41c8d360b9356d4396b0734"),
+				types.HexToFelt("0x5cd4c66b0e8a60d28a2b0af658432555df36fd7a7eeb6b146bd3b603e088945"),
+			},
+			MaxFee: types.HexToFelt("0x0"),
+		},
+		&types.TransactionInvoke{
+			Hash:               types.HexToTransactionHash("0x6687ec12afde961e106b3b8f060f268cb29e6d70e2dbec38d7a6ef7b6c1f846"),
+			ContractAddress:    types.HexToAddress("0x433d2313d0995a41089ecbc91f6b3fd262119dbb6d10194f995612e2da20993"),
+			EntryPointSelector: types.HexToFelt("0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"),
+			CallData: []types.Felt{
+				types.HexToFelt("0x1"),
+				types.HexToFelt("0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82"),
+				types.HexToFelt("0xe48e45e0642d5f170bb832c637926f4c85b77d555848b693304600c4275f26"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0x3"),
+				types.HexToFelt("0x3"),
+				types.HexToFelt("0x49d2db5f6c17a5a2894f52125048aaa988850009"),
+				types.HexToFelt("0x2c72fe176204c4f35400000"),
+				types.HexToFelt("0x0"),
+				types.HexToFelt("0x0"),
+			},
+			Signature: []types.Felt{
+				types.HexToFelt("0x26807fb7cf476856c5c0592c66481e10ab8bb256b1eb7553c18d2734629feff"),
+				types.HexToFelt("0x6b8b8d99a8c988fa58582c76b4867c856aa0dd2c6f8ae8b5b76965f12305f98"),
+			},
+			MaxFee: types.HexToFelt("0x0"),
+		},
+		&types.TransactionDeploy{
+			Hash:            types.HexToTransactionHash("0x12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
+			ContractAddress: types.HexToAddress("0x31c9cdb9b00cb35cf31c05855c0ec3ecf6f7952a1ce6e3c53c3455fcd75a280"),
+			ConstructorCallData: []types.Felt{
+				types.HexToFelt("0xcfc2e2866fd08bfb4ac73b70e0c136e326ae18fc797a2c090c8811c695577e"),
+				types.HexToFelt("0x5f1dd5a5aef88e0498eeca4e7b2ea0fa7110608c11531278742f0b5499af4b3"),
+			},
+		},
+	}
+	receipts = []*types.TransactionReceipt{
+		{
+			TxHash:     types.HexToTransactionHash("0x4e0e3a35c2d99fce89e0583d042f98cf71d1384554b5771694956a62b9f05fd"),
+			ActualFee:  types.HexToFelt("0x0"),
+			Status:     types.TxStatusAcceptedOnL1,
+			StatusData: "",
+			Events: []types.Event{
+				{
+					FromAddress: types.HexToAddress("0x03585c0f52144b1db7bfb8f644182324704c2ac1cc3470957bd097ead7ef2aa4"),
+					Keys: []types.Felt{
+						types.HexToFelt("0x5ad857f66a5b55f1301ff1ed7e098ac6d4433148f0b72ebc4a2945ab85ad53"),
+					},
+					Data: []types.Felt{
+						types.HexToFelt("0x4e0e3a35c2d99fce89e0583d042f98cf71d1384554b5771694956a62b9f05fd"),
+						types.HexToFelt("0x0"),
+					},
+				},
+			},
+		},
+		{
+			TxHash:     types.HexToTransactionHash("0x6687ec12afde961e106b3b8f060f268cb29e6d70e2dbec38d7a6ef7b6c1f846"),
+			ActualFee:  types.HexToFelt("0x0"),
+			Status:     types.TxStatusAcceptedOnL2,
+			StatusData: "",
+			MessagesSent: []types.MessageL2ToL1{
+				{
+					ToAddress: types.HexToEthAddress("0xae0Ee0A63A2cE6BaeEFFE56e7714FB4EFE48D419"),
+					Payload: []types.Felt{
+						types.HexToFelt("0x0"),
+						types.HexToFelt("0x49d2db5f6c17a5a2894f52125048aaa988850009"),
+						types.HexToFelt("0x2386f26fc10000"),
+						types.HexToFelt("0x0"),
+					},
+				},
+			},
+			L1OriginMessage: nil,
+			Events: []types.Event{
+				{
+					FromAddress: types.HexToAddress("0x73314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82"),
+					Keys: []types.Felt{
+						types.HexToFelt("0x194fc63c49b0f07c8e7a78476844837255213824bd6cb81e0ccfb949921aad1"),
+					},
+					Data: []types.Felt{
+						types.HexToFelt("0x49d2db5f6c17a5a2894f52125048aaa988850009"),
+						types.HexToFelt("0x2386f26fc10000"),
+						types.HexToFelt("0x0"),
+					},
+				},
+				{
+					FromAddress: types.HexToAddress("0x433d2313d0995a41089ecbc91f6b3fd262119dbb6d10194f995612e2da20993"),
+					Keys: []types.Felt{
+						types.HexToFelt("0x433d2313d0995a41089ecbc91f6b3fd262119dbb6d10194f995612e2da20993"),
+					},
+					Data: []types.Felt{
+						types.HexToFelt("0x433d2313d0995a41089ecbc91f6b3fd262119dbb6d10194f995612e2da20993"),
+						types.HexToFelt("0x0"),
+					},
+				},
+			},
+		},
+		{
+			TxHash:     types.HexToTransactionHash("0x12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
+			ActualFee:  types.HexToFelt("0x0"),
+			Status:     types.TxStatusAcceptedOnL1,
+			StatusData: "",
+			L1OriginMessage: &types.MessageL1ToL2{
+				FromAddress: types.HexToEthAddress("0xae0ee0a63a2ce6baeeffe56e7714fb4efe48d419"),
+				Payload: []types.Felt{
+					types.HexToFelt("0xc"),
+					types.HexToFelt("0x22"),
+				},
+			},
+		},
+	}
+	blocks = []types.Block{
+		{
+			BlockHash:    types.HexToBlockHash("0x1cf10396b725510b794f03d90bd670d463747ece48c94e36ac9e04b9ec122b6"),
+			ParentHash:   types.HexToBlockHash("0x2439288f35c3da4a8aa3f689ddcf6f83fd9bdc9357c04d12265501e68e14d64"),
+			BlockNumber:  2591,
+			Status:       types.BlockStatusAcceptedOnL1,
+			Sequencer:    types.HexToAddress("0x21f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"),
+			NewRoot:      types.HexToFelt("0166c5ae3f0f631a4d5543d447aa4cee6a975b2e83070d8986c94952aea10aae"),
+			OldRoot:      types.HexToFelt("06d8106c901208726feea70d1155d085aee27e63ae0368b9ecc1c3bf1b5d4fe8"),
+			AcceptedTime: 1654588282,
+			TimeStamp:    1654587282,
+			TxCount:      3,
+			TxHashes: []types.TransactionHash{
+				types.HexToTransactionHash("0x4e0e3a35c2d99fce89e0583d042f98cf71d1384554b5771694956a62b9f05fd"),
+				types.HexToTransactionHash("0x6687ec12afde961e106b3b8f060f268cb29e6d70e2dbec38d7a6ef7b6c1f846"),
+				types.HexToTransactionHash("0x12c96ae3c050771689eb261c9bf78fac2580708c7f1f3d69a9647d8be59f1e1"),
+			},
+			EventCount: 3,
+		},
+	}
+)
+
+func TestGetBlock(t *testing.T) {
+	// Initialize transaction service
+	services.TransactionService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.TransactionService.Run(); err != nil {
+		t.Fatalf("unexpected error starting the transaction service: %s", err)
+	}
+	defer services.TransactionService.Close(context.Background())
+	// Initialize block service
+	services.BlockService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.BlockService.Run(); err != nil {
+		t.Fatalf("unexpeceted error starting the block service: %s", err)
+	}
+	defer services.BlockService.Close(context.Background())
+	// Store transactions
+	for _, txn := range txns {
+		services.TransactionService.StoreTransaction(txn.GetHash(), txn)
+	}
+	// Store receipts
+	for _, receipt := range receipts {
+		services.TransactionService.StoreReceipt(receipt.TxHash, receipt)
+	}
+	// Store blocks
+	for _, block := range blocks {
+		services.BlockService.StoreBlock(block.BlockHash, &block)
+	}
+
+	type testItem struct {
+		Scope    RequestedScope
+		Response BlockResponse
+	}
+	// Test requests
+	response := make(map[string][]testItem)
+
+	response["0x1cf10396b725510b794f03d90bd670d463747ece48c94e36ac9e04b9ec122b6"] = []testItem{
+		{
+			Scope: ScopeTxnHash,
+			Response: BlockResponse{
+				BlockHash:    types.HexToBlockHash("0x1cf10396b725510b794f03d90bd670d463747ece48c94e36ac9e04b9ec122b6"),
+				ParentHash:   types.HexToBlockHash("0x2439288f35c3da4a8aa3f689ddcf6f83fd9bdc9357c04d12265501e68e14d64"),
+				BlockNumber:  2591,
+				Status:       types.BlockStatusAcceptedOnL1,
+				Sequencer:    types.HexToAddress("0x21f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"),
+				NewRoot:      types.HexToFelt("0166c5ae3f0f631a4d5543d447aa4cee6a975b2e83070d8986c94952aea10aae"),
+				OldRoot:      types.HexToFelt("06d8106c901208726feea70d1155d085aee27e63ae0368b9ecc1c3bf1b5d4fe8"),
+				AcceptedTime: 1654588282,
+				Transactions: []types.TransactionHash{
+					txns[0].GetHash(),
+					txns[1].GetHash(),
+					txns[2].GetHash(),
+				},
+			},
+		},
+		{
+			Scope: ScopeFullTxns,
+			Response: BlockResponse{
+				BlockHash:    types.HexToBlockHash("0x1cf10396b725510b794f03d90bd670d463747ece48c94e36ac9e04b9ec122b6"),
+				ParentHash:   types.HexToBlockHash("0x2439288f35c3da4a8aa3f689ddcf6f83fd9bdc9357c04d12265501e68e14d64"),
+				BlockNumber:  2591,
+				Status:       types.BlockStatusAcceptedOnL1,
+				Sequencer:    types.HexToAddress("0x21f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"),
+				NewRoot:      types.HexToFelt("0166c5ae3f0f631a4d5543d447aa4cee6a975b2e83070d8986c94952aea10aae"),
+				OldRoot:      types.HexToFelt("06d8106c901208726feea70d1155d085aee27e63ae0368b9ecc1c3bf1b5d4fe8"),
+				AcceptedTime: 1654588282,
+				Transactions: []*Txn{
+					NewTxn(txns[0]),
+					NewTxn(txns[1]),
+					NewTxn(txns[2]),
+				},
+			},
+		},
+		{
+			Scope: ScopeFullTxnAndReceipts,
+			Response: BlockResponse{
+				BlockHash:    types.HexToBlockHash("0x1cf10396b725510b794f03d90bd670d463747ece48c94e36ac9e04b9ec122b6"),
+				ParentHash:   types.HexToBlockHash("0x2439288f35c3da4a8aa3f689ddcf6f83fd9bdc9357c04d12265501e68e14d64"),
+				BlockNumber:  2591,
+				Status:       types.BlockStatusAcceptedOnL1,
+				Sequencer:    types.HexToAddress("0x21f4b90b0377c82bf330b7b5295820769e72d79d8acd0effa0ebde6e9988bc5"),
+				NewRoot:      types.HexToFelt("0166c5ae3f0f631a4d5543d447aa4cee6a975b2e83070d8986c94952aea10aae"),
+				OldRoot:      types.HexToFelt("06d8106c901208726feea70d1155d085aee27e63ae0368b9ecc1c3bf1b5d4fe8"),
+				AcceptedTime: 1654588282,
+				Transactions: []*TxnAndReceipt{
+					{
+						Txn:        *NewTxn(txns[0]),
+						TxnReceipt: *NewTxnReceipt(receipts[0]),
+					},
+					{
+						Txn:        *NewTxn(txns[1]),
+						TxnReceipt: *NewTxnReceipt(receipts[1]),
+					},
+					{
+						Txn:        *NewTxn(txns[2]),
+						TxnReceipt: *NewTxnReceipt(receipts[2]),
+					},
+				},
+			},
+		},
+	}
+
+	for _, block := range blocks {
+		blockTest := response[block.BlockHash.Hex()]
+		for _, item := range blockTest {
+			testServer(t, []rpcTest{
+				{
+					Request:  buildRequest("starknet_getBlockByHash", block.BlockHash, item.Scope),
+					Response: buildResponse(item.Response),
+				},
+				{
+					Request:  buildRequest("starknet_getBlockByNumber", block.BlockNumber, item.Scope),
+					Response: buildResponse(item.Response),
+				},
+			})
+		}
+	}
+}
+
+func TestGetTransactionByHash(t *testing.T) {
+	services.TransactionService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.TransactionService.Run(); err != nil {
+		t.Fatalf("unexpected error starting the transaction service: %s", err)
+	}
+	defer services.TransactionService.Close(context.Background())
+	responses := make(map[string]*Txn)
+	for _, txn := range txns {
+		services.TransactionService.StoreTransaction(txn.GetHash(), txn)
+		responses[txn.GetHash().String()] = NewTxn(txn)
+	}
+	for _, txn := range txns {
+		testServer(t, []rpcTest{
+			{
+				Request:  buildRequest("starknet_getTransactionByHash", txn.GetHash()),
+				Response: buildResponse(responses[txn.GetHash().String()]),
+			},
+		})
+	}
+}
+
+func TestGetTransactionByBlockHashAndIndex(t *testing.T) {
+	// Initialize transaction service
+	services.TransactionService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.TransactionService.Run(); err != nil {
+		t.Fatalf("unexpected error starting the transaction service: %s", err)
+	}
+	defer services.TransactionService.Close(context.Background())
+	// Initialize block service
+	services.BlockService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.BlockService.Run(); err != nil {
+		t.Fatalf("unexpeceted error starting the block service: %s", err)
+	}
+	defer services.BlockService.Close(context.Background())
+	// Store transactions
+	for _, txn := range txns {
+		services.TransactionService.StoreTransaction(txn.GetHash(), txn)
+	}
+	// Store blocks
+	for _, block := range blocks {
+		services.BlockService.StoreBlock(block.BlockHash, &block)
+	}
+	// Build test cases
+	tests := make([]rpcTest, 0)
+	for _, block := range blocks {
+		for i := range block.TxHashes {
+			tests = append(tests, rpcTest{
+				Request:  buildRequest("starknet_getTransactionByBlockHashAndIndex", block.BlockHash.Hex(), i),
+				Response: buildResponse(NewTxn(txns[i])),
+			})
+		}
+	}
+	// Run tests
+	testServer(t, tests)
+}
+
+func TestGetTransactionByBlockNumberAndIndex(t *testing.T) {
+	// Initialize transaction service
+	services.TransactionService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.TransactionService.Run(); err != nil {
+		t.Fatalf("unexpected error starting the transaction service: %s", err)
+	}
+	defer services.TransactionService.Close(context.Background())
+	// Initialize block service
+	services.BlockService.Setup(db.NewKeyValueDb(t.TempDir(), 0))
+	if err := services.BlockService.Run(); err != nil {
+		t.Fatalf("unexpeceted error starting the block service: %s", err)
+	}
+	defer services.BlockService.Close(context.Background())
+	// Store transactions
+	for _, txn := range txns {
+		services.TransactionService.StoreTransaction(txn.GetHash(), txn)
+	}
+	// Store blocks
+	for _, block := range blocks {
+		services.BlockService.StoreBlock(block.BlockHash, &block)
+	}
+	// Build test cases
+	tests := make([]rpcTest, 0)
+	for _, block := range blocks {
+		for i := range block.TxHashes {
+			tests = append(tests, rpcTest{
+				Request:  buildRequest("starknet_getTransactionByBlockNumberAndIndex", block.BlockNumber, i),
+				Response: buildResponse(NewTxn(txns[i])),
+			})
+		}
+	}
+	// Run tests
+	testServer(t, tests)
+}
diff --git a/pkg/rpc/rpc_test.go b/pkg/rpc/rpc_test.go
index 119e6b2b..03675394 100644
--- a/pkg/rpc/rpc_test.go
+++ b/pkg/rpc/rpc_test.go
@@ -38,7 +38,7 @@ func testServer(t *testing.T, tests []rpcTest) {
 		}
 		s := string(data)
 		if s != v.Response {
-			t.Errorf("expected %v, got %v", v.Response, string(data))
+			t.Errorf("expected `%v`, got `%v`", v.Response, string(data))
 			_ = res.Body.Close()
 		}
 		t.Log("Executed test ", i)
diff --git a/pkg/rpc/rpc_tests.json b/pkg/rpc/rpc_tests.json
index 8791f1b5..b33b7489 100644
--- a/pkg/rpc/rpc_tests.json
+++ b/pkg/rpc/rpc_tests.json
@@ -3,6 +3,10 @@
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"345\",\"method\":\"echo\",\"params\":[\"Hello Echo\"]}",
     "response": "{\"jsonrpc\":\"2.0\",\"result\":\"Hello Echo\",\"id\":\"345\"}\n"
   },
+  {
+    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"345\",\"method\":\"echoErr\",\"params\":[\"Hello EchoErr\"]}",
+    "response": "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":0,\"message\":\"Hello EchoErr\"},\"id\":\"345\"}\n"
+  },
   {
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"345\",\"method\":\"starknet_getEvents\",\"params\":[{\"fromBlock\":0,\"toBlock\":0,\"address\":\"\",\"keys\":null,\"page_size\":0,\"page_number\":0}]}",
     "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"EmittedEventArray\":null,\"page_number\":0},\"id\":\"345\"}\n"
@@ -19,54 +23,6 @@
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"34\",\"method\":\"starknet_call\",\"params\":9{[{\"calldata\":[\"0x1234\"],\"contract_address\":\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\",\n\"entry_point_selector\":\"0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320\"}, \"latest\"]}}",
     "response": "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32700,\"message\":\"Parse error.\"}}\n"
   },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"0\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"pending\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"pending\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"0\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"2\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"pending\",\"TXN_HASH\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"pending\",\"parent_hash\":\"TXN_HASH\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"2\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"pending\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"1\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"3\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"pending\",\"TXN_HASH\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"3\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"4\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"latest\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"latest\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"4\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"5\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"latest\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"5\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"6\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"latest\",\"TXN_HASH\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"latest\",\"parent_hash\":\"TXN_HASH\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"6\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"7\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"latest\",\"TXN_HASH\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"7\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"8\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"latest\",\"FULL_TXNS\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"latest\",\"parent_hash\":\"FULL_TXNS\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"8\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"9\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"latest\",\"FULL_TXNS\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"9\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"10\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"latest\",\"FULL_TXN_AND_RECEIPTS\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"latest\",\"parent_hash\":\"FULL_TXN_AND_RECEIPTS\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"10\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"11\",\"method\":\"starknet_getBlockByNumber\",\"params\":[\"latest\",\"FULL_TXN_AND_RECEIPTS\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"11\"}\n"
-  },
   {
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"39\",\"method\":\"starknet_protocolVersion\"}",
     "response": "{\"jsonrpc\":\"2.0\",\"result\":\"Here the Protocol Version\",\"id\":\"39\"}\n"
@@ -79,41 +35,13 @@
     "request": "[{\"jsonrpc\":\"2.0\",\"id\":\"34\",\"method\":\"starknet_call\",\"params\":[{\"calldata\":[\"0x1234\"],\"contract_address\":\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\",\n\"entry_point_selector\":\"0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320\"}, \"latest\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"35\",\"method\":\"starknet_call\",\"params\":[{\"calldata\":[\"0x1234\"],\"contract_address\":\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\",\n\"entry_point_selector\":\"0x362398bec32bc0ebb411203221a35a0301193a96f317ebe5e40be9f60d15320\"}, \"pending\"]}]",
     "response": "[{\"jsonrpc\":\"2.0\",\"result\":[\"Response\",\"of\",\"starknet_call\"],\"id\":\"34\"},{\"jsonrpc\":\"2.0\",\"result\":[\"Response\",\"of\",\"starknet_call\"],\"id\":\"35\"}]\n"
   },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"12\",\"method\":\"starknet_getBlockByHash\",\"params\":[\"0x7d328a71faf48c5c3857e99f20a77b18522480956d1cd5bff1ff2df3c8b427b\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"0x7d328a71faf48c5c3857e99f20a77b18522480956d1cd5bff1ff2df3c8b427b\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"12\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"13\",\"method\":\"starknet_getBlockByNumber\",\"params\":[41000]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"parent_hash\":\"\",\"block_number\":0,\"status\":\"\",\"sequencer\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"transactions\":null},\"id\":\"13\"}\n"
-  },
   {
     "request": "[{\"jsonrpc\":\"2.0\",\"id\":\"14\",\"method\":\"starknet_getStateUpdateByHash\",\"params\":[\"latest\"]},{\"jsonrpc\":\"2.0\",\"id\":\"15\",\"method\":\"starknet_getStateUpdateByHash\",\"params\":[\"0x7d328a71faf48c5c3857e99f20a77b18522480956d1cd5bff1ff2df3c8b427b\"]}]",
     "response": "[{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"state_diff\":{\"storage_diffs\":null,\"contracts\":null}},\"id\":\"14\"},{\"jsonrpc\":\"2.0\",\"result\":{\"block_hash\":\"\",\"new_root\":\"\",\"old_root\":\"\",\"accepted_time\":0,\"state_diff\":{\"storage_diffs\":null,\"contracts\":null}},\"id\":\"15\"}]\n"
   },
-  {
-    "request": "[{\"jsonrpc\":\"2.0\",\"id\":\"16\",\"method\":\"starknet_getStorageAt\",\"params\":[\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\", \"0x0206F38F7E4F15E87567361213C28F235CCCDAA1D7FD34C9DB1DFE9489C6A091\", \"latest\"]},{\"jsonrpc\":\"2.0\",\"id\":\"17\",\"method\":\"starknet_getStorageAt\",\"params\":[\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\", \"0x0206F38F7E4F15E87567361213C28F235CCCDAA1D7FD34C9DB1DFE9489C6A091\", \"pending\"]},{\"jsonrpc\":\"2.0\",\"id\":\"18\",\"method\":\"starknet_getStorageAt\",\"params\":[\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\", \"0x0206F38F7E4F15E87567361213C28F235CCCDAA1D7FD34C9DB1DFE9489C6A091\", \"0x3871c8a0c3555687515a07f365f6f5b1d8c2ae953f7844575b8bde2b2efed27\"]}]'",
-    "response": "[{\"jsonrpc\":\"2.0\",\"result\":\"Storage\",\"id\":\"16\"},{\"jsonrpc\":\"2.0\",\"result\":\"Storage\",\"id\":\"17\"},{\"jsonrpc\":\"2.0\",\"result\":\"Storage\",\"id\":\"18\"}]\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"19\",\"method\":\"starknet_getTransactionByHash\",\"params\":[\"0x74ec6667e6057becd3faff77d9ab14aecf5dde46edb7c599ee771f70f9e80ba\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"19\"}\n"
-  },
-  {
-    "request": "[{\"jsonrpc\":\"2.0\",\"id\":\"20\",\"method\":\"starknet_getTransactionByBlockHashAndIndex\",\"params\":[\"latest\", 0]},\n{\"jsonrpc\":\"2.0\",\"id\":\"21\",\"method\":\"starknet_getTransactionByBlockNumberAndIndex\",\"params\":[\"latest\", 0]},\n{\"jsonrpc\":\"2.0\",\"id\":\"22\",\"method\":\"starknet_getTransactionByBlockHashAndIndex\",\"params\":[\"pending\", 0]},\n{\"jsonrpc\":\"2.0\",\"id\":\"23\",\"method\":\"starknet_getTransactionByBlockNumberAndIndex\",\"params\":[\"pending\", 0]},\n{\"jsonrpc\":\"2.0\",\"id\":\"24\",\"method\":\"starknet_getTransactionByBlockHashAndIndex\",\"params\":[\"0x3871c8a0c3555687515a07f365f6f5b1d8c2ae953f7844575b8bde2b2efed27\", 4]},\n{\"jsonrpc\":\"2.0\",\"id\":\"25\",\"method\":\"starknet_getTransactionByBlockNumberAndIndex\",\"params\":[21348, 4]}]",
-    "response": "[{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"20\"},{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"21\"},{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"22\"},{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"23\"},{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"24\"},{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"25\"}]\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"22\",\"method\":\"starknet_getTransactionByBlockNumberAndIndex\",\"params\":[\"pending\", 0]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"contract_address\":\"\",\"entry_point_selector\":\"\",\"calldata\":null,\"txn_hash\":\"\"},\"id\":\"22\"}\n"
-  },
   {
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"26\",\"method\":\"starknet_getTransactionReceipt\",\"params\":[\"0x74ec6667e6057becd3faff77d9ab14aecf5dde46edb7c599ee771f70f9e80ba\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"txn_hash\":\"\",\"status\":\"\",\"status_data\":\"\",\"messages_sent\":null,\"l1_origin_message\":{\"from_address\":\"\",\"payload\":null},\"events\":{\"keys\":null,\"data\":null,\"from_address\":\"\"}},\"id\":\"26\"}\n"
-  },
-  {
-    "request": "{\"jsonrpc\":\"2.0\",\"id\":\"27\",\"method\":\"starknet_getCode\",\"params\":[\"0x6fbd460228d843b7fbef670ff15607bf72e19fa94de21e29811ada167b4ca39\"]}",
-    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"bytecode\":null,\"abi\":\"\"},\"id\":\"27\"}\n"
+    "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"txn_hash\":\"0x0\"},\"id\":\"26\"}\n"
   },
   {
     "request": "[{\"jsonrpc\":\"2.0\",\"id\":\"28\",\"method\":\"starknet_getBlockTransactionCountByHash\",\"params\":[\"latest\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"29\",\"method\":\"starknet_getBlockTransactionCountByNumber\",\"params\":[\"latest\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"30\",\"method\":\"starknet_getBlockTransactionCountByHash\",\"params\":[\"pending\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"31\",\"method\":\"starknet_getBlockTransactionCountByNumber\",\"params\":[\"pending\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"32\",\"method\":\"starknet_getBlockTransactionCountByHash\",\"params\":[\"0x3871c8a0c3555687515a07f365f6f5b1d8c2ae953f7844575b8bde2b2efed27\"]},\n{\"jsonrpc\":\"2.0\",\"id\":\"33\",\"method\":\"starknet_getBlockTransactionCountByNumber\",\"params\":[21348]}]",
@@ -139,4 +67,4 @@
     "request": "{\"jsonrpc\":\"2.0\",\"id\":\"40\",\"method\":\"starknet_syncing\"}",
     "response": "{\"jsonrpc\":\"2.0\",\"result\":{\"starting_block\":\"\",\"current_block\":\"\",\"highest_block\":\"\"},\"id\":\"40\"}\n"
   }
-]
\ No newline at end of file
+]
diff --git a/pkg/common/felt_test.go b/pkg/types/felt_test.go
similarity index 88%
rename from pkg/common/felt_test.go
rename to pkg/types/felt_test.go
index ad3e2c8e..9b6644bc 100644
--- a/pkg/common/felt_test.go
+++ b/pkg/types/felt_test.go
@@ -1,4 +1,4 @@
-package common
+package types
 
 import (
 	"bytes"
@@ -248,3 +248,40 @@ func TestFelt_MarshalJSON(t *testing.T) {
 		}
 	}
 }
+
+func TestFelt_UnmarshalJSON(t *testing.T) {
+	type TestCase struct {
+		Input []byte
+		Want  Felt
+	}
+	tests := [...]TestCase{
+		{
+			[]byte("\"0xa\""),
+			[FeltLength]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10},
+		},
+		{
+			[]byte("\"0xc1e3718ac229397d192530c0ca37982fd6609a2b9efa2fbc09c1df983f43d225\""),
+			[FeltLength]byte{193, 227, 113, 138, 194, 41, 57, 125, 25, 37, 48, 192, 202, 55, 152, 47, 214, 96, 154, 43, 158, 250, 47, 188, 9, 193, 223, 152, 63, 67, 210, 37},
+		},
+	}
+	for _, test := range tests {
+		f := Felt{}
+		err := json.Unmarshal(test.Input, &f)
+		if err != nil {
+			t.Error(fmt.Errorf("json.Unmarshal(%s): %w", string(test.Input), err))
+		}
+		if bytes.Compare(f.Bytes(), test.Want.Bytes()) != 0 {
+			t.Errorf("json.Unmarshal(%s) = %s, want: %s", string(test.Input), f, test.Want)
+		}
+	}
+
+	f := Felt{}
+	err := json.Unmarshal([]byte("\"0x23r\""), &f)
+	if err == nil {
+		t.Error("unexpected nil error, want: \"invalid hexadecimal string\"")
+	}
+	err = json.Unmarshal([]byte("{}"), &f)
+	if err == nil {
+		t.Error("unexpected nil error, want: \"unexpected token type\"")
+	}
+}