Repository: trustwallet/blockatlas
Pull request #847 (2020-02-13 01:14:15)

Split platform files per API implementation
Split platform files per API implementation:

- `base.go` for platform base implementation
- `transaction.go` for tx and token tx API's
- `block.go` for block observer
- `stake.go` for naming service API's
- `token.go` for Token API's
- `collection.go` for Collection API's
- `domain.go` for naming service API's


closes https://github.com/trustwallet/blockatlas/issues/715

todo[bot]: ## remove once most of the clients will be updated (deadline: March 17th)

https://github.com/trustwallet/blockatlas/blob/a46549e59bebefa4ba981f04b77452154a31c005/platform/ethereum/collection.go#L236-L241

---

###### This comment was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in a46549e59bebefa4ba981f04b77452154a31c005 in #847. cc @trustwallet.
vikmeup: address.go? What does this mean? transaction.go or transactions.go would be self explanatory 

What is xpub addresses? 
vikmeup: Why base? Is is transaction or block API? 
Pantani: > address.go? What does this mean? transaction.go or transactions.go would be self explanatory
> 
> What is xpub addresses?

https://github.com/trustwallet/blockatlas/blob/master/pkg/blockatlas/api.go#L45
Pantani: > Why base? Is is transaction or block API?

Is the both, transactions and block, this is the base methods all coins need to implement before implementing other features
vikmeup: Block is not requires, many blockchains don't have it implemented
vikmeup: > But I think is weird as well, do you have another suggestion for a name?

transaction.go is a good name. all it does - return transaction list

todo[bot]: ## remove once most of the clients will be updated (deadline: March 17th)

https://github.com/trustwallet/blockatlas/blob/02fc774218e9fd556e1f02442845e7340ae30f2d/platform/ethereum/collection.go#L241-L246

---

###### This comment was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in 02fc774218e9fd556e1f02442845e7340ae30f2d in #847. cc @trustwallet.
todo[bot]: ## remove once most of the clients will be updated (deadline: March 17th)

https://github.com/trustwallet/blockatlas/blob/15a68d1e597e22192cd17a0da9b171220fc02c00/platform/ethereum/collection.go#L241-L246

---

###### This comment was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in 15a68d1e597e22192cd17a0da9b171220fc02c00 in #847. cc @trustwallet.
vikmeup: @Pantani can you rebase? let's get this merged
todo[bot]: ## remove once most of the clients will be updated (deadline: March 17th)

https://github.com/trustwallet/blockatlas/blob/4867a1ec6ce52a432cd987b3bdb8f15528011136/platform/ethereum/collection.go#L241-L246

---

###### This comment was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in 4867a1ec6ce52a432cd987b3bdb8f15528011136 in #847. cc @trustwallet.
Pantani: > Looks good! much more organized
> 
> You used to have `custom.go` - was not able to find that in the changes

The only custom route we have now is transactions for bitcoin, I put the `v1/xpub` and `v1/address` inside `transaction.go` to be more clear, but I going to put inside the developer documentation in case we need custom routes, create a `custom.go` file
todo[bot]: ## remove once most of the clients will be updated (deadline: March 17th)

https://github.com/trustwallet/blockatlas/blob/98d5f76a907298e242101a87a8ef44ad5c060c02/platform/ethereum/collection.go#L241-L246

---

###### This comment was generated by [todo](https://todo.jasonet.co) based on a `TODO` comment in 98d5f76a907298e242101a87a8ef44ad5c060c02 in #847. cc @trustwallet.

Patch:
diff --git a/pkg/address/address.go b/pkg/address/address.go
index 05c3260..a720f8c 100644
--- a/pkg/address/address.go
+++ b/pkg/address/address.go
@@ -1,6 +1,10 @@
 package address
 
 import (
+	"crypto/sha256"
+	"encoding/hex"
+	"github.com/mr-tron/base58"
+	"github.com/trustwallet/blockatlas/pkg/errors"
 	"golang.org/x/crypto/sha3"
 	"strings"
 )
@@ -37,3 +41,19 @@ func EIP55Checksum(unchecksummed string) string {
 	val := string(result)
 	return "0x" + val
 }
+
+// HexToAddress converts a hex representation of a Tron address
+// into a Base58 string with a 4 byte checksum.
+func HexToAddress(hexAddr string) (b58 string, err error) {
+	bytes, err := hex.DecodeString(hexAddr)
+	if err != nil {
+		return "", errors.E(err, errors.TypePlatformUnmarshal,
+			errors.Params{"hexAddr": hexAddr}).PushToSentry()
+	}
+	var checksum [32]byte
+	checksum = sha256.Sum256(bytes)
+	checksum = sha256.Sum256(checksum[:])
+	bytes = append(bytes, checksum[:4]...)
+	b58 = base58.EncodeAlphabet(bytes, base58.BTCAlphabet)
+	return
+}
diff --git a/platform/aeternity/base.go b/platform/aeternity/base.go
new file mode 100644
index 0000000..1f345c8
--- /dev/null
+++ b/platform/aeternity/base.go
@@ -0,0 +1,20 @@
+package aeternity
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("aeternity.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.AE]
+}
diff --git a/platform/aeternity/api.go b/platform/aeternity/transaction.go
similarity index 84%
rename from platform/aeternity/api.go
rename to platform/aeternity/transaction.go
index 9b5d5e4..48adf1f 100644
--- a/platform/aeternity/api.go
+++ b/platform/aeternity/transaction.go
@@ -5,23 +5,8 @@ import (
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"strings"
-
-	"github.com/spf13/viper"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("aeternity.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.AE]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	addressTxs, err := p.client.GetTxs(address, blockatlas.TxPerPage)
 	if err != nil {
diff --git a/platform/aeternity/api_test.go b/platform/aeternity/transaction_test.go
similarity index 100%
rename from platform/aeternity/api_test.go
rename to platform/aeternity/transaction_test.go
diff --git a/platform/aion/base.go b/platform/aion/base.go
new file mode 100644
index 0000000..15379aa
--- /dev/null
+++ b/platform/aion/base.go
@@ -0,0 +1,20 @@
+package aion
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("aion.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.AION]
+}
diff --git a/platform/aion/api.go b/platform/aion/transaction.go
similarity index 84%
rename from platform/aion/api.go
rename to platform/aion/transaction.go
index 8287980..d1bc7c3 100644
--- a/platform/aion/api.go
+++ b/platform/aion/transaction.go
@@ -1,26 +1,12 @@
 package aion
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/numbers"
 	"strconv"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("aion.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.AION]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	if srcTxs, err := p.client.GetTxsOfAddress(address, blockatlas.TxPerPage); err == nil {
 		return NormalizeTxs(srcTxs.Content), err
diff --git a/platform/aion/api_test.go b/platform/aion/transaction_test.go
similarity index 100%
rename from platform/aion/api_test.go
rename to platform/aion/transaction_test.go
diff --git a/platform/algorand/base.go b/platform/algorand/base.go
new file mode 100644
index 0000000..1e121c6
--- /dev/null
+++ b/platform/algorand/base.go
@@ -0,0 +1,21 @@
+package algorand
+
+import (
+	"github.com/spf13/viper"
+
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("algorand.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.ALGO]
+}
diff --git a/platform/algorand/block.go b/platform/algorand/block.go
new file mode 100644
index 0000000..b9260aa
--- /dev/null
+++ b/platform/algorand/block.go
@@ -0,0 +1,21 @@
+package algorand
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetLatestBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	txs, err := p.client.GetTxsInBlock(num)
+	if err != nil {
+		return nil, err
+	}
+
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    NormalizeTxs(txs),
+	}, nil
+}
diff --git a/platform/algorand/api.go b/platform/algorand/transaction.go
similarity index 66%
rename from platform/algorand/api.go
rename to platform/algorand/transaction.go
index fab56b1..9a25e24 100644
--- a/platform/algorand/api.go
+++ b/platform/algorand/transaction.go
@@ -1,43 +1,12 @@
 package algorand
 
 import (
-	"github.com/spf13/viper"
-
 	"strconv"
 
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("algorand.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.ALGO]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetLatestBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	txs, err := p.client.GetTxsInBlock(num)
-	if err != nil {
-		return nil, err
-	}
-
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    NormalizeTxs(txs),
-	}, nil
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	txs, err := p.client.GetTxsOfAddress(address)
 	if err != nil {
diff --git a/platform/algorand/api_test.go b/platform/algorand/transaction_test.go
similarity index 100%
rename from platform/algorand/api_test.go
rename to platform/algorand/transaction_test.go
diff --git a/platform/binance/base.go b/platform/binance/base.go
new file mode 100644
index 0000000..ae15a6e
--- /dev/null
+++ b/platform/binance/base.go
@@ -0,0 +1,25 @@
+package binance
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	dexClient DexClient
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("binance.api"))}
+	p.client.ErrorHandler = getHTTPError
+
+	p.dexClient = DexClient{blockatlas.InitClient(viper.GetString("binance.dex"))}
+	p.dexClient.ErrorHandler = getHTTPError
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.BNB]
+}
diff --git a/platform/binance/block.go b/platform/binance/block.go
new file mode 100644
index 0000000..9115c6c
--- /dev/null
+++ b/platform/binance/block.go
@@ -0,0 +1,39 @@
+package binance
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/errors"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	// No native function to get height in explorer API
+	// Workaround: Request list of blocks
+	// and return number of the newest one
+	list, err := p.client.GetBlockList(1)
+	if err != nil {
+		return 0, err
+	}
+	if len(list.BlockArray) == 0 {
+		return 0, errors.E("no block descriptor found", errors.TypePlatformApi).PushToSentry()
+	}
+	return list.BlockArray[0].BlockHeight, nil
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	srcTxs, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+
+	txs := make(blockatlas.TxPage, 0)
+	childTxs, err := p.getTxChildChan(srcTxs.Txs)
+	if err == nil {
+		txs = NormalizeTxs(childTxs, "", "")
+	} else {
+		txs = NormalizeTxs(srcTxs.Txs, "", "")
+	}
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
diff --git a/platform/binance/token.go b/platform/binance/token.go
new file mode 100644
index 0000000..0e63334
--- /dev/null
+++ b/platform/binance/token.go
@@ -0,0 +1,63 @@
+package binance
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"strings"
+)
+
+func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
+	account, err := p.dexClient.GetAccountMetadata(address)
+	if err != nil || len(account.Balances) == 0 {
+		return []blockatlas.Token{}, nil
+	}
+	tokens, err := p.dexClient.GetTokens()
+	if err != nil {
+		return nil, err
+	}
+	return NormalizeTokens(account.Balances, tokens), nil
+}
+
+// NormalizeToken converts a Binance token into the generic model
+func NormalizeToken(srcToken *Balance, tokens *TokenPage) (t blockatlas.Token, ok bool) {
+	if srcToken.isAllZeroBalance() {
+		return t, false
+	}
+
+	tk := tokens.findToken(srcToken.Symbol)
+	if tk == nil {
+		return t, false
+	}
+
+	t = blockatlas.Token{
+		Name:     tk.Name,
+		Symbol:   tk.OriginalSymbol,
+		TokenID:  tk.Symbol,
+		Coin:     coin.BNB,
+		Decimals: uint(decimalPlaces(tk.TotalSupply)),
+		Type:     blockatlas.TokenTypeBEP2,
+	}
+
+	return t, true
+}
+
+// NormalizeTxs converts multiple Binance tokens
+func NormalizeTokens(srcBalance []Balance, tokens *TokenPage) (tokenPage []blockatlas.Token) {
+	for _, srcToken := range srcBalance {
+		token, ok := NormalizeToken(&srcToken, tokens)
+		if !ok {
+			continue
+		}
+		tokenPage = append(tokenPage, token)
+	}
+	return
+}
+
+// decimalPlaces count the decimals places.
+func decimalPlaces(v string) int {
+	s := strings.Split(v, ".")
+	if len(s) < 2 {
+		return 0
+	}
+	return len(s[1])
+}
diff --git a/platform/binance/api.go b/platform/binance/transaction.go
similarity index 64%
rename from platform/binance/api.go
rename to platform/binance/transaction.go
index ab53f5b..289fe6f 100644
--- a/platform/binance/api.go
+++ b/platform/binance/transaction.go
@@ -2,70 +2,29 @@ package binance
 
 import (
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"github.com/trustwallet/blockatlas/pkg/errors"
 	"github.com/trustwallet/blockatlas/pkg/logger"
 	"github.com/trustwallet/blockatlas/pkg/numbers"
 	"strings"
 	"sync"
 
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 )
 
-type Platform struct {
-	client    Client
-	dexClient DexClient
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("binance.api"))}
-	p.client.ErrorHandler = getHTTPError
-
-	p.dexClient = DexClient{blockatlas.InitClient(viper.GetString("binance.dex"))}
-	p.dexClient.ErrorHandler = getHTTPError
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.BNB]
+func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
+	// Endpoint supports queries without token query parameter
+	return p.GetTokenTxsByAddress(address, p.Coin().Symbol)
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	// No native function to get height in explorer API
-	// Workaround: Request list of blocks
-	// and return number of the newest one
-	list, err := p.client.GetBlockList(1)
+func (p *Platform) GetTokenTxsByAddress(address string, token string) (blockatlas.TxPage, error) {
+	srcTxs, err := p.client.GetTxsOfAddress(address, token)
 	if err != nil {
-		return 0, err
-	}
-	if len(list.BlockArray) == 0 {
-		return 0, errors.E("no block descriptor found", errors.TypePlatformApi).PushToSentry()
+		return nil, err
 	}
-	return list.BlockArray[0].BlockHeight, nil
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	srcTxs, err := p.client.GetBlockByNumber(num)
+	txs, err := p.getTxChildChan(srcTxs.Txs)
 	if err != nil {
 		return nil, err
 	}
-
-	txs := make(blockatlas.TxPage, 0)
-	childTxs, err := p.getTxChildChan(srcTxs.Txs)
-	if err == nil {
-		txs = NormalizeTxs(childTxs, "", "")
-	} else {
-		txs = NormalizeTxs(srcTxs.Txs, "", "")
-	}
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
-func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
-	// Endpoint supports queries without token query parameter
-	return p.GetTokenTxsByAddress(address, p.Coin().Symbol)
+	return NormalizeTxs(txs, token, address), nil
 }
 
 // getTxChildChan get all child assets from a tx
@@ -100,18 +59,6 @@ func (p *Platform) getTxChildChan(srcTxs []Tx) ([]Tx, error) {
 	return txs, nil
 }
 
