Repository: trustwallet/blockatlas (go)

Issue:
[Zilliqa] 413 error when requesting large blocks
## Summary of Bug

```
ERRO[0171] Client Errors                                 fingerprint="[client_errors]" tags="[{status_code 413} {host api.zilliqa.com} {path }]" url="https://api.zilliqa.com"
WARN[0171] retry GetBlockByNumber                        attempts=4 number=1002025 sleep=5.205359519s symbol=ZIL
ERRO[0176] Client Errors                                 fingerprint="[client_errors]" tags="[{status_code 413} {host api.zilliqa.com} {path }]" url="https://api.zilliqa.com"
WARN[0177] retry GetBlockByNumber                        attempts=3 number=1002025 sleep=11.17723038s symbol=ZIL
ERRO[0188] Client Errors                                 fingerprint="[client_errors]" tags="[{status_code 413} {host api.zilliqa.com} {path }]" url="https://api.zilliqa.com"
WARN[0189] retry GetBlockByNumber                        attempts=2 number=1002025 sleep=32.644065751s symbol=ZIL
```

Possible solutions:

Set limit for JSON RPC batch request, instead of sending 1K commands, set to 100, but need to test this assumption


Pull request:
[Zilliqa] small batch json rpc requests
Fixes #1406

Changes: 

1. refactor rpc/viewblock sub package
2. break batch json rpc requests into smaller batches