-func (p *Platform) GetTokenTxsByAddress(address string, token string) (blockatlas.TxPage, error) {
-	srcTxs, err := p.client.GetTxsOfAddress(address, token)
-	if err != nil {
-		return nil, err
-	}
-	txs, err := p.getTxChildChan(srcTxs.Txs)
-	if err != nil {
-		return nil, err
-	}
-	return NormalizeTxs(txs, token, address), nil
-}
-
 func normalizeTransfer(tx blockatlas.Tx, srcTx Tx, token, address string) (blockatlas.TxPage, bool) {
 	// Verify if the tx has more them one asset
 	if srcTx.HasChildren == 1 {
@@ -241,59 +188,3 @@ func NormalizeTxs(srcTxs []Tx, token, adress string) (txs []blockatlas.Tx) {
 	}
 	return
 }
-
-func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
-	account, err := p.dexClient.GetAccountMetadata(address)
-	if err != nil || len(account.Balances) == 0 {
-		return []blockatlas.Token{}, nil
-	}
-	tokens, err := p.dexClient.GetTokens()
-	if err != nil {
-		return nil, err
-	}
-	return NormalizeTokens(account.Balances, tokens), nil
-}
-
-// NormalizeToken converts a Binance token into the generic model
-func NormalizeToken(srcToken *Balance, tokens *TokenPage) (t blockatlas.Token, ok bool) {
-	if srcToken.isAllZeroBalance() {
-		return t, false
-	}
-
-	tk := tokens.findToken(srcToken.Symbol)
-	if tk == nil {
-		return t, false
-	}
-
-	t = blockatlas.Token{
-		Name:     tk.Name,
-		Symbol:   tk.OriginalSymbol,
-		TokenID:  tk.Symbol,
-		Coin:     coin.BNB,
-		Decimals: uint(decimalPlaces(tk.TotalSupply)),
-		Type:     blockatlas.TokenTypeBEP2,
-	}
-
-	return t, true
-}
-
-// NormalizeTxs converts multiple Binance tokens
-func NormalizeTokens(srcBalance []Balance, tokens *TokenPage) (tokenPage []blockatlas.Token) {
-	for _, srcToken := range srcBalance {
-		token, ok := NormalizeToken(&srcToken, tokens)
-		if !ok {
-			continue
-		}
-		tokenPage = append(tokenPage, token)
-	}
-	return
-}
-
-// decimalPlaces count the decimals places.
-func decimalPlaces(v string) int {
-	s := strings.Split(v, ".")
-	if len(s) < 2 {
-		return 0
-	}
-	return len(s[1])
-}
diff --git a/platform/bitcoin/base.go b/platform/bitcoin/base.go
new file mode 100644
index 0000000..a8c685b
--- /dev/null
+++ b/platform/bitcoin/base.go
@@ -0,0 +1,35 @@
+package bitcoin
+
+import (
+	"fmt"
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	CoinIndex uint
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[p.CoinIndex]
+}
+
+func (p *Platform) ConfigKey() string {
+	return fmt.Sprintf("%s.api", p.Coin().Handle)
+}
+
+func (p *Platform) GetAddressesFromXpub(xpub string) ([]string, error) {
+	tokens, err := p.client.GetAddressesFromXpub(xpub)
+	addresses := make([]string, 0)
+	for _, token := range tokens {
+		addresses = append(addresses, token.Name)
+	}
+	return addresses, err
+}
diff --git a/platform/bitcoin/block.go b/platform/bitcoin/block.go
new file mode 100644
index 0000000..fb3bf22
--- /dev/null
+++ b/platform/bitcoin/block.go
@@ -0,0 +1,61 @@
+package bitcoin
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/logger"
+	"sync"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	status, err := p.client.GetBlockNumber()
+	return status.Backend.Blocks, err
+}
+
+func (p *Platform) GetAllBlockPages(total, num int64) []Transaction {
+	txs := make([]Transaction, 0)
+	if total <= 1 {
+		return txs
+	}
+
+	start := int64(1)
+	var wg sync.WaitGroup
+	out := make(chan TransactionsList, int(total-start))
+	wg.Add(int(total - start))
+	for start < total {
+		start++
+		go func(page, num int64, out chan TransactionsList, wg *sync.WaitGroup) {
+			defer wg.Done()
+			block, err := p.client.GetTransactionsByBlock(num, page)
+			if err != nil {
+				logger.Error("GetTransactionsByBlockChan", err, logger.Params{"number": num, "page": page})
+				return
+			}
+			out <- block
+		}(start, num, out, &wg)
+	}
+	wg.Wait()
+	close(out)
+	for r := range out {
+		txs = append(txs, r.TransactionList()...)
+	}
+	return txs
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	page := int64(1)
+	block, err := p.client.GetTransactionsByBlock(num, page)
+	if err != nil {
+		return nil, err
+	}
+	txPages := p.GetAllBlockPages(block.TotalPages, num)
+	txs := append(txPages, block.TransactionList()...)
+	var normalized []blockatlas.Tx
+	for _, tx := range txs {
+		normalized = append(normalized, normalizeTransaction(tx, p.CoinIndex))
+	}
+	return &blockatlas.Block{
+		Number: num,
+		ID:     block.Hash,
+		Txs:    normalized,
+	}, nil
+}
diff --git a/platform/bitcoin/api.go b/platform/bitcoin/transaction.go
similarity index 73%
rename from platform/bitcoin/api.go
rename to platform/bitcoin/transaction.go
index 7b89ee6..ee3f509 100644
--- a/platform/bitcoin/api.go
+++ b/platform/bitcoin/transaction.go
@@ -1,36 +1,14 @@
 package bitcoin
 
 import (
-	"fmt"
 	mapset "github.com/deckarep/golang-set"
 	"github.com/gin-gonic/gin"
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"github.com/trustwallet/blockatlas/pkg/logger"
 	"github.com/trustwallet/blockatlas/pkg/numbers"
 	"net/http"
-	"sync"
 )
 
-type Platform struct {
-	client    Client
-	CoinIndex uint
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[p.CoinIndex]
-}
-
-func (p *Platform) ConfigKey() string {
-	return fmt.Sprintf("%s.api", p.Coin().Handle)
-}
-
 // @Summary Get xpub transactions
 // @ID xpub
 // @Description Get transactions from xpub address
@@ -101,77 +79,69 @@ func (p *Platform) getTxsByAddress(address string) ([]blockatlas.Tx, error) {
 	return txs, nil
 }
 
-func (p *Platform) GetAddressesFromXpub(xpub string) ([]string, error) {
-	tokens, err := p.client.GetAddressesFromXpub(xpub)
-	addresses := make([]string, 0)
-	for _, token := range tokens {
-		addresses = append(addresses, token.Name)
+func normalizeTxs(sourceTxs TransactionsList, coinIndex uint, addressSet mapset.Set) []blockatlas.Tx {
+	var txs []blockatlas.Tx
+	for _, transaction := range sourceTxs.TransactionList() {
+		if tx, ok := normalizeTransfer(transaction, coinIndex, addressSet); ok {
+			txs = append(txs, tx)
+		}
 	}
-	return addresses, err
+	return txs
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	status, err := p.client.GetBlockNumber()
-	return status.Backend.Blocks, err
-}
+func normalizeTransfer(transaction Transaction, coinIndex uint, addressSet mapset.Set) (tx blockatlas.Tx, ok bool) {
+	tx = normalizeTransaction(transaction, coinIndex)
+	direction := InferDirection(&tx, addressSet)
+	value := InferValue(&tx, direction, addressSet)
 
-func (p *Platform) GetAllBlockPages(total, num int64) []Transaction {
-	txs := make([]Transaction, 0)
-	if total <= 1 {
-		return txs
+	tx.Direction = direction
+	tx.Meta = blockatlas.Transfer{
+		Value:    value,
+		Symbol:   coin.Coins[coinIndex].Symbol,
+		Decimals: coin.Coins[coinIndex].Decimals,
 	}
 
-	start := int64(1)
-	var wg sync.WaitGroup
-	out := make(chan TransactionsList, int(total-start))
-	wg.Add(int(total - start))
-	for start < total {
-		start++
-		go func(page, num int64, out chan TransactionsList, wg *sync.WaitGroup) {
-			defer wg.Done()
-			block, err := p.client.GetTransactionsByBlock(num, page)
-			if err != nil {
-				logger.Error("GetTransactionsByBlockChan", err, logger.Params{"number": num, "page": page})
-				return
-			}
-			out <- block
-		}(start, num, out, &wg)
-	}
-	wg.Wait()
-	close(out)
-	for r := range out {
-		txs = append(txs, r.TransactionList()...)
-	}
-	return txs
+	return tx, true
 }
 
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	page := int64(1)
-	block, err := p.client.GetTransactionsByBlock(num, page)
-	if err != nil {
-		return nil, err
+func InferDirection(tx *blockatlas.Tx, addressSet mapset.Set) blockatlas.Direction {
+	inputSet := mapset.NewSet()
+	for _, address := range tx.Inputs {
+		inputSet.Add(address.Address)
+	}
+	outputSet := mapset.NewSet()
+	for _, address := range tx.Outputs {
+		outputSet.Add(address.Address)
 	}
-	txPages := p.GetAllBlockPages(block.TotalPages, num)
-	txs := append(txPages, block.TransactionList()...)
-	var normalized []blockatlas.Tx
-	for _, tx := range txs {
-		normalized = append(normalized, normalizeTransaction(tx, p.CoinIndex))
+	intersect := addressSet.Intersect(inputSet)
+	if intersect.Cardinality() == 0 {
+		return blockatlas.DirectionIncoming
+	}
+	if outputSet.IsProperSubset(addressSet) || outputSet.Equal(inputSet) {
+		return blockatlas.DirectionSelf
 	}
-	return &blockatlas.Block{
-		Number: num,
-		ID:     block.Hash,
-		Txs:    normalized,
-	}, nil
+	return blockatlas.DirectionOutgoing
 }
 
-func normalizeTxs(sourceTxs TransactionsList, coinIndex uint, addressSet mapset.Set) []blockatlas.Tx {
-	var txs []blockatlas.Tx
-	for _, transaction := range sourceTxs.TransactionList() {
-		if tx, ok := normalizeTransfer(transaction, coinIndex, addressSet); ok {
-			txs = append(txs, tx)
+func InferValue(tx *blockatlas.Tx, direction blockatlas.Direction, addressSet mapset.Set) blockatlas.Amount {
+	value := blockatlas.Amount("0")
+	if len(tx.Outputs) == 0 {
+		return value
+	}
+	if direction == blockatlas.DirectionOutgoing || direction == blockatlas.DirectionSelf {
+		value = tx.Outputs[0].Value
+	} else if direction == blockatlas.DirectionIncoming {
+		amount := value
+		for _, output := range tx.Outputs {
+			if !addressSet.Contains(output.Address) {
+				continue
+			}
+			value := numbers.AddAmount(string(amount), string(output.Value))
+			amount = blockatlas.Amount(value)
 		}
+		value = amount
 	}
-	return txs
+	return value
 }
 
 func normalizeTransaction(tx Transaction, coinIndex uint) blockatlas.Tx {
@@ -210,21 +180,6 @@ func normalizeTransaction(tx Transaction, coinIndex uint) blockatlas.Tx {
 	}
 }
 
-func normalizeTransfer(transaction Transaction, coinIndex uint, addressSet mapset.Set) (tx blockatlas.Tx, ok bool) {
-	tx = normalizeTransaction(transaction, coinIndex)
-	direction := InferDirection(&tx, addressSet)
-	value := InferValue(&tx, direction, addressSet)
-
-	tx.Direction = direction
-	tx.Meta = blockatlas.Transfer{
-		Value:    value,
-		Symbol:   coin.Coins[coinIndex].Symbol,
-		Decimals: coin.Coins[coinIndex].Decimals,
-	}
-
-	return tx, true
-}
-
 func parseOutputs(outputs []Output) (addresses []blockatlas.TxOutput) {
 	set := make(map[string]blockatlas.TxOutput)
 	var ordered []string
@@ -249,46 +204,6 @@ func parseOutputs(outputs []Output) (addresses []blockatlas.TxOutput) {
 	return addresses
 }
 
-func InferDirection(tx *blockatlas.Tx, addressSet mapset.Set) blockatlas.Direction {
-	inputSet := mapset.NewSet()
-	for _, address := range tx.Inputs {
-		inputSet.Add(address.Address)
-	}
-	outputSet := mapset.NewSet()
-	for _, address := range tx.Outputs {
-		outputSet.Add(address.Address)
-	}
-	intersect := addressSet.Intersect(inputSet)
-	if intersect.Cardinality() == 0 {
-		return blockatlas.DirectionIncoming
-	}
-	if outputSet.IsProperSubset(addressSet) || outputSet.Equal(inputSet) {
-		return blockatlas.DirectionSelf
-	}
-	return blockatlas.DirectionOutgoing
-}
-
-func InferValue(tx *blockatlas.Tx, direction blockatlas.Direction, addressSet mapset.Set) blockatlas.Amount {
-	value := blockatlas.Amount("0")
-	if len(tx.Outputs) == 0 {
-		return value
-	}
-	if direction == blockatlas.DirectionOutgoing || direction == blockatlas.DirectionSelf {
-		value = tx.Outputs[0].Value
-	} else if direction == blockatlas.DirectionIncoming {
-		amount := value
-		for _, output := range tx.Outputs {
-			if !addressSet.Contains(output.Address) {
-				continue
-			}
-			value := numbers.AddAmount(string(amount), string(output.Value))
-			amount = blockatlas.Amount(value)
-		}
-		value = amount
-	}
-	return value
-}
-
 func (transaction *Transaction) getStatus() blockatlas.Status {
 	if transaction.Confirmations == 0 {
 		return blockatlas.StatusPending
diff --git a/platform/cosmos/base.go b/platform/cosmos/base.go
new file mode 100644
index 0000000..3fea3ce
--- /dev/null
+++ b/platform/cosmos/base.go
@@ -0,0 +1,26 @@
+package cosmos
+
+import (
+	"fmt"
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	CoinIndex uint
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[p.CoinIndex]
+}
+
+func (p *Platform) ConfigKey() string {
+	return fmt.Sprintf("%s.api", p.Coin().Handle)
+}
diff --git a/platform/cosmos/block.go b/platform/cosmos/block.go
new file mode 100644
index 0000000..509f372
--- /dev/null
+++ b/platform/cosmos/block.go
@@ -0,0 +1,22 @@
+package cosmos
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	srcTxs, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+
+	txs := p.NormalizeTxs(srcTxs.Txs)
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
diff --git a/platform/cosmos/api.go b/platform/cosmos/transaction.go
similarity index 84%
rename from platform/cosmos/api.go
rename to platform/cosmos/transaction.go
index d957524..04f7521 100644
--- a/platform/cosmos/api.go
+++ b/platform/cosmos/transaction.go
@@ -1,9 +1,6 @@
 package cosmos
 
 import (
-	"fmt"
-	"github.com/spf13/viper"
-	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/logger"
 	"github.com/trustwallet/blockatlas/pkg/numbers"
@@ -12,41 +9,6 @@ import (
 	"time"
 )
 
-type Platform struct {
-	client    Client
-	CoinIndex uint
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[p.CoinIndex]
-}
-
-func (p *Platform) ConfigKey() string {
-	return fmt.Sprintf("%s.api", p.Coin().Handle)
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	srcTxs, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-
-	txs := p.NormalizeTxs(srcTxs.Txs)
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	tagsList := []string{"transfer.recipient", "message.sender"}
 	var wg sync.WaitGroup
diff --git a/platform/cosmos/api_test.go b/platform/cosmos/transaction_test.go
similarity index 100%
rename from platform/cosmos/api_test.go
rename to platform/cosmos/transaction_test.go
diff --git a/platform/ethereum/base.go b/platform/ethereum/base.go
new file mode 100644
index 0000000..2d5aadd
--- /dev/null
+++ b/platform/ethereum/base.go
@@ -0,0 +1,35 @@
+package ethereum
+
+import (
+	"fmt"
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client            Client
+	collectionsClient CollectionsClient
+	CoinIndex         uint
+	RpcURL            string
+}
+
+func (p *Platform) Init() error {
+	handle := coin.Coins[p.CoinIndex].Handle
+
+	coinVar := fmt.Sprintf("%s.api", handle)
+	p.client = Client{blockatlas.InitClient(viper.GetString(coinVar))}
+
+	collectionsApiVar := fmt.Sprintf("%s.collections_api", handle)
+	p.collectionsClient = CollectionsClient{blockatlas.InitClient(viper.GetString(collectionsApiVar))}
+
+	collectionsApiKeyVar := fmt.Sprintf("%s.collections_api_key", handle)
+	p.collectionsClient.Headers["X-API-KEY"] = viper.GetString(collectionsApiKeyVar)
+
+	p.RpcURL = viper.GetString("ethereum.rpc")
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[p.CoinIndex]
+}
diff --git a/platform/ethereum/block.go b/platform/ethereum/block.go
new file mode 100644
index 0000000..6d6620b
--- /dev/null
+++ b/platform/ethereum/block.go
@@ -0,0 +1,26 @@
+package ethereum
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"strconv"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	if srcPage, err := p.client.GetBlockByNumber(num); err == nil {
+		var txs []blockatlas.Tx
+		for _, srcTx := range srcPage {
+			txs = AppendTxs(txs, &srcTx, p.CoinIndex)
+		}
+		return &blockatlas.Block{
+			Number: num,
+			ID:     strconv.FormatInt(num, 10),
+			Txs:    txs,
+		}, nil
+	} else {
+		return nil, err
+	}
+}
diff --git a/platform/ethereum/collection.go b/platform/ethereum/collection.go
index 0fedd89..ffd8cf5 100644
--- a/platform/ethereum/collection.go
+++ b/platform/ethereum/collection.go
@@ -6,6 +6,11 @@ import (
 	"strings"
 )
 
+var (
+	supportedTypes = map[string]bool{"ERC721": true, "ERC1155": true}
+	slugTokens     = map[string]bool{"ERC1155": true}
+)
+
 func (p *Platform) GetCollections(owner string) (blockatlas.CollectionPage, error) {
 	collections, err := p.collectionsClient.GetCollections(owner)
 	if err != nil {
@@ -223,3 +228,27 @@ func NormalizeCollectible(c *Collection, a Collectible, coinIndex uint) blockatl
 		Coin:             coinIndex,
 	}
 }
+
+func searchCollection(collections []Collection, collectibleID string) *Collection {
+	for _, i := range collections {
+		if strings.EqualFold(i.Slug, collectibleID) {
+			return &i
+		}
+	}
+	return nil
+}
+
+//TODO: remove once most of the clients will be updated (deadline: March 17th)
+func oldSearchCollection(collections []Collection, collectibleID string) *Collection {
+	for _, i := range collections {
+		if strings.EqualFold(i.Slug, collectibleID) {
+			return &i
+		}
+		for _, contract := range i.Contracts {
+			if strings.EqualFold(contract.Address, collectibleID) {
+				return &i
+			}
+		}
+	}
+	return nil
+}
diff --git a/platform/ethereum/collection_client.go b/platform/ethereum/collection_client.go
index c6689d1..39cadf6 100644
--- a/platform/ethereum/collection_client.go
+++ b/platform/ethereum/collection_client.go
@@ -5,7 +5,6 @@ import (
 	"github.com/trustwallet/blockatlas/pkg/errors"
 	"net/url"
 	"strconv"
-	"strings"
 )
 
 type CollectionsClient struct {
@@ -74,27 +73,3 @@ func (c CollectionsClient) OldGetCollectibles(owner string, collectibleID string
 	err = c.Get(&page, "api/v1/assets", query)
 	return collection, page.Collectibles, err
 }
-
-func searchCollection(collections []Collection, collectibleID string) *Collection {
-	for _, i := range collections {
-		if strings.EqualFold(i.Slug, collectibleID) {
-			return &i
-		}
-	}
-	return nil
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func oldSearchCollection(collections []Collection, collectibleID string) *Collection {
-	for _, i := range collections {
-		if strings.EqualFold(i.Slug, collectibleID) {
-			return &i
-		}
-		for _, contract := range i.Contracts {
-			if strings.EqualFold(contract.Address, collectibleID) {
-				return &i
-			}
-		}
-	}
-	return nil
-}
diff --git a/platform/ethereum/ens.go b/platform/ethereum/domain.go
similarity index 85%
rename from platform/ethereum/ens.go
rename to platform/ethereum/domain.go
index c692944..354a06d 100644
--- a/platform/ethereum/ens.go
+++ b/platform/ethereum/domain.go
@@ -23,7 +23,7 @@ func (p *Platform) Lookup(coins []uint64, name string) ([]blockatlas.Resolved, e
 	}
 	for _, coin := range coins {
 		// try to get multi coin address
-		address, err := addressForCoin(resolver, coin, name)
+		address, err := addressForCoin(resolver, coin)
 		if err != nil {
 			logger.Error(errors.E(err, errors.Params{"coin": coin, "name": name}))
 			continue
@@ -34,12 +34,12 @@ func (p *Platform) Lookup(coins []uint64, name string) ([]blockatlas.Resolved, e
 	return result, nil
 }
 
-func addressForCoin(resolver *ens.Resolver, coin uint64, name string) (string, error) {
+func addressForCoin(resolver *ens.Resolver, coin uint64) (string, error) {
 	address, err := resolver.MultiAddress(coin)
 	if err != nil {
 		if coin == CoinType.ETH {
 			// user may not set multi coin address
-			result, err := lookupLegacyETH(resolver, name)
+			result, err := lookupLegacyETH(resolver)
 			if err != nil {
 				return "", errors.E(err, "query legacy address failed")
 			}
@@ -54,7 +54,7 @@ func addressForCoin(resolver *ens.Resolver, coin uint64, name string) (string, e
 	return encoded, nil
 }
 
-func lookupLegacyETH(resolver *ens.Resolver, name string) (string, error) {
+func lookupLegacyETH(resolver *ens.Resolver) (string, error) {
 	address, err := resolver.Address()
 	if err != nil {
 		return "", errors.E(err, "query address failed")
diff --git a/platform/ethereum/token.go b/platform/ethereum/token.go
new file mode 100644
index 0000000..cce551a
--- /dev/null
+++ b/platform/ethereum/token.go
@@ -0,0 +1,40 @@
+package ethereum
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
+	account, err := p.client.GetTokens(address)
+	if err != nil {
+		return nil, err
+	}
+	return NormalizeTokens(account.Docs, *p), nil
+}
+
+// NormalizeToken converts a Ethereum token into the generic model
+func NormalizeToken(srcToken *Token, coinIndex uint) (t blockatlas.Token, ok bool) {
+	t = blockatlas.Token{
+		Name:     srcToken.Contract.Name,
+		Symbol:   srcToken.Contract.Symbol,
+		TokenID:  srcToken.Contract.Contract,
+		Coin:     coinIndex,
+		Decimals: srcToken.Contract.Decimals,
+		Type:     blockatlas.TokenTypeERC20,
+	}
+
+	return t, true
+}
+
+// NormalizeTxs converts multiple Ethereum tokens
+func NormalizeTokens(srcTokens []Token, p Platform) []blockatlas.Token {
+	tokenPage := make([]blockatlas.Token, 0)
+	for _, srcToken := range srcTokens {
+		token, ok := NormalizeToken(&srcToken, p.CoinIndex)
+		if !ok {
+			continue
+		}
+		tokenPage = append(tokenPage, token)
+	}
+	return tokenPage
+}
diff --git a/platform/ethereum/api.go b/platform/ethereum/transaction.go
similarity index 56%
rename from platform/ethereum/api.go
rename to platform/ethereum/transaction.go
index 8871d28..5b27f19 100644
--- a/platform/ethereum/api.go
+++ b/platform/ethereum/transaction.go
@@ -1,50 +1,15 @@
 package ethereum
 
 import (
-	"fmt"
 	"github.com/gin-gonic/gin"
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/address"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/logger"
 	"math/big"
 	"net/http"
-	"strconv"
 )
 
-var (
-	supportedTypes = map[string]bool{"ERC721": true, "ERC1155": true}
-	slugTokens     = map[string]bool{"ERC1155": true}
-)
-
-type Platform struct {
-	client            Client
-	collectionsClient CollectionsClient
-	CoinIndex         uint
-	RpcURL            string
-}
-
-func (p *Platform) Init() error {
-	handle := coin.Coins[p.CoinIndex].Handle
-
-	coinVar := fmt.Sprintf("%s.api", handle)
-	p.client = Client{blockatlas.InitClient(viper.GetString(coinVar))}
-
-	collectionsApiVar := fmt.Sprintf("%s.collections_api", handle)
-	p.collectionsClient = CollectionsClient{blockatlas.InitClient(viper.GetString(collectionsApiVar))}
-
-	collectionsApiKeyVar := fmt.Sprintf("%s.collections_api_key", handle)
-	p.collectionsClient.Headers["X-API-KEY"] = viper.GetString(collectionsApiKeyVar)
-
-	p.RpcURL = viper.GetString("ethereum.rpc")
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[p.CoinIndex]
-}
-
 func (p *Platform) RegisterRoutes(router gin.IRouter) {
 	router.GET("/:address", func(c *gin.Context) {
 		p.getTransactions(c)
@@ -173,58 +138,3 @@ func apiError(c *gin.Context, err error) bool {
 	}
 	return false
 }
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	if srcPage, err := p.client.GetBlockByNumber(num); err == nil {
-		var txs []blockatlas.Tx
-		for _, srcTx := range srcPage {
-			txs = AppendTxs(txs, &srcTx, p.CoinIndex)
-		}
-		return &blockatlas.Block{
-			Number: num,
-			ID:     strconv.FormatInt(num, 10),
-			Txs:    txs,
-		}, nil
-	} else {
-		return nil, err
-	}
-}
-
-func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
-	account, err := p.client.GetTokens(address)
-	if err != nil {
-		return nil, err
-	}
-	return NormalizeTokens(account.Docs, *p), nil
-}
-
-// NormalizeToken converts a Ethereum token into the generic model
-func NormalizeToken(srcToken *Token, coinIndex uint) (t blockatlas.Token, ok bool) {
-	t = blockatlas.Token{
-		Name:     srcToken.Contract.Name,
-		Symbol:   srcToken.Contract.Symbol,
-		TokenID:  srcToken.Contract.Contract,
-		Coin:     coinIndex,
-		Decimals: srcToken.Contract.Decimals,
-		Type:     blockatlas.TokenTypeERC20,
-	}
-
-	return t, true
-}
-
-// NormalizeTxs converts multiple Ethereum tokens
-func NormalizeTokens(srcTokens []Token, p Platform) []blockatlas.Token {
-	tokenPage := make([]blockatlas.Token, 0)
-	for _, srcToken := range srcTokens {
-		token, ok := NormalizeToken(&srcToken, p.CoinIndex)
-		if !ok {
-			continue
-		}
-		tokenPage = append(tokenPage, token)
-	}
-	return tokenPage
-}
diff --git a/platform/fio/base.go b/platform/fio/base.go
new file mode 100644
index 0000000..6db1586
--- /dev/null
+++ b/platform/fio/base.go
@@ -0,0 +1,20 @@
+package fio
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("fio.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.FIO]
+}
diff --git a/platform/fio/api.go b/platform/fio/domain.go
similarity index 58%
rename from platform/fio/api.go
rename to platform/fio/domain.go
index 0b293d9..4da1846 100644
--- a/platform/fio/api.go
+++ b/platform/fio/domain.go
@@ -1,28 +1,10 @@
 package fio
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("fio.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.FIO]
-}
-
-func (p *Platform) GetTxsByAddress(address string) (page blockatlas.TxPage, err error) {
-	return page, err
-}
-
 func (p *Platform) Lookup(coins []uint64, name string) ([]blockatlas.Resolved, error) {
 	var result []blockatlas.Resolved
 	for _, coinId := range coins {
diff --git a/platform/fio/transaction.go b/platform/fio/transaction.go
new file mode 100644
index 0000000..11f87ae
--- /dev/null
+++ b/platform/fio/transaction.go
@@ -0,0 +1,9 @@
+package fio
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) GetTxsByAddress(address string) (page blockatlas.TxPage, err error) {
+	return page, err
+}
diff --git a/platform/harmony/base.go b/platform/harmony/base.go
new file mode 100644
index 0000000..75bce0e
--- /dev/null
+++ b/platform/harmony/base.go
@@ -0,0 +1,21 @@
+package harmony
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("harmony.api"))}
+	p.client.Headers["Content-Type"] = "application/json"
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.ONE]
+}
diff --git a/platform/harmony/block.go b/platform/harmony/block.go
new file mode 100644
index 0000000..c6e90cb
--- /dev/null
+++ b/platform/harmony/block.go
@@ -0,0 +1,30 @@
+package harmony
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	srcBlock, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+	block := p.NormalizeBlock(&srcBlock)
+	return &block, nil
+}
+
+func (p *Platform) NormalizeBlock(block *BlockInfo) blockatlas.Block {
+	blockNumber, err := hexToInt(block.Number)
+	if err != nil {
+		return blockatlas.Block{}
+	}
+	return blockatlas.Block{
+		ID:     block.Hash,
+		Number: int64(blockNumber),
+		Txs:    NormalizeTxs(block.Transactions),
+	}
+}
diff --git a/platform/harmony/api.go b/platform/harmony/transaction.go
similarity index 71%
rename from platform/harmony/api.go
rename to platform/harmony/transaction.go
index 2197a34..a83a6dd 100644
--- a/platform/harmony/api.go
+++ b/platform/harmony/transaction.go
@@ -1,7 +1,6 @@
 package harmony
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/errors"
@@ -9,20 +8,6 @@ import (
 	"strconv"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("harmony.api"))}
-	p.client.Headers["Content-Type"] = "application/json"
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.ONE]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	result, err := p.client.GetTxsOfAddress(address)
 	if err != nil {
@@ -31,31 +16,6 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return NormalizeTxs(result.Transactions), err
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	srcBlock, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-	block := p.NormalizeBlock(&srcBlock)
-	return &block, nil
-}
-
-func (p *Platform) NormalizeBlock(block *BlockInfo) blockatlas.Block {
-	blockNumber, err := hexToInt(block.Number)
-	if err != nil {
-		return blockatlas.Block{}
-	}
-	return blockatlas.Block{
-		ID:     block.Hash,
-		Number: int64(blockNumber),
-		Txs:    NormalizeTxs(block.Transactions),
-	}
-}
-
 func NormalizeTxs(txs []Transaction) blockatlas.TxPage {
 	normalizeTxs := make([]blockatlas.Tx, 0)
 	for _, srcTx := range txs {
diff --git a/platform/harmony/api_test.go b/platform/harmony/transaction_test.go
similarity index 100%
rename from platform/harmony/api_test.go
rename to platform/harmony/transaction_test.go
diff --git a/platform/icon/base.go b/platform/icon/base.go
new file mode 100644
index 0000000..74c785a
--- /dev/null
+++ b/platform/icon/base.go
@@ -0,0 +1,20 @@
+package icon
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("icon.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.ICX]
+}
diff --git a/platform/icon/api.go b/platform/icon/transaction.go
similarity index 85%
rename from platform/icon/api.go
rename to platform/icon/transaction.go
index 06faf72..0742e94 100644
--- a/platform/icon/api.go
+++ b/platform/icon/transaction.go
@@ -1,7 +1,6 @@
 package icon
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/errors"
@@ -10,19 +9,6 @@ import (
 	"time"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("icon.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.ICX]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	trxs, err := p.client.GetAddressTransactions(address)
 	if err != nil {
diff --git a/platform/icon/api_test.go b/platform/icon/transaction_test.go
similarity index 100%
rename from platform/icon/api_test.go
rename to platform/icon/transaction_test.go
diff --git a/platform/iotex/base.go b/platform/iotex/base.go
new file mode 100644
index 0000000..e9de7da
--- /dev/null
+++ b/platform/iotex/base.go
@@ -0,0 +1,20 @@
+package iotex
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("iotex.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.IOTX]
+}
diff --git a/platform/iotex/block.go b/platform/iotex/block.go
new file mode 100644
index 0000000..6e5d3f5
--- /dev/null
+++ b/platform/iotex/block.go
@@ -0,0 +1,29 @@
+package iotex
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetLatestBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	var normalized []blockatlas.Tx
+	txs, err := p.client.GetTxsInBlock(num)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, action := range txs {
+		tx := Normalize(action)
+		if tx != nil {
+			normalized = append(normalized, *tx)
+		}
+	}
+
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    normalized,
+	}, nil
+}
diff --git a/platform/iotex/api.go b/platform/iotex/transaction.go
similarity index 70%
rename from platform/iotex/api.go
rename to platform/iotex/transaction.go
index 18b2949..03f5086 100644
--- a/platform/iotex/api.go
+++ b/platform/iotex/transaction.go
@@ -5,47 +5,9 @@ import (
 	"strconv"
 	"time"
 
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("iotex.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.IOTX]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetLatestBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	var normalized []blockatlas.Tx
-	txs, err := p.client.GetTxsInBlock(num)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, action := range txs {
-		tx := Normalize(action)
-		if tx != nil {
-			normalized = append(normalized, *tx)
-		}
-	}
-
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    normalized,
-	}, nil
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	txs := make([]blockatlas.Tx, 0)
 	var start int64
diff --git a/platform/iotex/api_test.go b/platform/iotex/transaction_test.go
similarity index 100%
rename from platform/iotex/api_test.go
rename to platform/iotex/transaction_test.go
diff --git a/platform/nano/base.go b/platform/nano/base.go
new file mode 100644
index 0000000..08cac55
--- /dev/null
+++ b/platform/nano/base.go
@@ -0,0 +1,22 @@
+package nano
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+
+	"github.com/spf13/viper"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("nano.api"))}
+	p.client.Headers["Content-Type"] = "application/json"
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.NANO]
+}
diff --git a/platform/nano/api.go b/platform/nano/transaction.go
similarity index 80%
rename from platform/nano/api.go
rename to platform/nano/transaction.go
index 95b57fe..2e9ff1b 100644
--- a/platform/nano/api.go
+++ b/platform/nano/transaction.go
@@ -4,26 +4,9 @@ import (
 	"encoding/json"
 	"strconv"
 
-	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-
-	"github.com/spf13/viper"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("nano.api"))}
-	p.client.Headers["Content-Type"] = "application/json"
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.NANO]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	normalized := make([]blockatlas.Tx, 0)
 	history, err := p.client.GetAccountHistory(address)
diff --git a/platform/nano/api_test.go b/platform/nano/transaction_test.go
similarity index 100%
rename from platform/nano/api_test.go
rename to platform/nano/transaction_test.go
diff --git a/platform/nebulas/base.go b/platform/nebulas/base.go
new file mode 100644
index 0000000..2b03679
--- /dev/null
+++ b/platform/nebulas/base.go
@@ -0,0 +1,20 @@
+package nebulas
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("nebulas.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.NAS]
+}
diff --git a/platform/nebulas/api.go b/platform/nebulas/transaction.go
similarity index 85%
rename from platform/nebulas/api.go
rename to platform/nebulas/transaction.go
index 0a1f269..d31df4d 100644
--- a/platform/nebulas/api.go
+++ b/platform/nebulas/transaction.go
@@ -1,24 +1,10 @@
 package nebulas
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("nebulas.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.NAS]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	txs, err := p.client.GetTxs(address, 1)
 	if err != nil {
diff --git a/platform/nebulas/api_test.go b/platform/nebulas/transaction_test.go
similarity index 100%
rename from platform/nebulas/api_test.go
rename to platform/nebulas/transaction_test.go
diff --git a/platform/nimiq/base.go b/platform/nimiq/base.go
new file mode 100644
index 0000000..e64e822
--- /dev/null
+++ b/platform/nimiq/base.go
@@ -0,0 +1,20 @@
+package nimiq
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("nimiq.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.NIM]
+}
diff --git a/platform/nimiq/block.go b/platform/nimiq/block.go
new file mode 100644
index 0000000..e245847
--- /dev/null
+++ b/platform/nimiq/block.go
@@ -0,0 +1,27 @@
+package nimiq
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	srcBlock, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+	block := NormalizeBlock(srcBlock)
+	return &block, nil
+}
+
+// NormalizeBlock converts a Nimiq block into the generic model
+func NormalizeBlock(srcBlock *Block) blockatlas.Block {
+	return blockatlas.Block{
+		Number: srcBlock.Number,
+		ID:     srcBlock.Hash,
+		Txs:    NormalizeTxs(srcBlock.Txs),
+	}
+}
diff --git a/platform/nimiq/api.go b/platform/nimiq/transaction.go
similarity index 55%
rename from platform/nimiq/api.go
rename to platform/nimiq/transaction.go
index 021e9c0..dc220aa 100644
--- a/platform/nimiq/api.go
+++ b/platform/nimiq/transaction.go
@@ -1,37 +1,10 @@
 package nimiq
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("nimiq.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.NIM]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	srcBlock, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-	block := NormalizeBlock(srcBlock)
-	return &block, nil
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	srcTxs, err := p.client.GetTxsOfAddress(address)
 	if err != nil {
@@ -67,12 +40,3 @@ func NormalizeTxs(srcTxs []Tx) []blockatlas.Tx {
 	}
 	return txs
 }
-
-// NormalizeBlock converts a Nimiq block into the generic model
-func NormalizeBlock(srcBlock *Block) blockatlas.Block {
-	return blockatlas.Block{
-		Number: srcBlock.Number,
-		ID:     srcBlock.Hash,
-		Txs:    NormalizeTxs(srcBlock.Txs),
-	}
-}
diff --git a/platform/nimiq/api_test.go b/platform/nimiq/transaction_test.go
similarity index 100%
rename from platform/nimiq/api_test.go
rename to platform/nimiq/transaction_test.go
diff --git a/platform/ontology/base.go b/platform/ontology/base.go
new file mode 100644
index 0000000..dd5ce04
--- /dev/null
+++ b/platform/ontology/base.go
@@ -0,0 +1,20 @@
+package ontology
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	blockatlas "github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("ontology.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.ONT]
+}
diff --git a/platform/ontology/block.go b/platform/ontology/block.go
new file mode 100644
index 0000000..f656e0c
--- /dev/null
+++ b/platform/ontology/block.go
@@ -0,0 +1,33 @@
+package ontology
+
+import (
+	blockatlas "github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/errors"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	block, err := p.client.CurrentBlockNumber()
+	if err != nil {
+		return 0, errors.E(err, "CurrentBlockNumber")
+	}
+	if len(block.Result.Records) == 0 {
+		return 0, errors.E("invalid block height result")
+	}
+	return block.Result.Records[0].Height, nil
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	blockOnt, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+	txsRaw, err := p.getTxDetails(blockOnt.Result.Txs)
+	if err != nil {
+		return nil, err
+	}
+	txs := normalizeTxs(txsRaw, AssetAll)
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
diff --git a/platform/ontology/api.go b/platform/ontology/transaction.go
similarity index 81%
rename from platform/ontology/api.go
rename to platform/ontology/transaction.go
index 6213e63..c2fb048 100644
--- a/platform/ontology/api.go
+++ b/platform/ontology/transaction.go
@@ -1,7 +1,6 @@
 package ontology
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	blockatlas "github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/errors"
@@ -10,19 +9,6 @@ import (
 	"sync"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("ontology.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.ONT]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return p.GetTokenTxsByAddress(address, string(AssetONT))
 }
@@ -121,33 +107,6 @@ func normalizeONG(tx blockatlas.Tx, transfers Transfers) (blockatlas.Tx, bool) {
 	return tx, true
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	block, err := p.client.CurrentBlockNumber()
-	if err != nil {
-		return 0, errors.E(err, "CurrentBlockNumber")
-	}
-	if len(block.Result.Records) == 0 {
-		return 0, errors.E("invalid block height result")
-	}
-	return block.Result.Records[0].Height, nil
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	blockOnt, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-	txsRaw, err := p.getTxDetails(blockOnt.Result.Txs)
-	if err != nil {
-		return nil, err
-	}
-	txs := normalizeTxs(txsRaw, AssetAll)
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
 func (p *Platform) getTxDetails(srcTx []Tx) ([]Tx, error) {
 	var wg sync.WaitGroup
 	txsOntV2Chan := make(chan Tx, len(srcTx))
diff --git a/platform/ontology/api_test.go b/platform/ontology/transaction_test.go
similarity index 100%
rename from platform/ontology/api_test.go
rename to platform/ontology/transaction_test.go
diff --git a/platform/polkadot/base.go b/platform/polkadot/base.go
new file mode 100644
index 0000000..e2342dd
--- /dev/null
+++ b/platform/polkadot/base.go
@@ -0,0 +1,26 @@
+package polkadot
+
+import (
+	"fmt"
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	CoinIndex uint
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[p.CoinIndex]
+}
+
+func (p *Platform) ConfigKey() string {
+	return fmt.Sprintf("%s.api", p.Coin().Handle)
+}
diff --git a/platform/polkadot/block.go b/platform/polkadot/block.go
new file mode 100644
index 0000000..7008fe9
--- /dev/null
+++ b/platform/polkadot/block.go
@@ -0,0 +1,21 @@
+package polkadot
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetCurrentBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
+		txs := p.NormalizeExtrinsics(srcBlock)
+		return &blockatlas.Block{
+			Number: num,
+			Txs:    txs,
+		}, nil
+	} else {
+		return nil, err
+	}
+}
diff --git a/platform/polkadot/api.go b/platform/polkadot/transaction.go
similarity index 79%
rename from platform/polkadot/api.go
rename to platform/polkadot/transaction.go
index 4de110c..1685c71 100644
--- a/platform/polkadot/api.go
+++ b/platform/polkadot/transaction.go
@@ -7,34 +7,14 @@ import (
 	"github.com/trustwallet/blockatlas/pkg/numbers"
 	"strings"
 
-	"github.com/spf13/viper"
-	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 )
 
-type Platform struct {
-	client    Client
-	CoinIndex uint
-}
-
 var NetworkByteMap = map[string]byte{
 	"DOT": 0x00,
 	"KSM": 0x02,
 }
 
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString(p.ConfigKey()))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[p.CoinIndex]
-}
-
-func (p *Platform) ConfigKey() string {
-	return fmt.Sprintf("%s.api", p.Coin().Handle)
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	transfers, err := p.client.GetTransfersOfAddress(address)
 	if err != nil {
@@ -50,22 +30,6 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return txs, nil
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetCurrentBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
-		txs := p.NormalizeExtrinsics(srcBlock)
-		return &blockatlas.Block{
-			Number: num,
-			Txs:    txs,
-		}, nil
-	} else {
-		return nil, err
-	}
-}
-
 func (p *Platform) NormalizeTransfer(srcTx *Transfer) blockatlas.Tx {
 	decimals := p.Coin().Decimals
 	amount := strings.Split(numbers.DecimalExp(srcTx.Amount, int(decimals)), ".")[0]
diff --git a/platform/polkadot/api_test.go b/platform/polkadot/transaction_test.go
similarity index 100%
rename from platform/polkadot/api_test.go
rename to platform/polkadot/transaction_test.go
diff --git a/platform/ripple/base.go b/platform/ripple/base.go
new file mode 100644
index 0000000..83be177
--- /dev/null
+++ b/platform/ripple/base.go
@@ -0,0 +1,20 @@
+package ripple
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("ripple.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.XRP]
+}
diff --git a/platform/ripple/block.go b/platform/ripple/block.go
new file mode 100644
index 0000000..6509272
--- /dev/null
+++ b/platform/ripple/block.go
@@ -0,0 +1,21 @@
+package ripple
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetCurrentBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
+		txs := NormalizeTxs(srcBlock)
+		return &blockatlas.Block{
+			Number: num,
+			Txs:    txs,
+		}, nil
+	} else {
+		return nil, err
+	}
+}
diff --git a/platform/ripple/api.go b/platform/ripple/transaction.go
similarity index 74%
rename from platform/ripple/api.go
rename to platform/ripple/transaction.go
index 9397e87..84f0fd8 100644
--- a/platform/ripple/api.go
+++ b/platform/ripple/transaction.go
@@ -1,26 +1,12 @@
 package ripple
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"strconv"
 	"time"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("ripple.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.XRP]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	s, err := p.client.GetTxsOfAddress(address)
 	if err != nil {
@@ -39,22 +25,6 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return txs, nil
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetCurrentBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
-		txs := NormalizeTxs(srcBlock)
-		return &blockatlas.Block{
-			Number: num,
-			Txs:    txs,
-		}, nil
-	} else {
-		return nil, err
-	}
-}
-
 func NormalizeTxs(srcTxs []Tx) (txs []blockatlas.Tx) {
 	for _, srcTx := range srcTxs {
 		tx, ok := NormalizeTx(&srcTx)
diff --git a/platform/ripple/api_test.go b/platform/ripple/transaction_test.go
similarity index 100%
rename from platform/ripple/api_test.go
rename to platform/ripple/transaction_test.go
diff --git a/platform/stellar/base.go b/platform/stellar/base.go
new file mode 100644
index 0000000..67a7ec7
--- /dev/null
+++ b/platform/stellar/base.go
@@ -0,0 +1,24 @@
+package stellar
+
+import (
+	"fmt"
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	CoinIndex uint
+}
+
+func (p *Platform) Init() error {
+	handle := coin.Coins[p.CoinIndex].Handle
+	api := fmt.Sprintf("%s.api", handle)
+	p.client = Client{blockatlas.InitClient(viper.GetString(api))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[p.CoinIndex]
+}
diff --git a/platform/stellar/block.go b/platform/stellar/block.go
new file mode 100644
index 0000000..0de588d
--- /dev/null
+++ b/platform/stellar/block.go
@@ -0,0 +1,25 @@
+package stellar
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
+		block := p.NormalizeBlock(srcBlock)
+		return &block, nil
+	} else {
+		return nil, err
+	}
+}
+func (p *Platform) NormalizeBlock(block *Block) blockatlas.Block {
+	return blockatlas.Block{
+		ID:     block.Ledger.Id,
+		Number: block.Ledger.Sequence,
+		Txs:    p.NormalizePayments(block.Payments),
+	}
+}
diff --git a/platform/stellar/api.go b/platform/stellar/transaction.go
similarity index 71%
rename from platform/stellar/api.go
rename to platform/stellar/transaction.go
index 8cb86dd..53cd01c 100644
--- a/platform/stellar/api.go
+++ b/platform/stellar/transaction.go
@@ -1,8 +1,6 @@
 package stellar
 
 import (
-	"fmt"
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/numbers"
@@ -11,22 +9,6 @@ import (
 	"time"
 )
 
-type Platform struct {
-	client    Client
-	CoinIndex uint
-}
-
-func (p *Platform) Init() error {
-	handle := coin.Coins[p.CoinIndex].Handle
-	api := fmt.Sprintf("%s.api", handle)
-	p.client = Client{blockatlas.InitClient(viper.GetString(api))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[p.CoinIndex]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	payments, err := p.client.GetTxsOfAddress(address)
 	if err != nil {
@@ -36,27 +18,6 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return p.NormalizePayments(payments), nil
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	if srcBlock, err := p.client.GetBlockByNumber(num); err == nil {
-		block := p.NormalizeBlock(srcBlock)
-		return &block, nil
-	} else {
-		return nil, err
-	}
-}
-
-func (p *Platform) NormalizeBlock(block *Block) blockatlas.Block {
-	return blockatlas.Block{
-		ID:     block.Ledger.Id,
-		Number: block.Ledger.Sequence,
-		Txs:    p.NormalizePayments(block.Payments),
-	}
-}
-
 func (p *Platform) NormalizePayments(payments []Payment) (txs []blockatlas.Tx) {
 	var (
 		wg      sync.WaitGroup
diff --git a/platform/stellar/api_test.go b/platform/stellar/transaction_test.go
similarity index 100%
rename from platform/stellar/api_test.go
rename to platform/stellar/transaction_test.go
diff --git a/platform/tezos/base.go b/platform/tezos/base.go
new file mode 100644
index 0000000..0aad538
--- /dev/null
+++ b/platform/tezos/base.go
@@ -0,0 +1,23 @@
+package tezos
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client    Client
+	rpcClient RpcClient
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("tezos.api"))}
+	p.client.SetTimeout(35)
+	p.rpcClient = RpcClient{blockatlas.InitClient(viper.GetString("tezos.rpc"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.XTZ]
+}
diff --git a/platform/tezos/block.go b/platform/tezos/block.go
new file mode 100644
index 0000000..2a219b5
--- /dev/null
+++ b/platform/tezos/block.go
@@ -0,0 +1,40 @@
+package tezos
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/logger"
+	"sync"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetCurrentBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	txTypes := []TxType{TxTransactions, TxDelegations}
+	var wg sync.WaitGroup
+	out := make(chan []Transaction, len(txTypes))
+	wg.Add(len(txTypes))
+	for _, t := range txTypes {
+		go func(txType TxType, num int64, wg *sync.WaitGroup) {
+			defer wg.Done()
+			txs, err := p.client.GetBlockByNumber(num, txType)
+			if err != nil {
+				logger.Error("GetAddrTxs", err, logger.Params{"txType": txType, "num": num})
+				return
+			}
+			out <- txs
+		}(t, num, &wg)
+	}
+	wg.Wait()
+	close(out)
+	srcTxs := make([]Transaction, 0)
+	for r := range out {
+		srcTxs = append(srcTxs, r...)
+	}
+	txs := NormalizeTxs(srcTxs)
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
diff --git a/platform/tezos/stake.go b/platform/tezos/stake.go
index e4bf4a3..bd46c6d 100644
--- a/platform/tezos/stake.go
+++ b/platform/tezos/stake.go
@@ -6,6 +6,8 @@ import (
 	services "github.com/trustwallet/blockatlas/services/assets"
 )
 
+const Annual = 6.09
+
 func (p *Platform) GetDelegations(address string) (blockatlas.DelegationsPage, error) {
 	account, err := p.rpcClient.GetAccount(address)
 	if err != nil {
diff --git a/platform/tezos/api.go b/platform/tezos/transaction.go
similarity index 64%
rename from platform/tezos/api.go
rename to platform/tezos/transaction.go
index 1ae3b18..354c795 100644
--- a/platform/tezos/api.go
+++ b/platform/tezos/transaction.go
@@ -1,31 +1,12 @@
 package tezos
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/logger"
 	"sync"
 )
 
-type Platform struct {
-	client    Client
-	rpcClient RpcClient
-}
-
-const Annual = 6.09
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("tezos.api"))}
-	p.client.SetTimeout(35)
-	p.rpcClient = RpcClient{blockatlas.InitClient(viper.GetString("tezos.rpc"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.XTZ]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	txTypes := []TxType{TxTransactions}
 	var wg sync.WaitGroup
@@ -51,39 +32,6 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	return NormalizeTxs(srcTxs), nil
 }
 
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetCurrentBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	txTypes := []TxType{TxTransactions, TxDelegations}
-	var wg sync.WaitGroup
-	out := make(chan []Transaction, len(txTypes))
-	wg.Add(len(txTypes))
-	for _, t := range txTypes {
-		go func(txType TxType, num int64, wg *sync.WaitGroup) {
-			defer wg.Done()
-			txs, err := p.client.GetBlockByNumber(num, txType)
-			if err != nil {
-				logger.Error("GetAddrTxs", err, logger.Params{"txType": txType, "num": num})
-				return
-			}
-			out <- txs
-		}(t, num, &wg)
-	}
-	wg.Wait()
-	close(out)
-	srcTxs := make([]Transaction, 0)
-	for r := range out {
-		srcTxs = append(srcTxs, r...)
-	}
-	txs := NormalizeTxs(srcTxs)
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
 func NormalizeTxs(srcTxs []Transaction) (txs []blockatlas.Tx) {
 	for _, srcTx := range srcTxs {
 		tx, ok := NormalizeTx(srcTx)
diff --git a/platform/tezos/api_test.go b/platform/tezos/transaction_test.go
similarity index 100%
rename from platform/tezos/api_test.go
rename to platform/tezos/transaction_test.go
diff --git a/platform/theta/base.go b/platform/theta/base.go
new file mode 100644
index 0000000..c7db94c
--- /dev/null
+++ b/platform/theta/base.go
@@ -0,0 +1,20 @@
+package theta
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("theta.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.THETA]
+}
diff --git a/platform/theta/api.go b/platform/theta/transaction.go
similarity index 89%
rename from platform/theta/api.go
rename to platform/theta/transaction.go
index 9697ff1..72b192d 100644
--- a/platform/theta/api.go
+++ b/platform/theta/transaction.go
@@ -1,25 +1,11 @@
 package theta
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"strconv"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("theta.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.THETA]
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	// Endpoint supports queries without token query parameter
 	return p.GetTokenTxsByAddress(address, "")
diff --git a/platform/theta/api_test.go b/platform/theta/transaction_test.go
similarity index 100%
rename from platform/theta/api_test.go
rename to platform/theta/transaction_test.go
diff --git a/platform/tron/api.go b/platform/tron/api.go
deleted file mode 100644
index d21ff4f..0000000
--- a/platform/tron/api.go
+++ /dev/null
@@ -1,257 +0,0 @@
-package tron
-
-import (
-	"encoding/hex"
-	"github.com/spf13/viper"
-	"github.com/trustwallet/blockatlas/coin"
-	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"github.com/trustwallet/blockatlas/pkg/errors"
-	"github.com/trustwallet/blockatlas/pkg/logger"
-	"sync"
-)
-
-type Platform struct {
-	client Client
-}
-
-const Annual = 0.74
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("tron.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.TRX]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.CurrentBlockNumber()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	block, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-
-	txsChan := p.NormalizeBlockTxs(block.Txs)
-	txs := make(blockatlas.TxPage, 0)
-	for cTxs := range txsChan {
-		txs = append(txs, cTxs)
-	}
-
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
-func (p *Platform) NormalizeBlockTxs(srcTxs []Tx) chan blockatlas.Tx {
-	txChan := make(chan blockatlas.Tx, len(srcTxs))
-	var wg sync.WaitGroup
-	for _, srcTx := range srcTxs {
-		wg.Add(1)
-		go func(s Tx, c chan blockatlas.Tx) {
-			defer wg.Done()
-			p.NormalizeBlockChannel(s, c)
-		}(srcTx, txChan)
-	}
-	wg.Wait()
-	close(txChan)
-	return txChan
-}
-
-func (p *Platform) NormalizeBlockChannel(srcTx Tx, txChan chan blockatlas.Tx) {
-	if len(srcTx.Data.Contracts) == 0 {
-		return
-	}
-
-	tx, err := Normalize(srcTx)
-	if err != nil {
-		return
-	}
-	transfer := srcTx.Data.Contracts[0].Parameter.Value
-	if len(transfer.AssetName) > 0 {
-		assetName, err := hex.DecodeString(transfer.AssetName[:])
-		if err == nil {
-			info, err := p.client.GetTokenInfo(string(assetName))
-			if err == nil && len(info.Data) > 0 {
-				setTokenMeta(tx, srcTx, info.Data[0])
-			}
-		}
-	}
-	txChan <- *tx
-}
-
-func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
-	Txs, err := p.client.GetTxsOfAddress(address, "")
-	if err != nil && len(Txs) == 0 {
-		return nil, err
-	}
-
-	txs := make(blockatlas.TxPage, 0)
-	for _, srcTx := range Txs {
-		tx, err := Normalize(srcTx)
-		if err != nil {
-			continue
-		}
-
-		if len(srcTx.Data.Contracts) > 0 && srcTx.Data.Contracts[0].Type == TransferContract {
-			txs = append(txs, *tx)
-		} else {
-			continue
-		}
-	}
-
-	return txs, nil
-}
-
-func (p *Platform) GetTokenTxsByAddress(address, token string) (blockatlas.TxPage, error) {
-	tokenTxs, err := p.client.GetTxsOfAddress(address, token)
-	if err != nil {
-		return nil, errors.E(err, "TRON: failed to get token from address", errors.TypePlatformApi,
-			errors.Params{"address": address, "token": token}).PushToSentry()
-	}
-
-	txs := make(blockatlas.TxPage, 0)
-
-	if len(tokenTxs) == 0 {
-		return txs, nil
-	}
-
-	info, err := p.client.GetTokenInfo(token)
-	if err != nil || len(info.Data) == 0 {
-		return nil, errors.E(err, "TRON: failed to get token info", errors.TypePlatformApi,
-			errors.Params{"address": address, "token": token}).PushToSentry()
-	}
-
-	for _, srcTx := range tokenTxs {
-		tx, err := Normalize(srcTx)
-		if err != nil {
-			logger.Error(err)
-			continue
-		}
-		setTokenMeta(tx, srcTx, info.Data[0])
-		txs = append(txs, *tx)
-	}
-
-	return txs, nil
-}
-
-func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
-	tokens, err := p.client.GetAccount(address)
-	if err != nil {
-		return nil, err
-	}
-	tokenPage := make(blockatlas.TokenPage, 0)
-	if len(tokens.Data) == 0 {
-		return tokenPage, nil
-	}
-
-	var tokenIds []string
-	for _, v := range tokens.Data[0].AssetsV2 {
-		tokenIds = append(tokenIds, v.Key)
-	}
-
-	tokensChan := p.getTokens(tokenIds)
-	for info := range tokensChan {
-		tokenPage = append(tokenPage, info)
-	}
-	return tokenPage, nil
-}
-
-func (p *Platform) getTokens(ids []string) chan blockatlas.Token {
-	tkChan := make(chan blockatlas.Token, len(ids))
-	var wg sync.WaitGroup
-	for _, id := range ids {
-		wg.Add(1)
-		go func(i string, c chan blockatlas.Token) {
-			defer wg.Done()
-			err := p.getTokensChannel(i, c)
-			if err != nil {
-				logger.Error(err)
-			}
-		}(id, tkChan)
-	}
-	wg.Wait()
-	close(tkChan)
-	return tkChan
-}
-
-func (p *Platform) getTokensChannel(id string, tkChan chan blockatlas.Token) error {
-	info, err := p.client.GetTokenInfo(id)
-	if err != nil || len(info.Data) == 0 {
-		logger.Error(err, "GetTokenInfo: invalid token")
-		return err
-	}
-	asset := NormalizeToken(info.Data[0])
-	tkChan <- asset
-	return nil
-}
-
-func NormalizeToken(info AssetInfo) blockatlas.Token {
-	return blockatlas.Token{
-		Name:     info.Name,
-		Symbol:   info.Symbol,
-		TokenID:  info.ID,
-		Coin:     coin.TRX,
-		Decimals: info.Decimals,
-		Type:     blockatlas.TokenTypeTRC10,
-	}
-}
-
-func setTokenMeta(tx *blockatlas.Tx, srcTx Tx, tokenInfo AssetInfo) {
-	transfer := srcTx.Data.Contracts[0].Parameter.Value
-	tx.Meta = blockatlas.TokenTransfer{
-		Name:     tokenInfo.Name,
-		Symbol:   tokenInfo.Symbol,
-		TokenID:  tokenInfo.ID,
-		Decimals: tokenInfo.Decimals,
-		Value:    transfer.Amount,
-		From:     tx.From,
-		To:       tx.To,
-	}
-}
-
-/// Normalize converts a Tron transaction into the generic model
-func Normalize(srcTx Tx) (*blockatlas.Tx, error) {
-	if len(srcTx.Data.Contracts) == 0 {
-		return nil, errors.E("TRON: transfer without contract", errors.TypePlatformApi,
-			errors.Params{"tx": srcTx}).PushToSentry()
-	}
-
-	contract := srcTx.Data.Contracts[0]
-	if contract.Type != TransferContract && contract.Type != TransferAssetContract {
-		return nil, errors.E("TRON: invalid contract transfer", errors.TypePlatformApi,
-			errors.Params{"tx": srcTx, "type": contract.Type})
-	}
-
-	transfer := contract.Parameter.Value
-	from, err := HexToAddress(transfer.OwnerAddress)
-	if err != nil {
-		return nil, errors.E(err, "TRON: failed to get from address", errors.TypePlatformApi,
-			errors.Params{"tx": srcTx}).PushToSentry()
-	}
-	to, err := HexToAddress(transfer.ToAddress)
-	if err != nil {
-		return nil, errors.E(err, "TRON: failed to get to address", errors.TypePlatformApi,
-			errors.Params{"tx": srcTx}).PushToSentry()
-	}
-
-	return &blockatlas.Tx{
-		ID:     srcTx.ID,
-		Coin:   coin.TRX,
-		Date:   srcTx.BlockTime / 1000,
-		From:   from,
-		To:     to,
-		Fee:    "0",
-		Block:  0,
-		Status: blockatlas.StatusCompleted,
-		Meta: blockatlas.Transfer{
-			Value:    transfer.Amount,
-			Symbol:   coin.Coins[coin.TRX].Symbol,
-			Decimals: coin.Coins[coin.TRX].Decimals,
-		},
-	}, nil
-}
diff --git a/platform/tron/base.go b/platform/tron/base.go
new file mode 100644
index 0000000..c713d98
--- /dev/null
+++ b/platform/tron/base.go
@@ -0,0 +1,20 @@
+package tron
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("tron.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.TRX]
+}
diff --git a/platform/tron/block.go b/platform/tron/block.go
new file mode 100644
index 0000000..dba8310
--- /dev/null
+++ b/platform/tron/block.go
@@ -0,0 +1,66 @@
+package tron
+
+import (
+	"encoding/hex"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"sync"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.CurrentBlockNumber()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	block, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+
+	txsChan := p.NormalizeBlockTxs(block.Txs)
+	txs := make(blockatlas.TxPage, 0)
+	for cTxs := range txsChan {
+		txs = append(txs, cTxs)
+	}
+
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
+
+func (p *Platform) NormalizeBlockTxs(srcTxs []Tx) chan blockatlas.Tx {
+	txChan := make(chan blockatlas.Tx, len(srcTxs))
+	var wg sync.WaitGroup
+	for _, srcTx := range srcTxs {
+		wg.Add(1)
+		go func(s Tx, c chan blockatlas.Tx) {
+			defer wg.Done()
+			p.NormalizeBlockChannel(s, c)
+		}(srcTx, txChan)
+	}
+	wg.Wait()
+	close(txChan)
+	return txChan
+}
+
+func (p *Platform) NormalizeBlockChannel(srcTx Tx, txChan chan blockatlas.Tx) {
+	if len(srcTx.Data.Contracts) == 0 {
+		return
+	}
+
+	tx, err := Normalize(srcTx)
+	if err != nil {
+		return
+	}
+	transfer := srcTx.Data.Contracts[0].Parameter.Value
+	if len(transfer.AssetName) > 0 {
+		assetName, err := hex.DecodeString(transfer.AssetName[:])
+		if err == nil {
+			info, err := p.client.GetTokenInfo(string(assetName))
+			if err == nil && len(info.Data) > 0 {
+				setTokenMeta(tx, srcTx, info.Data[0])
+			}
+		}
+	}
+	txChan <- *tx
+}
diff --git a/platform/tron/stake.go b/platform/tron/stake.go
index 817b856..c3886dd 100644
--- a/platform/tron/stake.go
+++ b/platform/tron/stake.go
@@ -1,6 +1,7 @@
 package tron
 
 import (
+	"github.com/trustwallet/blockatlas/pkg/address"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/errors"
 	"github.com/trustwallet/blockatlas/pkg/logger"
@@ -9,6 +10,8 @@ import (
 	"time"
 )
 
+const Annual = 0.74
+
 func (p *Platform) GetValidators() (blockatlas.ValidatorPage, error) {
 	results := make(blockatlas.ValidatorPage, 0)
 	validators, err := p.client.GetValidators()
@@ -67,7 +70,7 @@ func (p *Platform) UndelegatedBalance(address string) (string, error) {
 }
 
 func normalizeValidator(v Validator) (validator blockatlas.Validator, ok bool) {
-	address, err := HexToAddress(v.Address)
+	address, err := address.HexToAddress(v.Address)
 	if err != nil {
 		return validator, false
 	}
diff --git a/platform/tron/token.go b/platform/tron/token.go
new file mode 100644
index 0000000..fc7f21f
--- /dev/null
+++ b/platform/tron/token.go
@@ -0,0 +1,70 @@
+package tron
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/logger"
+	"sync"
+)
+
+func (p *Platform) GetTokenListByAddress(address string) (blockatlas.TokenPage, error) {
+	tokens, err := p.client.GetAccount(address)
+	if err != nil {
+		return nil, err
+	}
+	tokenPage := make(blockatlas.TokenPage, 0)
+	if len(tokens.Data) == 0 {
+		return tokenPage, nil
+	}
+
+	var tokenIds []string
+	for _, v := range tokens.Data[0].AssetsV2 {
+		tokenIds = append(tokenIds, v.Key)
+	}
+
+	tokensChan := p.getTokens(tokenIds)
+	for info := range tokensChan {
+		tokenPage = append(tokenPage, info)
+	}
+	return tokenPage, nil
+}
+
+func (p *Platform) getTokens(ids []string) chan blockatlas.Token {
+	tkChan := make(chan blockatlas.Token, len(ids))
+	var wg sync.WaitGroup
+	for _, id := range ids {
+		wg.Add(1)
+		go func(i string, c chan blockatlas.Token) {
+			defer wg.Done()
+			err := p.getTokensChannel(i, c)
+			if err != nil {
+				logger.Error(err)
+			}
+		}(id, tkChan)
+	}
+	wg.Wait()
+	close(tkChan)
+	return tkChan
+}
+
+func (p *Platform) getTokensChannel(id string, tkChan chan blockatlas.Token) error {
+	info, err := p.client.GetTokenInfo(id)
+	if err != nil || len(info.Data) == 0 {
+		logger.Error(err, "GetTokenInfo: invalid token")
+		return err
+	}
+	asset := NormalizeToken(info.Data[0])
+	tkChan <- asset
+	return nil
+}
+
+func NormalizeToken(info AssetInfo) blockatlas.Token {
+	return blockatlas.Token{
+		Name:     info.Name,
+		Symbol:   info.Symbol,
+		TokenID:  info.ID,
+		Coin:     coin.TRX,
+		Decimals: info.Decimals,
+		Type:     blockatlas.TokenTypeTRC10,
+	}
+}
diff --git a/platform/tron/transaction.go b/platform/tron/transaction.go
new file mode 100644
index 0000000..d12a25a
--- /dev/null
+++ b/platform/tron/transaction.go
@@ -0,0 +1,119 @@
+package tron
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/address"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"github.com/trustwallet/blockatlas/pkg/errors"
+	"github.com/trustwallet/blockatlas/pkg/logger"
+)
+
+func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
+	Txs, err := p.client.GetTxsOfAddress(address, "")
+	if err != nil && len(Txs) == 0 {
+		return nil, err
+	}
+
+	txs := make(blockatlas.TxPage, 0)
+	for _, srcTx := range Txs {
+		tx, err := Normalize(srcTx)
+		if err != nil {
+			continue
+		}
+
+		if len(srcTx.Data.Contracts) > 0 && srcTx.Data.Contracts[0].Type == TransferContract {
+			txs = append(txs, *tx)
+		} else {
+			continue
+		}
+	}
+
+	return txs, nil
+}
+
+func (p *Platform) GetTokenTxsByAddress(address, token string) (blockatlas.TxPage, error) {
+	tokenTxs, err := p.client.GetTxsOfAddress(address, token)
+	if err != nil {
+		return nil, errors.E(err, "TRON: failed to get token from address", errors.TypePlatformApi,
+			errors.Params{"address": address, "token": token}).PushToSentry()
+	}
+
+	txs := make(blockatlas.TxPage, 0)
+
+	if len(tokenTxs) == 0 {
+		return txs, nil
+	}
+
+	info, err := p.client.GetTokenInfo(token)
+	if err != nil || len(info.Data) == 0 {
+		return nil, errors.E(err, "TRON: failed to get token info", errors.TypePlatformApi,
+			errors.Params{"address": address, "token": token}).PushToSentry()
+	}
+
+	for _, srcTx := range tokenTxs {
+		tx, err := Normalize(srcTx)
+		if err != nil {
+			logger.Error(err)
+			continue
+		}
+		setTokenMeta(tx, srcTx, info.Data[0])
+		txs = append(txs, *tx)
+	}
+
+	return txs, nil
+}
+
+func setTokenMeta(tx *blockatlas.Tx, srcTx Tx, tokenInfo AssetInfo) {
+	transfer := srcTx.Data.Contracts[0].Parameter.Value
+	tx.Meta = blockatlas.TokenTransfer{
+		Name:     tokenInfo.Name,
+		Symbol:   tokenInfo.Symbol,
+		TokenID:  tokenInfo.ID,
+		Decimals: tokenInfo.Decimals,
+		Value:    transfer.Amount,
+		From:     tx.From,
+		To:       tx.To,
+	}
+}
+
+/// Normalize converts a Tron transaction into the generic model
+func Normalize(srcTx Tx) (*blockatlas.Tx, error) {
+	if len(srcTx.Data.Contracts) == 0 {
+		return nil, errors.E("TRON: transfer without contract", errors.TypePlatformApi,
+			errors.Params{"tx": srcTx}).PushToSentry()
+	}
+
+	contract := srcTx.Data.Contracts[0]
+	if contract.Type != TransferContract && contract.Type != TransferAssetContract {
+		return nil, errors.E("TRON: invalid contract transfer", errors.TypePlatformApi,
+			errors.Params{"tx": srcTx, "type": contract.Type})
+	}
+
+	transfer := contract.Parameter.Value
+	from, err := address.HexToAddress(transfer.OwnerAddress)
+	if err != nil {
+		return nil, errors.E(err, "TRON: failed to get from address", errors.TypePlatformApi,
+			errors.Params{"tx": srcTx}).PushToSentry()
+	}
+	to, err := address.HexToAddress(transfer.ToAddress)
+	if err != nil {
+		return nil, errors.E(err, "TRON: failed to get to address", errors.TypePlatformApi,
+			errors.Params{"tx": srcTx}).PushToSentry()
+	}
+
+	return &blockatlas.Tx{
+		ID:     srcTx.ID,
+		Coin:   coin.TRX,
+		Date:   srcTx.BlockTime / 1000,
+		From:   from,
+		To:     to,
+		Fee:    "0",
+		Block:  0,
+		Status: blockatlas.StatusCompleted,
+		Meta: blockatlas.Transfer{
+			Value:    transfer.Amount,
+			Symbol:   coin.Coins[coin.TRX].Symbol,
+			Decimals: coin.Coins[coin.TRX].Decimals,
+		},
+	}, nil
+}
diff --git a/platform/tron/util.go b/platform/tron/util.go
deleted file mode 100644
index c7ef9d7..0000000
--- a/platform/tron/util.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package tron
-
-import (
-	"crypto/sha256"
-	"encoding/hex"
-	"github.com/mr-tron/base58"
-	"github.com/trustwallet/blockatlas/pkg/errors"
-)
-
-// HexToAddress converts a hex representation of a Tron address
-// into a Base58 string with a 4 byte checksum.
-func HexToAddress(hexAddr string) (b58 string, err error) {
-	bytes, err := hex.DecodeString(hexAddr)
-	if err != nil {
-		return "", errors.E(err, errors.TypePlatformUnmarshal,
-			errors.Params{"hexAddr": hexAddr}).PushToSentry()
-	}
-	var checksum [32]byte
-	checksum = sha256.Sum256(bytes)
-	checksum = sha256.Sum256(checksum[:])
-	bytes = append(bytes, checksum[:4]...)
-	b58 = base58.EncodeAlphabet(bytes, base58.BTCAlphabet)
-	return
-}
diff --git a/platform/vechain/base.go b/platform/vechain/base.go
new file mode 100644
index 0000000..2b66085
--- /dev/null
+++ b/platform/vechain/base.go
@@ -0,0 +1,20 @@
+package vechain
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("vechain.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.VET]
+}
diff --git a/platform/vechain/block.go b/platform/vechain/block.go
new file mode 100644
index 0000000..bae6e1c
--- /dev/null
+++ b/platform/vechain/block.go
@@ -0,0 +1,26 @@
+package vechain
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	return p.client.GetCurrentBlock()
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	block, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+	cTxs := p.getTransactionsByIDs(block.Transactions)
+	txs := make(blockatlas.TxPage, 0)
+	for t := range cTxs {
+		txs = append(txs, t...)
+	}
+	return &blockatlas.Block{
+		Number: num,
+		ID:     block.Id,
+		Txs:    txs,
+	}, nil
+}
diff --git a/platform/vechain/api.go b/platform/vechain/transaction.go
similarity index 86%
rename from platform/vechain/api.go
rename to platform/vechain/transaction.go
index b272531..bdf4a7e 100644
--- a/platform/vechain/api.go
+++ b/platform/vechain/transaction.go
@@ -1,8 +1,6 @@
 package vechain
 
 import (
-	"github.com/spf13/viper"
-	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/address"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"github.com/trustwallet/blockatlas/pkg/errors"
@@ -12,40 +10,6 @@ import (
 	"sync"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("vechain.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.VET]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	return p.client.GetCurrentBlock()
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	block, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-	cTxs := p.getTransactionsByIDs(block.Transactions)
-	txs := make(blockatlas.TxPage, 0)
-	for t := range cTxs {
-		txs = append(txs, t...)
-	}
-	return &blockatlas.Block{
-		Number: num,
-		ID:     block.Id,
-		Txs:    txs,
-	}, nil
-}
-
 func (p *Platform) GetTokenTxsByAddress(address, token string) (blockatlas.TxPage, error) {
 	curBlock, err := p.CurrentBlockNumber()
 	if err != nil {
diff --git a/platform/vechain/api_test.go b/platform/vechain/transaction_test.go
similarity index 100%
rename from platform/vechain/api_test.go
rename to platform/vechain/transaction_test.go
diff --git a/platform/waves/base.go b/platform/waves/base.go
new file mode 100644
index 0000000..6715d90
--- /dev/null
+++ b/platform/waves/base.go
@@ -0,0 +1,20 @@
+package waves
+
+import (
+	"github.com/spf13/viper"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+type Platform struct {
+	client Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("waves.api"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.WAVES]
+}
diff --git a/platform/waves/block.go b/platform/waves/block.go
new file mode 100644
index 0000000..4d7d9d9
--- /dev/null
+++ b/platform/waves/block.go
@@ -0,0 +1,26 @@
+package waves
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	currentBlock, err := p.client.GetCurrentBlock()
+	if err != nil {
+		return 0, err
+	}
+	return currentBlock.Height, nil
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	srcTxs, err := p.client.GetBlockByNumber(num)
+	if err != nil {
+		return nil, err
+	}
+	txs := NormalizeTxs(srcTxs.Transactions)
+
+	return &blockatlas.Block{
+		Number: num,
+		Txs:    txs,
+	}, nil
+}
diff --git a/platform/waves/api.go b/platform/waves/transaction.go
similarity index 64%
rename from platform/waves/api.go
rename to platform/waves/transaction.go
index fe987f8..aafa2ea 100644
--- a/platform/waves/api.go
+++ b/platform/waves/transaction.go
@@ -1,47 +1,12 @@
 package waves
 
 import (
-	"github.com/spf13/viper"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"strconv"
 
 	"github.com/trustwallet/blockatlas/coin"
 )
 
-type Platform struct {
-	client Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("waves.api"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.WAVES]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	currentBlock, err := p.client.GetCurrentBlock()
-	if err != nil {
-		return 0, err
-	}
-	return currentBlock.Height, nil
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	srcTxs, err := p.client.GetBlockByNumber(num)
-	if err != nil {
-		return nil, err
-	}
-	txs := NormalizeTxs(srcTxs.Transactions)
-
-	return &blockatlas.Block{
-		Number: num,
-		Txs:    txs,
-	}, nil
-}
-
 func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
 	addressTxs, err := p.client.GetTxs(address, 25)
 	if err != nil {
diff --git a/platform/waves/api_test.go b/platform/waves/transaction_test.go
similarity index 100%
rename from platform/waves/api_test.go
rename to platform/waves/transaction_test.go
diff --git a/platform/zilliqa/api.go b/platform/zilliqa/api.go
deleted file mode 100644
index 46ec69d..0000000
--- a/platform/zilliqa/api.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package zilliqa
-
-import (
-	"strconv"
-
-	"github.com/trustwallet/blockatlas/coin"
-	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-
-	"github.com/spf13/viper"
-)
-
-type Platform struct {
-	client    Client
-	rpcClient RpcClient
-	udClient  Client
-}
-
-func (p *Platform) Init() error {
-	p.client = Client{blockatlas.InitClient(viper.GetString("zilliqa.api"))}
-	p.client.Headers["X-APIKEY"] = viper.GetString("zilliqa.key")
-
-	p.rpcClient = RpcClient{blockatlas.InitClient(viper.GetString("zilliqa.rpc"))}
-	p.udClient = Client{blockatlas.InitClient(viper.GetString("zilliqa.lookup"))}
-	return nil
-}
-
-func (p *Platform) Coin() coin.Coin {
-	return coin.Coins[coin.ZIL]
-}
-
-func (p *Platform) CurrentBlockNumber() (int64, error) {
-	info, err := p.rpcClient.GetBlockchainInfo()
-	if err != nil {
-		return 0, err
-	}
-	block, err := strconv.ParseInt(info.NumTxBlocks, 10, 64)
-	if err != nil {
-		return 0, err
-	}
-
-	return block, nil
-}
-
-func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
-	var normalized []blockatlas.Tx
-	txs, err := p.rpcClient.GetTxInBlock(num)
-	if err != nil {
-		return nil, err
-	}
-
-	for _, srcTx := range txs {
-		tx := Normalize(&srcTx)
-		normalized = append(normalized, tx)
-	}
-	block := blockatlas.Block{
-		Number: num,
-		Txs:    normalized,
-	}
-
-	return &block, nil
-}
-
-func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
-	var normalized []blockatlas.Tx
-	txs, err := p.client.GetTxsOfAddress(address)
-
-	if err != nil {
-		return nil, err
-	}
-
-	for _, srcTx := range txs {
-		tx := Normalize(&srcTx)
-		if len(normalized) >= blockatlas.TxPerPage {
-			break
-		}
-		normalized = append(normalized, tx)
-	}
-
-	return normalized, nil
-}
-
-func Normalize(srcTx *Tx) (tx blockatlas.Tx) {
-	tx = blockatlas.Tx{
-		ID:       srcTx.Hash,
-		Coin:     coin.ZIL,
-		Date:     srcTx.Timestamp / 1000,
-		From:     srcTx.From,
-		To:       srcTx.To,
-		Fee:      blockatlas.Amount(srcTx.Fee),
-		Block:    srcTx.BlockHeight,
-		Sequence: srcTx.NonceValue(),
-		Meta: blockatlas.Transfer{
-			Value:    blockatlas.Amount(srcTx.Value),
-			Symbol:   coin.Coins[coin.ZIL].Symbol,
-			Decimals: coin.Coins[coin.ZIL].Decimals,
-		},
-	}
-	if !srcTx.ReceiptSuccess {
-		tx.Status = blockatlas.StatusFailed
-	}
-	return tx
-}
diff --git a/platform/zilliqa/base.go b/platform/zilliqa/base.go
new file mode 100644
index 0000000..5a0550e
--- /dev/null
+++ b/platform/zilliqa/base.go
@@ -0,0 +1,27 @@
+package zilliqa
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+
+	"github.com/spf13/viper"
+)
+
+type Platform struct {
+	client    Client
+	rpcClient RpcClient
+	udClient  Client
+}
+
+func (p *Platform) Init() error {
+	p.client = Client{blockatlas.InitClient(viper.GetString("zilliqa.api"))}
+	p.client.Headers["X-APIKEY"] = viper.GetString("zilliqa.key")
+
+	p.rpcClient = RpcClient{blockatlas.InitClient(viper.GetString("zilliqa.rpc"))}
+	p.udClient = Client{blockatlas.InitClient(viper.GetString("zilliqa.lookup"))}
+	return nil
+}
+
+func (p *Platform) Coin() coin.Coin {
+	return coin.Coins[coin.ZIL]
+}
diff --git a/platform/zilliqa/block.go b/platform/zilliqa/block.go
new file mode 100644
index 0000000..dbfb6c2
--- /dev/null
+++ b/platform/zilliqa/block.go
@@ -0,0 +1,39 @@
+package zilliqa
+
+import (
+	"strconv"
+
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) CurrentBlockNumber() (int64, error) {
+	info, err := p.rpcClient.GetBlockchainInfo()
+	if err != nil {
+		return 0, err
+	}
+	block, err := strconv.ParseInt(info.NumTxBlocks, 10, 64)
+	if err != nil {
+		return 0, err
+	}
+
+	return block, nil
+}
+
+func (p *Platform) GetBlockByNumber(num int64) (*blockatlas.Block, error) {
+	var normalized []blockatlas.Tx
+	txs, err := p.rpcClient.GetTxInBlock(num)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, srcTx := range txs {
+		tx := Normalize(&srcTx)
+		normalized = append(normalized, tx)
+	}
+	block := blockatlas.Block{
+		Number: num,
+		Txs:    normalized,
+	}
+
+	return &block, nil
+}
diff --git a/platform/zilliqa/zns.go b/platform/zilliqa/domain.go
similarity index 100%
rename from platform/zilliqa/zns.go
rename to platform/zilliqa/domain.go
diff --git a/platform/zilliqa/transaction.go b/platform/zilliqa/transaction.go
new file mode 100644
index 0000000..cb52d1e
--- /dev/null
+++ b/platform/zilliqa/transaction.go
@@ -0,0 +1,47 @@
+package zilliqa
+
+import (
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+)
+
+func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) {
+	var normalized []blockatlas.Tx
+	txs, err := p.client.GetTxsOfAddress(address)
+
+	if err != nil {
+		return nil, err
+	}
+
+	for _, srcTx := range txs {
+		tx := Normalize(&srcTx)
+		if len(normalized) >= blockatlas.TxPerPage {
+			break
+		}
+		normalized = append(normalized, tx)
+	}
+
+	return normalized, nil
+}
+
+func Normalize(srcTx *Tx) (tx blockatlas.Tx) {
+	tx = blockatlas.Tx{
+		ID:       srcTx.Hash,
+		Coin:     coin.ZIL,
+		Date:     srcTx.Timestamp / 1000,
+		From:     srcTx.From,
+		To:       srcTx.To,
+		Fee:      blockatlas.Amount(srcTx.Fee),
+		Block:    srcTx.BlockHeight,
+		Sequence: srcTx.NonceValue(),
+		Meta: blockatlas.Transfer{
+			Value:    blockatlas.Amount(srcTx.Value),
+			Symbol:   coin.Coins[coin.ZIL].Symbol,
+			Decimals: coin.Coins[coin.ZIL].Decimals,
+		},
+	}
+	if !srcTx.ReceiptSuccess {
+		tx.Status = blockatlas.StatusFailed
+	}
+	return tx
+}


Test patch:
diff --git a/pkg/address/address_test.go b/pkg/address/address_test.go
index cdbbd41..1cae855 100644
--- a/pkg/address/address_test.go
+++ b/pkg/address/address_test.go
@@ -47,3 +47,15 @@ func TestRemove0x(t *testing.T) {
 		})
 	}
 }
+
+func TestHexToAddress(t *testing.T) {
+	in := "4182dd6b9966724ae2fdc79b416c7588da67ff1b35"
+	expected := "TMuA6YqfCeX8EhbfYEg5y7S4DqzSJireY9"
+	got, err := HexToAddress(in)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if expected != got {
+		t.Fatalf("expected %s, got %s", expected, got)
+	}
+}
diff --git a/platform/binance/token_test.go b/platform/binance/token_test.go
new file mode 100644
index 0000000..99106ac
--- /dev/null
+++ b/platform/binance/token_test.go
@@ -0,0 +1,154 @@
+package binance
+
+import (
+	"encoding/json"
+	"github.com/stretchr/testify/assert"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"testing"
+)
+
+const (
+	myToken = `
+{
+	"free": "17199.38841739",
+	"frozen": "0.00000000",
+	"locked": "0.00000000",
+	"symbol": "ARN-71B"
+}
+`
+	myTokenAllZero = `
+{
+	"free": "0.00000000",
+	"frozen": "0.00000000",
+	"locked": "0.00000000",
+	"symbol": "ARN-71B"
+}
+`
+	myTokenFreeZero = `
+{
+	"free": "0.00000000",
+	"frozen": "1.00000000",
+	"locked": "0.00000000",
+	"symbol": "ARN-71B"
+}
+`
+	myTokenFrozenAndFreeZero = `
+{
+	"free": "0.00000000",
+	"frozen": "0.00000000",
+	"locked": "0.00000001",
+	"symbol": "ARN-71B"
+}
+`
+	tokenList = `
+[
+  {
+    "mintable": false,
+    "name": "Aeron",
+    "original_symbol": "ARN",
+    "owner": "bnb1dq8ae0ayztqp99peggq5sygzf3n7u2ze4t0jne",
+    "symbol": "ARN-71B",
+    "total_supply": "20000000.00000000"
+  },
+  {
+    "mintable": false,
+    "name": "BOLT Token",
+    "original_symbol": "BOLT",
+    "owner": "bnb177ujwmshxu8r9za4vy9ztqn65tmr54ddw958rt",
+    "symbol": "BOLT-4C6",
+    "total_supply": "995000000.00000000"
+  }
+]
+`
+)
+
+var (
+	tokenDst = blockatlas.Token{
+		Name:     "Aeron",
+		Symbol:   "ARN",
+		Decimals: 8,
+		TokenID:  "ARN-71B",
+		Coin:     coin.BNB,
+		Type:     blockatlas.TokenTypeBEP2,
+	}
+	emptyTokenDst = blockatlas.Token{}
+)
+
+type testToken struct {
+	name        string
+	apiResponse string
+	expected    blockatlas.Token
+	tokens      string
+	ok          bool
+}
+
+func TestNormalizeToken(t *testing.T) {
+	testingTokens := []testToken{
+		{
+			name:        "Test with not zero balance",
+			apiResponse: myToken,
+			tokens:      tokenList,
+			expected:    tokenDst,
+			ok:          true,
+		},
+		{
+			name:        "Test with all zero balance",
+			apiResponse: myTokenAllZero,
+			tokens:      tokenList,
+			expected:    emptyTokenDst,
+			ok:          false,
+		},
+		{
+			name:        "Test with only free zero balance",
+			apiResponse: myTokenFreeZero,
+			tokens:      tokenList,
+			expected:    tokenDst,
+			ok:          true,
+		},
+		{
+			name:        "Test with free and frozen zero balances",
+			apiResponse: myTokenFrozenAndFreeZero,
+			tokens:      tokenList,
+			expected:    tokenDst,
+			ok:          true,
+		},
+	}
+	for _, testToken := range testingTokens {
+		t.Run(testToken.name, func(t *testing.T) {
+			var srcToken Balance
+			err := json.Unmarshal([]byte(testToken.apiResponse), &srcToken)
+			assert.Nil(t, err)
+
+			var srcTokens TokenPage
+			err = json.Unmarshal([]byte(testToken.tokens), &srcTokens)
+			assert.Nil(t, err)
+
+			tk, ok := NormalizeToken(&srcToken, &srcTokens)
+			assert.Equal(t, testToken.ok, ok, "token: token could not be normalized")
+			assert.Equal(t, testToken.expected, tk, "token: token don't equal")
+		})
+	}
+}
+
+func TestDecimalPlaces(t *testing.T) {
+	tests := []struct {
+		name  string
+		value string
+		want  int
+	}{
+		{"Test text value with dot", "decimal.places", 6},
+		{"Test float value", "1234.543212222", 9},
+		{"Test float value", "5.33333333", 8},
+		{"Test text value", "decimal", 0},
+		{"Test integer value", "4", 0},
+		{"Test empty value", "", 0},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := decimalPlaces(tt.value); got != tt.want {
+				t.Errorf("decimalPlaces() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/platform/binance/api_test.go b/platform/binance/transaction_test.go
similarity index 85%
rename from platform/binance/api_test.go
rename to platform/binance/transaction_test.go
index f5d64d2..e770f59 100644
--- a/platform/binance/api_test.go
+++ b/platform/binance/transaction_test.go
@@ -538,151 +538,6 @@ func TestNormalizeTxs(t *testing.T) {
 	}
 }
 
-const (
-	myToken = `
-{
-	"free": "17199.38841739",
-	"frozen": "0.00000000",
-	"locked": "0.00000000",
-	"symbol": "ARN-71B"
-}
-`
-	myTokenAllZero = `
-{
-	"free": "0.00000000",
-	"frozen": "0.00000000",
-	"locked": "0.00000000",
-	"symbol": "ARN-71B"
-}
-`
-	myTokenFreeZero = `
-{
-	"free": "0.00000000",
-	"frozen": "1.00000000",
-	"locked": "0.00000000",
-	"symbol": "ARN-71B"
-}
-`
-	myTokenFrozenAndFreeZero = `
-{
-	"free": "0.00000000",
-	"frozen": "0.00000000",
-	"locked": "0.00000001",
-	"symbol": "ARN-71B"
-}
-`
-	tokenList = `
-[
-  {
-    "mintable": false,
-    "name": "Aeron",
-    "original_symbol": "ARN",
-    "owner": "bnb1dq8ae0ayztqp99peggq5sygzf3n7u2ze4t0jne",
-    "symbol": "ARN-71B",
-    "total_supply": "20000000.00000000"
-  },
-  {
-    "mintable": false,
-    "name": "BOLT Token",
-    "original_symbol": "BOLT",
-    "owner": "bnb177ujwmshxu8r9za4vy9ztqn65tmr54ddw958rt",
-    "symbol": "BOLT-4C6",
-    "total_supply": "995000000.00000000"
-  }
-]
-`
-)
-
-var (
-	tokenDst = blockatlas.Token{
-		Name:     "Aeron",
-		Symbol:   "ARN",
-		Decimals: 8,
-		TokenID:  "ARN-71B",
-		Coin:     coin.BNB,
-		Type:     blockatlas.TokenTypeBEP2,
-	}
-	emptyTokenDst = blockatlas.Token{}
-)
-
-type testToken struct {
-	name        string
-	apiResponse string
-	expected    blockatlas.Token
-	tokens      string
-	ok          bool
-}
-
-func TestNormalizeToken(t *testing.T) {
-	testingTokens := []testToken{
-		{
-			name:        "Test with not zero balance",
-			apiResponse: myToken,
-			tokens:      tokenList,
-			expected:    tokenDst,
-			ok:          true,
-		},
-		{
-			name:        "Test with all zero balance",
-			apiResponse: myTokenAllZero,
-			tokens:      tokenList,
-			expected:    emptyTokenDst,
-			ok:          false,
-		},
-		{
-			name:        "Test with only free zero balance",
-			apiResponse: myTokenFreeZero,
-			tokens:      tokenList,
-			expected:    tokenDst,
-			ok:          true,
-		},
-		{
-			name:        "Test with free and frozen zero balances",
-			apiResponse: myTokenFrozenAndFreeZero,
-			tokens:      tokenList,
-			expected:    tokenDst,
-			ok:          true,
-		},
-	}
-	for _, testToken := range testingTokens {
-		t.Run(testToken.name, func(t *testing.T) {
-			var srcToken Balance
-			err := json.Unmarshal([]byte(testToken.apiResponse), &srcToken)
-			assert.Nil(t, err)
-
-			var srcTokens TokenPage
-			err = json.Unmarshal([]byte(testToken.tokens), &srcTokens)
-			assert.Nil(t, err)
-
-			tk, ok := NormalizeToken(&srcToken, &srcTokens)
-			assert.Equal(t, testToken.ok, ok, "token: token could not be normalized")
-			assert.Equal(t, testToken.expected, tk, "token: token don't equal")
-		})
-	}
-}
-
-func TestDecimalPlaces(t *testing.T) {
-	tests := []struct {
-		name  string
-		value string
-		want  int
-	}{
-		{"Test text value with dot", "decimal.places", 6},
-		{"Test float value", "1234.543212222", 9},
-		{"Test float value", "5.33333333", 8},
-		{"Test text value", "decimal", 0},
-		{"Test integer value", "4", 0},
-		{"Test empty value", "", 0},
-	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			if got := decimalPlaces(tt.value); got != tt.want {
-				t.Errorf("decimalPlaces() = %v, want %v", got, tt.want)
-			}
-		})
-	}
-}
-
 func TestTokenSymbol(t *testing.T) {
 	assert.Equal(t, "UGAS", TokenSymbol("UGAS"))
 	assert.Equal(t, "UGAS", TokenSymbol("UGAS-B0C"))
diff --git a/platform/bitcoin/api_test.go b/platform/bitcoin/transaction_test.go
similarity index 99%
rename from platform/bitcoin/api_test.go
rename to platform/bitcoin/transaction_test.go
index a05badb..e7a18dd 100644
--- a/platform/bitcoin/api_test.go
+++ b/platform/bitcoin/transaction_test.go
@@ -3,12 +3,11 @@ package bitcoin
 import (
 	"bytes"
 	"encoding/json"
+	mapset "github.com/deckarep/golang-set"
 	"github.com/stretchr/testify/assert"
+	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"testing"
-
-	mapset "github.com/deckarep/golang-set"
-	"github.com/trustwallet/blockatlas/coin"
 )
 
 const outgoingTx = `{
diff --git a/platform/ethereum/collection_client_test.go b/platform/ethereum/collection_client_test.go
deleted file mode 100644
index 8cc1183..0000000
--- a/platform/ethereum/collection_client_test.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package ethereum
-
-import (
-	"fmt"
-	"github.com/stretchr/testify/assert"
-	"testing"
-)
-
-var c1 = Collection{
-	Slug: "enjin",
-	Contracts: []PrimaryAssetContract{{
-		Address: "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-	}},
-}
-var c2 = Collection{
-	Slug: "cryptokitties",
-	Contracts: []PrimaryAssetContract{{
-		Address: "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-	}},
-}
-var c3 = Collection{
-	Slug: "age-of-rust",
-	Contracts: []PrimaryAssetContract{{
-		Address: "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	}},
-}
-
-func TestSearchCollection(t *testing.T) {
-	var tests = []struct {
-		collections   []Collection
-		collectibleID string
-		result        *Collection
-	}{
-		{[]Collection{c1, c2, c3}, "enjin", &c1},
-		{[]Collection{c1, c2, c3}, "cryptokitties", &c2},
-		{[]Collection{c1, c2}, "age-of-rust", nil},
-		{[]Collection{c1, c2, c3}, "age-of-rust", &c3},
-		{[]Collection{c1, c2}, "cryptokitties", &c2},
-		{[]Collection{c1}, "age-of-rust", nil},
-		{[]Collection{c1, c3}, "enjin", &c1},
-	}
-	for i, tt := range tests {
-		t.Run(fmt.Sprintf("searchCollection %d", i), func(t *testing.T) {
-			s := searchCollection(tt.collections, tt.collectibleID)
-			assert.EqualValues(t, s, tt.result)
-		})
-	}
-
-}
diff --git a/platform/ethereum/collection_test.go b/platform/ethereum/collection_test.go
index c68acb4..a3b6be1 100644
--- a/platform/ethereum/collection_test.go
+++ b/platform/ethereum/collection_test.go
@@ -2,6 +2,7 @@ package ethereum
 
 import (
 	"encoding/json"
+	"fmt"
 	"github.com/stretchr/testify/assert"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
@@ -203,3 +204,45 @@ func TestNormalizeCollectible(t *testing.T) {
 	expected := blockatlas.CollectiblePage{collectibleDst}
 	assert.Equal(t, page, expected, "collectible don't equal")
 }
+
+var c1 = Collection{
+	Slug: "enjin",
+	Contracts: []PrimaryAssetContract{{
+		Address: "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
+	}},
+}
+var c2 = Collection{
+	Slug: "cryptokitties",
+	Contracts: []PrimaryAssetContract{{
+		Address: "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
+	}},
+}
+var c3 = Collection{
+	Slug: "age-of-rust",
+	Contracts: []PrimaryAssetContract{{
+		Address: "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
+	}},
+}
+
+func TestSearchCollection(t *testing.T) {
+	var tests = []struct {
+		collections   []Collection
+		collectibleID string
+		result        *Collection
+	}{
+		{[]Collection{c1, c2, c3}, "enjin", &c1},
+		{[]Collection{c1, c2, c3}, "cryptokitties", &c2},
+		{[]Collection{c1, c2}, "age-of-rust", nil},
+		{[]Collection{c1, c2, c3}, "age-of-rust", &c3},
+		{[]Collection{c1, c2}, "cryptokitties", &c2},
+		{[]Collection{c1}, "age-of-rust", nil},
+		{[]Collection{c1, c3}, "enjin", &c1},
+	}
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("searchCollection %d", i), func(t *testing.T) {
+			s := searchCollection(tt.collections, tt.collectibleID)
+			assert.EqualValues(t, s, tt.result)
+		})
+	}
+
+}
diff --git a/platform/ethereum/token_test.go b/platform/ethereum/token_test.go
new file mode 100644
index 0000000..1a87cdf
--- /dev/null
+++ b/platform/ethereum/token_test.go
@@ -0,0 +1,73 @@
+package ethereum
+
+import (
+	"bytes"
+	"encoding/json"
+	"github.com/trustwallet/blockatlas/coin"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"testing"
+)
+
+const tokenSrc = `
+{
+	"balance": "0",
+	"contract": {
+		"contract": "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
+		"address": "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
+		"name": "FusChain",
+		"decimals": 18,
+		"symbol": "FUS"
+	}
+}`
+
+var tokenDst = blockatlas.Token{
+	Name:     "FusChain",
+	Symbol:   "FUS",
+	Decimals: 18,
+	TokenID:  "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
+	Coin:     coin.ETH,
+	Type:     blockatlas.TokenTypeERC20,
+}
+
+type testToken struct {
+	name        string
+	apiResponse string
+	expected    *blockatlas.Token
+}
+
+func TestNormalizeToken(t *testing.T) {
+	testNormalizeToken(t, &testToken{
+		name:        "token",
+		apiResponse: tokenSrc,
+		expected:    &tokenDst,
+	})
+}
+
+func testNormalizeToken(t *testing.T, _test *testToken) {
+	var token Token
+	err := json.Unmarshal([]byte(_test.apiResponse), &token)
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	tk, ok := NormalizeToken(&token, coin.ETH)
+	if !ok {
+		t.Errorf("token: token could not be normalized")
+	}
+
+	resJSON, err := json.Marshal(&tk)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	dstJSON, err := json.Marshal(_test.expected)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if !bytes.Equal(resJSON, dstJSON) {
+		println(string(resJSON))
+		println(string(dstJSON))
+		t.Error("token: token don't equal")
+	}
+}
diff --git a/platform/ethereum/api_test.go b/platform/ethereum/transaction_test.go
similarity index 83%
rename from platform/ethereum/api_test.go
rename to platform/ethereum/transaction_test.go
index 7b9da9d..c26a50f 100644
--- a/platform/ethereum/api_test.go
+++ b/platform/ethereum/transaction_test.go
@@ -237,67 +237,3 @@ func testNormalize(t *testing.T, _test *test) {
 		t.Error(_test.name + ": tx don't equal")
 	}
 }
-
-const tokenSrc = `
-{
-	"balance": "0",
-	"contract": {
-		"contract": "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
-		"address": "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
-		"name": "FusChain",
-		"decimals": 18,
-		"symbol": "FUS"
-	}
-}`
-
-var tokenDst = blockatlas.Token{
-	Name:     "FusChain",
-	Symbol:   "FUS",
-	Decimals: 18,
-	TokenID:  "0xa14839c9837657efcde754ebeaf5cbecdd801b2a",
-	Coin:     coin.ETH,
-	Type:     blockatlas.TokenTypeERC20,
-}
-
-type testToken struct {
-	name        string
-	apiResponse string
-	expected    *blockatlas.Token
-}
-
-func TestNormalizeToken(t *testing.T) {
-	testNormalizeToken(t, &testToken{
-		name:        "token",
-		apiResponse: tokenSrc,
-		expected:    &tokenDst,
-	})
-}
-
-func testNormalizeToken(t *testing.T, _test *testToken) {
-	var token Token
-	err := json.Unmarshal([]byte(_test.apiResponse), &token)
-	if err != nil {
-		t.Error(err)
-		return
-	}
-	tk, ok := NormalizeToken(&token, coin.ETH)
-	if !ok {
-		t.Errorf("token: token could not be normalized")
-	}
-
-	resJSON, err := json.Marshal(&tk)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	dstJSON, err := json.Marshal(_test.expected)
-	if err != nil {
-		t.Fatal(err)
-	}
-
-	if !bytes.Equal(resJSON, dstJSON) {
-		println(string(resJSON))
-		println(string(dstJSON))
-		t.Error("token: token don't equal")
-	}
-}
diff --git a/platform/tron/token_test.go b/platform/tron/token_test.go
new file mode 100644
index 0000000..8bf9089
--- /dev/null
+++ b/platform/tron/token_test.go
@@ -0,0 +1,22 @@
+package tron
+
+import (
+	"github.com/stretchr/testify/assert"
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"testing"
+)
+
+var tokenDst = blockatlas.Token{
+	Name:     "Test",
+	Symbol:   "TST",
+	Decimals: 8,
+	TokenID:  "1",
+	Coin:     195,
+	Type:     "TRC10",
+}
+
+func TestNormalizeToken(t *testing.T) {
+	asset := AssetInfo{Name: "Test", Symbol: "TST", ID: "1", Decimals: 8}
+	actual := NormalizeToken(asset)
+	assert.Equal(t, tokenDst, actual)
+}
diff --git a/platform/tron/api_test.go b/platform/tron/transaction_test.go
similarity index 90%
rename from platform/tron/api_test.go
rename to platform/tron/transaction_test.go
index 12d1a42..6a619e9 100644
--- a/platform/tron/api_test.go
+++ b/platform/tron/transaction_test.go
@@ -133,18 +133,3 @@ func testNormalize(t *testing.T, _test *test) {
 	assert.NotNil(t, res)
 	assert.Equal(t, _test.expected, res)
 }
-
-var tokenDst = blockatlas.Token{
-	Name:     "Test",
-	Symbol:   "TST",
-	Decimals: 8,
-	TokenID:  "1",
-	Coin:     195,
-	Type:     "TRC10",
-}
-
-func TestNormalizeToken(t *testing.T) {
-	asset := AssetInfo{Name: "Test", Symbol: "TST", ID: "1", Decimals: 8}
-	actual := NormalizeToken(asset)
-	assert.Equal(t, tokenDst, actual)
-}
diff --git a/platform/tron/util_test.go b/platform/tron/util_test.go
deleted file mode 100644
index 85e8bda..0000000
--- a/platform/tron/util_test.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package tron
-
-import "testing"
-
-func TestHexToAddress(t *testing.T) {
-	in := "4182dd6b9966724ae2fdc79b416c7588da67ff1b35"
-	expected := "TMuA6YqfCeX8EhbfYEg5y7S4DqzSJireY9"
-	got, err := HexToAddress(in)
-	if err != nil {
-		t.Fatal(err)
-	}
-	if expected != got {
-		t.Fatalf("expected %s, got %s", expected, got)
-	}
-}