Patch:
diff --git a/go.mod b/go.mod
index 66688db..7d66f33 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,7 @@ require (
 	github.com/stretchr/testify v1.7.0
 	github.com/swaggo/gin-swagger v1.3.0
 	github.com/swaggo/swag v1.7.0
-	github.com/trustwallet/golibs v0.1.1
+	github.com/trustwallet/golibs v0.1.2
 	github.com/trustwallet/golibs/network v0.0.0-20210124080535-8638b407c4ab
 	golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
 	golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
diff --git a/go.sum b/go.sum
index 52df5a1..8b34dc8 100644
--- a/go.sum
+++ b/go.sum
@@ -432,8 +432,8 @@ github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6A
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/trustwallet/golibs v0.0.35 h1:VqWInO5s4Zg6XyCmqro8/xrEViOedZ3oYtBq/kn4fBg=
 github.com/trustwallet/golibs v0.0.35/go.mod h1:VI0APImKAYxvJ8MnMD6aHSKvnArXUd3NgNEghX5P+K8=
-github.com/trustwallet/golibs v0.1.1 h1:ZV5zbPbGD/dHcX3jnIcmdq24Llou230xYGHhK2WVvWQ=
-github.com/trustwallet/golibs v0.1.1/go.mod h1:SQ3mijAuOTX9GRyaqph3NY2ox04JD5WQ6PkWqX2NB8w=
+github.com/trustwallet/golibs v0.1.2 h1:YitiMP/RzQUJo+ZAJRjBh8BBl5bf+ZJV2hqiZiUMQSw=
+github.com/trustwallet/golibs v0.1.2/go.mod h1:SQ3mijAuOTX9GRyaqph3NY2ox04JD5WQ6PkWqX2NB8w=
 github.com/trustwallet/golibs/network v0.0.0-20210124080535-8638b407c4ab h1:djqS58OXHs6tkRoCyuPnMu5q5woQj/1bxUFnSN0Xlew=
 github.com/trustwallet/golibs/network v0.0.0-20210124080535-8638b407c4ab/go.mod h1:LDMLFnOnwmC30WuCCIJ56TWeXxwCVcrMFJYeC6GEnxY=
 github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
@@ -654,8 +654,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/postgres v1.0.7 h1:uCVjh1w7DSZ20Duo10JadA+1a0OZpgJk/o/z8pFpNQs=
-gorm.io/driver/postgres v1.0.7/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
+gorm.io/driver/postgres v1.0.8 h1:PAgM+PaHOSAeroTjHkCHCBIHHoBIf9RgPWGo8dF2DA8=
 gorm.io/driver/postgres v1.0.8/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
 gorm.io/gorm v1.20.12 h1:ebZ5KrSHzet+sqOCVdH9mTjW91L298nX3v5lVxAzSUY=
 gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
diff --git a/platform/vechain/transaction.go b/platform/vechain/transaction.go
index 3c2b978..b449ffe 100644
--- a/platform/vechain/transaction.go
+++ b/platform/vechain/transaction.go
@@ -5,8 +5,6 @@ import (
 	"strconv"
 	"sync"
 
-	log "github.com/sirupsen/logrus"
-
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/golibs/address"
 	"github.com/trustwallet/golibs/coin"
@@ -52,12 +50,7 @@ func (p *Platform) getTransactionsByIDs(ids []string) chan types.TxPage {
 		wg.Add(1)
 		go func(i string, c chan types.TxPage) {
 			defer wg.Done()
-			err := p.getTransactionChannel(i, c)
-			if err != nil {
-				log.WithFields(log.Fields{
-					"hash": i,
-				}).Error("Vechain Tx error")
-			}
+			_ = p.getTransactionChannel(i, c)
 		}(id, txChan)
 	}
 	wg.Wait()
diff --git a/platform/zilliqa/base.go b/platform/zilliqa/base.go
index 0ebacba..7f4b875 100644
--- a/platform/zilliqa/base.go
+++ b/platform/zilliqa/base.go
@@ -1,22 +1,21 @@
 package zilliqa
 
 import (
-	"github.com/trustwallet/golibs/client"
+	"github.com/trustwallet/blockatlas/platform/zilliqa/rpc"
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 	"github.com/trustwallet/golibs/coin"
-	"github.com/trustwallet/golibs/network/middleware"
 )
 
 type Platform struct {
-	client    Client
-	rpcClient RpcClient
+	client    viewblock.Client
+	rpcClient rpc.Client
 }
 
-func Init(api, apiKey, rpc string) *Platform {
+func Init(api, apiKey, rpcUrl string) *Platform {
 	p := &Platform{
-		client:    Client{client.InitClient(api, middleware.SentryErrorHandler)},
-		rpcClient: RpcClient{client.InitClient(rpc, middleware.SentryErrorHandler)},
+		client:    viewblock.InitClient(api, apiKey),
+		rpcClient: rpc.InitClient(rpcUrl),
 	}
-	p.client.Headers["X-APIKEY"] = apiKey
 	return p
 }
 
diff --git a/platform/zilliqa/block.go b/platform/zilliqa/block.go
index 91022a7..8cc60c5 100644
--- a/platform/zilliqa/block.go
+++ b/platform/zilliqa/block.go
@@ -3,6 +3,7 @@ package zilliqa
 import (
 	"strconv"
 
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 	"github.com/trustwallet/golibs/types"
 )
 
@@ -21,11 +22,19 @@ func (p *Platform) CurrentBlockNumber() (int64, error) {
 
 func (p *Platform) GetBlockByNumber(num int64) (*types.Block, error) {
 	var normalized []types.Tx
-	txs, err := p.rpcClient.GetTxInBlock(num)
+	var txs []viewblock.Tx
+
+	header, rpcTxs, err := p.rpcClient.GetTxInBlock(num)
 	if err != nil {
 		return nil, err
 	}
 
+	for _, tx := range rpcTxs {
+		if tx := TxFromRpc(tx, header); tx != nil {
+			txs = append(txs, *tx)
+		}
+	}
+
 	for _, srcTx := range txs {
 		tx := Normalize(&srcTx)
 		normalized = append(normalized, tx)
diff --git a/platform/zilliqa/model.go b/platform/zilliqa/model.go
index 1084279..43fd8ef 100644
--- a/platform/zilliqa/model.go
+++ b/platform/zilliqa/model.go
@@ -5,72 +5,11 @@ import (
 	"math/big"
 	"strconv"
 
-	"github.com/trustwallet/golibs/client"
+	"github.com/trustwallet/blockatlas/platform/zilliqa/rpc"
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 )
 
-type BlockTxs [][]string
-
-func (b BlockTxs) txs() []string {
-	txs := make([]string, 0)
-	for _, ids := range b {
-		txs = append(txs, ids...)
-	}
-	return txs
-}
-
-type Tx struct {
-	Hash           string      `json:"hash"`
-	BlockHeight    uint64      `json:"blockHeight"`
-	From           string      `json:"from"`
-	To             string      `json:"to"`
-	Value          string      `json:"value"`
-	Fee            string      `json:"fee"`
-	Timestamp      int64       `json:"timestamp"`
-	Signature      string      `json:"signature"`
-	Nonce          interface{} `json:"nonce"`
-	ReceiptSuccess bool        `json:"receiptSuccess"`
-}
-
-func (tx Tx) NonceValue() uint64 {
-	switch n := tx.Nonce.(type) {
-	case string:
-		r, err := strconv.Atoi(n)
-		if err != nil {
-			break
-		}
-		return uint64(r)
-	case int:
-		return uint64(n)
-	case float64:
-		return uint64(n)
-	}
-	return 0
-}
-
-type ChainInfo struct {
-	NumTxBlocks string `json:"NumTxBlocks"`
-}
-
-type TxReceipt struct {
-	CumulativeGas string `json:"cumulative_gas"`
-	EpochNum      string `json:"epoch_num"`
-	Success       bool   `json:"success"`
-}
-
-type TxRPC struct {
-	ID           string    `json:"ID"`
-	Amount       string    `json:"amount"`
-	GasLimit     string    `json:"gasLimit"`
-	GasPrice     string    `json:"gasPrice"`
-	Nonce        string    `json:"nonce"`
-	Receipt      TxReceipt `json:"receipt"`
-	SenderPubKey string    `json:"senderPubKey"`
-	Signature    string    `json:"signature"`
-	ToAddr       string    `json:"toAddr"`
-	Version      string    `json:"version"`
-}
-
-func (t *TxRPC) toTx(header BlockHeader) *Tx {
+func TxFromRpc(t rpc.Tx, header rpc.BlockHeader) *viewblock.Tx {
 	// t.recipient is not parsed correctly. Empty strings.
 
 	to, err := hex.DecodeString(t.ToAddr)
@@ -98,7 +37,7 @@ func (t *TxRPC) toTx(header BlockHeader) *Tx {
 	}
 	fee := new(big.Int).Mul(gasLimit, gasPrice)
 
-	return &Tx{
+	return &viewblock.Tx{
 		Hash:           "0x" + t.ID,
 		BlockHeight:    height,
 		From:           EncodePublicKeyToAddress(t.SenderPubKey),
@@ -111,33 +50,3 @@ func (t *TxRPC) toTx(header BlockHeader) *Tx {
 		ReceiptSuccess: t.Receipt.Success,
 	}
 }
-
-type BlockTxRpc struct {
-	JsonRpc string           `json:"jsonrpc"`
-	Error   *client.RpcError `json:"error,omitempty"`
-	Result  BlockTxs         `json:"result,omitempty"`
-	Id      string           `json:"id,omitempty"`
-}
-
-type HashesResponse struct {
-	ID      int        `json:"id"`
-	Jsonrpc string     `json:"jsonrpc"`
-	Result  [][]string `json:"result"`
-}
-
-func (h HashesResponse) Txs() []string {
-	var result []string
-	for _, subRes := range h.Result {
-		result = append(result, subRes...)
-	}
-	return result
-}
-
-type Block struct {
-	Header BlockHeader `json:"header"`
-}
-
-type BlockHeader struct {
-	Number    string `json:"BlockNum"`
-	Timestamp string `json:"Timestamp"`
-}
diff --git a/platform/zilliqa/rpc.go b/platform/zilliqa/rpc.go
deleted file mode 100644
index ccfce78..0000000
--- a/platform/zilliqa/rpc.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package zilliqa
-
-import (
-	"strconv"
-
-	"github.com/mitchellh/mapstructure"
-	"github.com/trustwallet/golibs/client"
-)
-
-type RpcClient struct {
-	client.Request
-}
-
-func (c *RpcClient) GetBlockchainInfo() (info *ChainInfo, err error) {
-	err = c.RpcCall(&info, "GetBlockchainInfo", nil)
-	return
-}
-
-func (c *RpcClient) GetTx(hash string) (tx TxRPC, err error) {
-	err = c.RpcCall(&tx, "GetTransaction", []string{hash})
-	return
-}
-
-func (c *RpcClient) GetTransactionsHashesInBlock(number int64) ([]string, error) {
-	strNumber := strconv.FormatInt(number, 10)
-	requestBody := &client.RpcRequest{
-		JsonRpc: client.JsonRpcVersion,
-		Method:  "GetTransactionsForTxBlock",
-		Params:  []string{strNumber},
-		Id:      number,
-	}
-	var result HashesResponse
-	err := c.Post(&result, "/", requestBody)
-	if err != nil {
-		return nil, err
-	}
-	return result.Txs(), nil
-}
-
-func (c *RpcClient) GetTxInBlock(number int64) ([]Tx, error) {
-	txs := make([]Tx, 0)
-	hashes, err := c.GetTransactionsHashesInBlock(number)
-	if err != nil || len(hashes) == 0 {
-		return txs, err
-	}
-
-	block, err := c.GetBlock(number)
-	if err != nil {
-		return txs, err
-	}
-
-	var requests client.RpcRequests
-	for _, hash := range hashes {
-		requests = append(requests, &client.RpcRequest{
-			Method: "GetTransaction",
-			Params: []string{hash},
-		})
-	}
-	responses, err := c.RpcBatchCall(requests)
-	if err != nil {
-		return nil, err
-	}
-	for _, result := range responses {
-		var txRPC TxRPC
-		if mapstructure.Decode(result.Result, &txRPC) != nil {
-			continue
-		}
-		if tx := txRPC.toTx(block.Header); tx != nil {
-			txs = append(txs, *tx)
-		}
-	}
-	return txs, nil
-}
-
-func (c *RpcClient) GetBlock(number int64) (block Block, err error) {
-	err = c.RpcCall(&block, "GetTxBlock", []string{strconv.FormatInt(number, 10)})
-	return
-}
diff --git a/platform/zilliqa/rpc/client.go b/platform/zilliqa/rpc/client.go
new file mode 100644
index 0000000..1be7edb
--- /dev/null
+++ b/platform/zilliqa/rpc/client.go
@@ -0,0 +1,95 @@
+package rpc
+
+import (
+	"strconv"
+
+	"github.com/mitchellh/mapstructure"
+	"github.com/trustwallet/golibs/client"
+	"github.com/trustwallet/golibs/network/middleware"
+)
+
+type Client struct {
+	client.Request
+}
+
+func InitClient(url string) Client {
+	return Client{client.InitClient(url, middleware.SentryErrorHandler)}
+}
+
+func (c *Client) GetBlockchainInfo() (info *ChainInfo, err error) {
+	err = c.RpcCall(&info, "GetBlockchainInfo", nil)
+	return
+}
+
+func (c *Client) GetTx(hash string) (tx Tx, err error) {
+	err = c.RpcCall(&tx, "GetTransaction", []string{hash})
+	return
+}
+
+func (c *Client) GetBlock(number int64) (block Block, err error) {
+	err = c.RpcCall(&block, "GetTxBlock", []string{strconv.FormatInt(number, 10)})
+	return
+}
+
+func (c *Client) GetTransactionsHashesInBlock(number int64) ([]string, error) {
+	strNumber := strconv.FormatInt(number, 10)
+	requestBody := &client.RpcRequest{
+		JsonRpc: client.JsonRpcVersion,
+		Method:  "GetTransactionsForTxBlock",
+		Params:  []string{strNumber},
+		Id:      number,
+	}
+	var result HashesResponse
+	err := c.Post(&result, "/", requestBody)
+	if err != nil {
+		return nil, err
+	}
+	return result.Txs(), nil
+}
+
+func (c *Client) GetTxInBlock(number int64) (header BlockHeader, txs []Tx, err error) {
+	hashes, err := c.GetTransactionsHashesInBlock(number)
+	if err != nil || len(hashes) == 0 {
+		return
+	}
+
+	block, err := c.GetBlock(number)
+	if err != nil {
+		return
+	}
+
+	// Avoid 413 Payload Too Large
+	elements := make([]interface{}, len(hashes))
+	for i := range elements {
+		elements[i] = hashes[i]
+	}
+	requests := client.MakeBatchRequests(elements, 500, mapHash)
+
+	var responses []client.RpcResponse
+	for _, reqs := range requests {
+		resps, e := c.RpcBatchCall(reqs)
+		if e != nil {
+			err = e
+			return
+		}
+		responses = append(responses, resps...)
+	}
+
+	for _, result := range responses {
+		var tx Tx
+		if mapstructure.Decode(result.Result, &tx) != nil {
+			continue
+		}
+		txs = append(txs, tx)
+	}
+
+	return block.Header, txs, nil
+}
+
+func mapHash(hash interface{}) client.RpcRequest {
+	array := []interface{}{hash}
+	return client.RpcRequest{
+		Method: "GetTransaction",
+		Params: array,
+	}
+}
diff --git a/platform/zilliqa/rpc/model.go b/platform/zilliqa/rpc/model.go
new file mode 100644
index 0000000..802088a
--- /dev/null
+++ b/platform/zilliqa/rpc/model.go
@@ -0,0 +1,66 @@
+package rpc
+
+import "github.com/trustwallet/golibs/client"
+
+type BlockTxs [][]string
+
+func (b BlockTxs) txs() []string {
+	txs := make([]string, 0)
+	for _, ids := range b {
+		txs = append(txs, ids...)
+	}
+	return txs
+}
+
+type ChainInfo struct {
+	NumTxBlocks string `json:"NumTxBlocks"`
+}
+
+type Receipt struct {
+	CumulativeGas string `json:"cumulative_gas"`
+	EpochNum      string `json:"epoch_num"`
+	Success       bool   `json:"success"`
+}
+
+type Tx struct {
+	ID           string  `json:"ID"`
+	Amount       string  `json:"amount"`
+	GasLimit     string  `json:"gasLimit"`
+	GasPrice     string  `json:"gasPrice"`
+	Nonce        string  `json:"nonce"`
+	Receipt      Receipt `json:"receipt"`
+	SenderPubKey string  `json:"senderPubKey"`
+	Signature    string  `json:"signature"`
+	ToAddr       string  `json:"toAddr"`
+	Version      string  `json:"version"`
+}
+
+type HashesResponse struct {
+	ID      int        `json:"id"`
+	Jsonrpc string     `json:"jsonrpc"`
+	Result  [][]string `json:"result"`
+}
+
+func (h HashesResponse) Txs() []string {
+	var result []string
+	for _, subRes := range h.Result {
+		result = append(result, subRes...)
+	}
+	return result
+}
+
+type BlockTxRpc struct {
+	JsonRpc string           `json:"jsonrpc"`
+	Error   *client.RpcError `json:"error,omitempty"`
+	Result  BlockTxs         `json:"result,omitempty"`
+	Id      string           `json:"id,omitempty"`
+}
+
+type Block struct {
+	Header BlockHeader `json:"header"`
+}
+
+type BlockHeader struct {
+	Number    string `json:"BlockNum"`
+	Timestamp string `json:"Timestamp"`
+}
diff --git a/platform/zilliqa/transaction.go b/platform/zilliqa/transaction.go
index a2f7f79..0173f1e 100644
--- a/platform/zilliqa/transaction.go
+++ b/platform/zilliqa/transaction.go
@@ -1,6 +1,7 @@
 package zilliqa
 
 import (
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 	"github.com/trustwallet/golibs/coin"
 	"github.com/trustwallet/golibs/types"
 )
@@ -24,7 +25,7 @@ func (p *Platform) GetTxsByAddress(address string) (types.TxPage, error) {
 	return normalized, nil
 }
 
-func Normalize(srcTx *Tx) (tx types.Tx) {
+func Normalize(srcTx *viewblock.Tx) (tx types.Tx) {
 	tx = types.Tx{
 		ID:       srcTx.Hash,
 		Coin:     coin.ZILLIQA,
diff --git a/platform/zilliqa/client.go b/platform/zilliqa/viewblock/client.go
similarity index 53%
rename from platform/zilliqa/client.go
rename to platform/zilliqa/viewblock/client.go
index 34fc136..7cc9a27 100644
--- a/platform/zilliqa/client.go
+++ b/platform/zilliqa/viewblock/client.go
@@ -1,15 +1,22 @@
-package zilliqa
+package viewblock
 
 import (
 	"fmt"
 
 	"github.com/trustwallet/golibs/client"
+	"github.com/trustwallet/golibs/network/middleware"
 )
 
 type Client struct {
 	client.Request
 }
 
+func InitClient(api, apiKey string) Client {
+	c := Client{client.InitClient(api, middleware.SentryErrorHandler)}
+	c.Headers["X-APIKEY"] = apiKey
+	return c
+}
+
 func (c *Client) GetTxsOfAddress(address string) (tx []Tx, err error) {
 	path := fmt.Sprintf("addresses/%s/txs", address)
 	err = c.Get(&tx, path, nil)
diff --git a/platform/zilliqa/viewblock/model.go b/platform/zilliqa/viewblock/model.go
new file mode 100644
index 0000000..0d79d39
--- /dev/null
+++ b/platform/zilliqa/viewblock/model.go
@@ -0,0 +1,34 @@
+package viewblock
+
+import (
+	"strconv"
+)
+
+type Tx struct {
+	Hash           string      `json:"hash"`
+	BlockHeight    uint64      `json:"blockHeight"`
+	From           string      `json:"from"`
+	To             string      `json:"to"`
+	Value          string      `json:"value"`
+	Fee            string      `json:"fee"`
+	Timestamp      int64       `json:"timestamp"`
+	Signature      string      `json:"signature"`
+	Nonce          interface{} `json:"nonce"`
+	ReceiptSuccess bool        `json:"receiptSuccess"`
+}
+
+func (tx Tx) NonceValue() uint64 {
+	switch n := tx.Nonce.(type) {
+	case string:
+		r, err := strconv.Atoi(n)
+		if err != nil {
+			break
+		}
+		return uint64(r)
+	case int:
+		return uint64(n)
+	case float64:
+		return uint64(n)
+	}
+	return 0
+}


Test patch:
diff --git a/platform/zilliqa/model_test.go b/platform/zilliqa/model_test.go
index b17204f..d187a4c 100644
--- a/platform/zilliqa/model_test.go
+++ b/platform/zilliqa/model_test.go
@@ -4,6 +4,9 @@ import (
 	"encoding/json"
 	"reflect"
 	"testing"
+
+	"github.com/trustwallet/blockatlas/platform/zilliqa/rpc"
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 )
 
 const transaction = `{
@@ -24,7 +27,7 @@ const transaction = `{
 }`
 
 func TestTxRPC_toTx(t *testing.T) {
-	tx := Tx{
+	tx := viewblock.Tx{
 		Hash:           "0xf73cf0a229a3d71e1a5c2ac4acbab598c706e64882a2e7c5ed6e406ce69fc16c",
 		BlockHeight:    185343,
 		From:           "zil1jrpjd8pjuv50cfkfr7eu6yrm3rn5u8rulqhqpz",
@@ -37,61 +40,16 @@ func TestTxRPC_toTx(t *testing.T) {
 		ReceiptSuccess: true,
 	}
 
-	var txRPC TxRPC
+	var txRPC rpc.Tx
 	err := json.Unmarshal([]byte(transaction), &txRPC)
 	if err != nil {
 		t.Error(err)
 		return
 	}
 
-	header := BlockHeader{Number: "185343", Timestamp: "1603831144458128"}
+	header := rpc.BlockHeader{Number: "185343", Timestamp: "1603831144458128"}
 
-	if got := txRPC.toTx(header); !reflect.DeepEqual(*got, tx) {
+	if got := TxFromRpc(txRPC, header); !reflect.DeepEqual(*got, tx) {
 		t.Errorf("TxRPC.toTx() = %v, want %v", *got, tx)
 	}
 }
-
-func TestTx_NonceValue(t *testing.T) {
-	tests := []struct {
-		name  string
-		nonce interface{}
-		want  uint64
-	}{
-		{"test int", 0, 0},
-		{"test float", 3.4, 3},
-		{"test string", "33", 33},
-		{"test error string", "test", 0},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			tx := Tx{
-				Nonce: tt.nonce,
-			}
-			if got := tx.NonceValue(); got != tt.want {
-				t.Errorf("NonceValue() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
-func TestBlockTxs_txs(t *testing.T) {
-	tests := []struct {
-		name string
-		b    BlockTxs
-		want []string
-	}{
-		{"test 1 tx", BlockTxs{{"tx1"}, {}}, []string{"tx1"}},
-		{"test 2 txs  1", BlockTxs{{"tx1"}, {"tx2"}}, []string{"tx1", "tx2"}},
-		{"test 2 txs 2", BlockTxs{{"tx1", "tx2"}}, []string{"tx1", "tx2"}},
-		{"test 3 txs 1", BlockTxs{{"tx1", "tx2"}, {"tx3"}}, []string{"tx1", "tx2", "tx3"}},
-		{"test 3 txs 2", BlockTxs{{"tx1"}, {"tx2"}, {"tx3"}}, []string{"tx1", "tx2", "tx3"}},
-		{"test 4 txs", BlockTxs{{"tx1", "tx2"}, {"tx3"}, {"tx4"}}, []string{"tx1", "tx2", "tx3", "tx4"}},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := tt.b.txs(); !reflect.DeepEqual(got, tt.want) {
-				t.Errorf("txs() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
diff --git a/platform/zilliqa/rpc/model_test.go b/platform/zilliqa/rpc/model_test.go
new file mode 100644
index 0000000..d30dc50
--- /dev/null
+++ b/platform/zilliqa/rpc/model_test.go
@@ -0,0 +1,28 @@
+package rpc
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestBlockTxs_txs(t *testing.T) {
+	tests := []struct {
+		name string
+		b    BlockTxs
+		want []string
+	}{
+		{"test 1 tx", BlockTxs{{"tx1"}, {}}, []string{"tx1"}},
+		{"test 2 txs  1", BlockTxs{{"tx1"}, {"tx2"}}, []string{"tx1", "tx2"}},
+		{"test 2 txs 2", BlockTxs{{"tx1", "tx2"}}, []string{"tx1", "tx2"}},
+		{"test 3 txs 1", BlockTxs{{"tx1", "tx2"}, {"tx3"}}, []string{"tx1", "tx2", "tx3"}},
+		{"test 3 txs 2", BlockTxs{{"tx1"}, {"tx2"}, {"tx3"}}, []string{"tx1", "tx2", "tx3"}},
+		{"test 4 txs", BlockTxs{{"tx1", "tx2"}, {"tx3"}, {"tx4"}}, []string{"tx1", "tx2", "tx3", "tx4"}},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := tt.b.txs(); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("txs() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/platform/zilliqa/transaction_test.go b/platform/zilliqa/transaction_test.go
index f65f4cb..674e2a2 100644
--- a/platform/zilliqa/transaction_test.go
+++ b/platform/zilliqa/transaction_test.go
@@ -4,6 +4,7 @@ import (
 	"reflect"
 	"testing"
 
+	"github.com/trustwallet/blockatlas/platform/zilliqa/viewblock"
 	"github.com/trustwallet/golibs/coin"
 	"github.com/trustwallet/golibs/mock"
 	"github.com/trustwallet/golibs/types"
@@ -45,7 +46,7 @@ func TestNormalizeTx(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			var srcTx Tx
+			var srcTx viewblock.Tx
 			_ = mock.JsonModelFromFilePath("mocks/"+tt.args.filename, &srcTx)
 			gotTx := Normalize(&srcTx)
 			if !reflect.DeepEqual(gotTx, tt.wantTx) {
diff --git a/platform/zilliqa/viewblock/model_test.go b/platform/zilliqa/viewblock/model_test.go
new file mode 100644
index 0000000..c6fed41
--- /dev/null
+++ b/platform/zilliqa/viewblock/model_test.go
@@ -0,0 +1,28 @@
+package viewblock
+
+import (
+	"testing"
+)
+
+func TestTx_NonceValue(t *testing.T) {
+	tests := []struct {
+		name  string
+		nonce interface{}
+		want  uint64
+	}{
+		{"test int", 0, 0},
+		{"test float", 3.4, 3},
+		{"test string", "33", 33},
+		{"test error string", "test", 0},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			tx := Tx{
+				Nonce: tt.nonce,
+			}
+			if got := tx.NonceValue(); got != tt.want {
+				t.Errorf("NonceValue() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}