Repository: trustwallet/blockatlas
Pull request #989 (2020-03-31 03:56:01)

Remove v2, сollections route
Post due

WIP:
- [x] Update swagger
- [x] Remove tests

EnoRage: Current supported collection handlers
<img width="1483" alt="Screenshot 2020-04-06 at 16 59 29" src="https://user-images.githubusercontent.com/22479658/78566498-fc4a3200-7827-11ea-81de-e8dbd358bbe1.png">

vikmeup: Can you verify to make sure only these fields are used in your code. `Collection.type` is not longer needed, as it part of Collectible, not a Collection. The rest feel free to remove

**Collection**:
```
private enum CodingKeys: String, CodingKey {
        case name,
            address,
            imageURL = "image_url",
            categoryDescription = "description",
            externalURL = "external_link",
            rawCoin = "coin",
            total,
            id
    }
```

**Collectible**:
```
private enum CodingKeys: String, CodingKey {
        case id = "id",
        tokenID = "token_id",
        collectionID = "collection_id",
        contractAddress = "contract_address",
        category,
        imageURL = "image_url",
        name,
        externalURL = "external_link",
        categoryDescription = "description",
        rawCoin = "coin",
        providerLink = "provider_link",
        rawType = "type",
        rawCollectibleNFT = "nft_version"
    }
```
EnoRage: > Can you verify to make sure only these fields are used in your code. `Collection.type` is not longer needed, as it part of Collectible, not a Collection. The rest feel free to remove
> 
> **Collection**:
> 
> ```
> private enum CodingKeys: String, CodingKey {
>         case name,
>             address,
>             imageURL = "image_url",
>             categoryDescription = "description",
>             externalURL = "external_link",
>             rawCoin = "coin",
>             total,
>             id
>     }
> ```
> 
> **Collectible**:
> 
> ```
> private enum CodingKeys: String, CodingKey {
>         case id = "id",
>         tokenID = "token_id",
>         collectionID = "collection_id",
>         contractAddress = "contract_address",
>         category,
>         imageURL = "image_url",
>         name,
>         externalURL = "external_link",
>         categoryDescription = "description",
>         rawCoin = "coin",
>         providerLink = "provider_link",
>         rawType = "type",
>         rawCollectibleNFT = "nft_version"
>     }
> ```

Now collections have only 1 version with current fields only, I removed all other stuff that is not connected with v4. 
V4 is now just collection api

Patch:
diff --git a/api/collection.go b/api/collection.go
index 9d1081f..4c58164 100644
--- a/api/collection.go
+++ b/api/collection.go
@@ -8,37 +8,6 @@ import (
 	"strconv"
 )
 
-// @Summary Get Collections
-// @ID collections_v2
-// @Description Get all collections from the address
-// @Accept json
-// @Produce json
-// @Tags Collections
-// @Param coin path string true "the coin name" default(ethereum)
-// @Param address path string true "the query address" default(0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB)
-// @Success 200 {object} blockatlas.CollectionPage
-// @Failure 500 {object} ginutils.ApiError
-// @Router /v2/{coin}/collections/{address} [get]
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func oldMakeCollectionsRoute(router gin.IRouter, api blockatlas.Platform) {
-	var collectionAPI blockatlas.CollectionAPI
-	collectionAPI, _ = api.(blockatlas.CollectionAPI)
-
-	if collectionAPI == nil {
-		return
-	}
-
-	router.GET("/collections/:owner", func(c *gin.Context) {
-		collections, err := collectionAPI.OldGetCollections(c.Param("owner"))
-		if err != nil {
-			ginutils.ErrorResponse(c).Message(err.Error()).Render()
-			return
-		}
-
-		ginutils.RenderSuccess(c, collections)
-	})
-}
-
 // @Summary Get Collections
 // @ID collections_v3
 // @Description Get all collections from the address
@@ -59,7 +28,7 @@ func makeCollectionsRoute(router gin.IRouter, api blockatlas.Platform) {
 	}
 
 	router.GET("/collections/:owner", func(c *gin.Context) {
-		collections, err := collectionAPI.GetCollections(c.Param("owner"))
+		collections, err := collectionAPI.GetCollectionsV3(c.Param("owner"))
 		if err != nil {
 			ginutils.ErrorResponse(c).Message(err.Error()).Render()
 			return
@@ -69,38 +38,6 @@ func makeCollectionsRoute(router gin.IRouter, api blockatlas.Platform) {
 	})
 }
 
-// @Summary Get Collection
-// @ID collection_v2
-// @Description Get a collection from the address
-// @Accept json
-// @Produce json
-// @Tags Collections
-// @Param coin path string true "the coin name" default(ethereum)
-// @Param owner path string true "the query address" default(0x0875BCab22dE3d02402bc38aEe4104e1239374a7)
-// @Param collection_id path string true "the query collection" default(0x06012c8cf97bead5deae237070f9587f8e7a266d)
-// @Success 200 {object} blockatlas.CollectionPage
-// @Failure 500 {object} ginutils.ApiError
-// @Router /v2/{coin}/collections/{owner}/collection/{collection_id} [get]
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func oldMakeCollectionRoute(router gin.IRouter, api blockatlas.Platform) {
-	var collectionAPI blockatlas.CollectionAPI
-	collectionAPI, _ = api.(blockatlas.CollectionAPI)
-
-	if collectionAPI == nil {
-		return
-	}
-
-	router.GET("/collections/:owner/collection/:collection_id", func(c *gin.Context) {
-		collectibles, err := collectionAPI.OldGetCollectibles(c.Param("owner"), c.Param("collection_id"))
-		if err != nil {
-			ginutils.ErrorResponse(c).Message(err.Error()).Render()
-			return
-		}
-
-		ginutils.RenderSuccess(c, collectibles)
-	})
-}
-
 // @Summary Get Collection
 // @ID collection_v3
 // @Description Get a collection from the address
@@ -122,7 +59,7 @@ func makeCollectionRoute(router gin.IRouter, api blockatlas.Platform) {
 	}
 
 	router.GET("/collections/:owner/collection/:collection_id", func(c *gin.Context) {
-		collectibles, err := collectionAPI.GetCollectibles(c.Param("owner"), c.Param("collection_id"))
+		collectibles, err := collectionAPI.GetCollectiblesV3(c.Param("owner"), c.Param("collection_id"))
 		if err != nil {
 			ginutils.ErrorResponse(c).Message(err.Error()).Render()
 			return
@@ -132,46 +69,6 @@ func makeCollectionRoute(router gin.IRouter, api blockatlas.Platform) {
 	})
 }
 
-// @Description Get collection categories
-// @ID collection_categories_v2
-// @Summary Get list of collections from a specific coin and addresses
-// @Accept json
-// @Produce json
-// @Tags Collections
-// @Param data body string true "Payload" default({"60": ["0xb3624367b1ab37daef42e1a3a2ced012359659b0"]})
-// @Success 200 {object} blockatlas.DocsResponse
-// @Router /v2/collectibles/categories [post]
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func oldMakeCategoriesBatchRoute(router gin.IRouter) {
-	router.POST("/collectibles/categories", func(c *gin.Context) {
-		var reqs map[string][]string
-		if err := c.BindJSON(&reqs); err != nil {
-			ginutils.ErrorResponse(c).Message(err.Error()).Render()
-			return
-		}
-
-		batch := make(blockatlas.CollectionPage, 0)
-		for key, addresses := range reqs {
-			coinId, err := strconv.Atoi(key)
-			if err != nil {
-				continue
-			}
-			p, ok := platform.CollectionAPIs[uint(coinId)]
-			if !ok {
-				continue
-			}
-			for _, address := range addresses {
-				collections, err := p.OldGetCollections(address)
-				if err != nil {
-					continue
-				}
-				batch = append(batch, collections...)
-			}
-		}
-		ginutils.RenderSuccess(c, batch)
-	})
-}
-
 // @Description Get collection categories
 // @ID collection_categories_v3
 // @Summary Get list of collections from a specific coin and addresses
@@ -189,7 +86,7 @@ func makeCategoriesBatchRoute(router gin.IRouter) {
 			return
 		}
 
-		batch := make(blockatlas.CollectionPage, 0)
+		batch := make(blockatlas.CollectionPageV3, 0)
 		for key, addresses := range reqs {
 			coinId, err := strconv.Atoi(key)
 			if err != nil {
@@ -200,7 +97,7 @@ func makeCategoriesBatchRoute(router gin.IRouter) {
 				continue
 			}
 			for _, address := range addresses {
-				collections, err := p.GetCollections(address)
+				collections, err := p.GetCollectionsV3(address)
 				if err != nil {
 					continue
 				}
@@ -239,7 +136,7 @@ func makeCategoriesBatchRouteV4(router gin.IRouter) {
 				continue
 			}
 			for _, address := range addresses {
-				collections, err := p.GetCollectionsV4(address)
+				collections, err := p.GetCollections(address)
 				if err != nil {
 					continue
 				}
@@ -271,7 +168,7 @@ func makeCollectionRouteV4(router gin.IRouter, api blockatlas.Platform) {
 	}
 
 	router.GET("/collections/:owner/collection/:collection_id", func(c *gin.Context) {
-		collectibles, err := collectionAPI.GetCollectiblesV4(c.Param("owner"), c.Param("collection_id"))
+		collectibles, err := collectionAPI.GetCollectibles(c.Param("owner"), c.Param("collection_id"))
 		if err != nil {
 			ginutils.ErrorResponse(c).Message(err.Error()).Render()
 			return
diff --git a/api/routes.go b/api/routes.go
index c843a21..b1f19b1 100644
--- a/api/routes.go
+++ b/api/routes.go
@@ -16,8 +16,8 @@ func SetupPlatformAPI(root gin.IRouter) {
 	root.GET("/status", GetStatus)
 
 	v1 := root.Group("/v1")
-	v2 := root.Group("/v2")
 	v3 := root.Group("/v3")
+	v2 := root.Group("/v2")
 	v4 := root.Group("/v4")
 
 	v1.GET("/", GetSupportedEndpoints)
@@ -42,16 +42,12 @@ func SetupPlatformAPI(root gin.IRouter) {
 	}
 
 	for _, collectionAPI := range platform.Platforms {
-		routerV2 := getRouter(v2, collectionAPI.Coin().Handle)
-		routerV3 := getRouter(v3, collectionAPI.Coin().Handle)
-		routerV4 := getRouter(v4, collectionAPI.Coin().Handle)
 
+		routerV3 := getRouter(v3, collectionAPI.Coin().Handle)
 		makeCollectionsRoute(routerV3, collectionAPI)
 		makeCollectionRoute(routerV3, collectionAPI)
 
-		oldMakeCollectionRoute(routerV2, collectionAPI)
-		oldMakeCollectionsRoute(routerV2, collectionAPI)
-
+		routerV4 := getRouter(v4, collectionAPI.Coin().Handle)
 		makeCollectionRouteV4(routerV4, collectionAPI)
 	}
 
@@ -65,12 +61,12 @@ func SetupPlatformAPI(root gin.IRouter) {
 	MakeLookupRoute(ns)
 	MakeLookupBatchRoute(batchNs)
 
-	oldMakeCategoriesBatchRoute(v2)
-	makeCategoriesBatchRoute(v3)
-	makeCategoriesBatchRouteV4(v4)
 	makeStakingDelegationsBatchRoute(v2)
 	makeStakingDelegationsSimpleBatchRoute(v2)
 
+	makeCategoriesBatchRoute(v3)
+	makeCategoriesBatchRouteV4(v4)
+
 	logger.Info("Routes set up", logger.Params{"routes": len(routers)})
 }
 
diff --git a/docs/docs.go b/docs/docs.go
index 247a273..99f6503 100644
--- a/docs/docs.go
+++ b/docs/docs.go
@@ -1,6 +1,6 @@
 // GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 // This file was generated by swaggo/swag at
-// 2020-03-01 04:39:26.356879 +0300 MSK m=+1.896562991
+// 2020-04-06 16:53:26.62462 +0300 MSK m=+1.619745776
 
 package docs
 
@@ -157,13 +157,6 @@ var doc = `{
                         "name": "token",
                         "in": "query"
                     },
-                    {
-                        "type": "integer",
-                        "default": 1574483028,
-                        "description": "Start timestamp",
-                        "name": "time_start",
-                        "in": "query"
-                    },
                     {
                         "type": "string",
                         "default": "USD",
@@ -301,42 +294,6 @@ var doc = `{
                 }
             }
         },
-        "/v2/collectibles/categories": {
-            "post": {
-                "description": "Get collection categories",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get list of collections from a specific coin and addresses",
-                "operationId": "collection_categories_v2",
-                "parameters": [
-                    {
-                        "default": "{\"60\": [\"0xb3624367b1ab37daef42e1a3a2ced012359659b0\"]}",
-                        "description": "Payload",
-                        "name": "data",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.DocsResponse"
-                        }
-                    }
-                }
-            }
-        },
         "/v2/ns/lookup": {
             "get": {
                 "description": "Lookup ENS/ZNS to find registered addresses for multiple coins",
@@ -452,110 +409,6 @@ var doc = `{
                 }
             }
         },
-        "/v2/{coin}/collections/{address}": {
-            "get": {
-                "description": "Get all collections from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collections",
-                "operationId": "collections_v2",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-                        "description": "the query address",
-                        "name": "address",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
-        "/v2/{coin}/collections/{owner}/collection/{collection_id}": {
-            "get": {
-                "description": "Get a collection from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collection",
-                "operationId": "collection_v2",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-                        "description": "the query address",
-                        "name": "owner",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
-                        "description": "the query collection",
-                        "name": "collection_id",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
         "/v2/{coin}/staking/delegations/{address}": {
             "get": {
                 "description": "Get stake delegations from the address",
@@ -740,146 +593,6 @@ var doc = `{
                 }
             }
         },
-        "/v3/collectibles/categories": {
-            "post": {
-                "description": "Get collection categories",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get list of collections from a specific coin and addresses",
-                "operationId": "collection_categories_v3",
-                "parameters": [
-                    {
-                        "default": "{\"60\": [\"0xb3624367b1ab37daef42e1a3a2ced012359659b0\"]}",
-                        "description": "Payload",
-                        "name": "data",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.DocsResponse"
-                        }
-                    }
-                }
-            }
-        },
-        "/v3/{coin}/collections/{address}": {
-            "get": {
-                "description": "Get all collections from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collections",
-                "operationId": "collections_v3",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-                        "description": "the query address",
-                        "name": "address",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
-        "/v3/{coin}/collections/{owner}/collection/{collection_id}": {
-            "get": {
-                "description": "Get a collection from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collection",
-                "operationId": "collection_v3",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-                        "description": "the query address",
-                        "name": "owner",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
-                        "description": "the query collection",
-                        "name": "collection_id",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
         "/v4/collectibles/categories": {
             "post": {
                 "description": "Get collection categories",
@@ -1001,7 +714,8 @@ var doc = `{
                     "type": "string"
                 },
                 "type": {
-                    "type": "string"
+                    "type": "object",
+                    "$ref": "#/definitions/blockatlas.CoinStatus"
                 }
             }
         },
@@ -1069,6 +783,17 @@ var doc = `{
                 "$ref": "#/definitions/api.Ticker"
             }
         },
+        "blockatlas.CoinStatus": {
+            "type": "object",
+            "properties": {
+                "error": {
+                    "type": "string"
+                },
+                "height": {
+                    "type": "integer"
+                }
+            }
+        },
         "blockatlas.Collection": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.json b/docs/swagger.json
index dc8104c..52096ec 100644
--- a/docs/swagger.json
+++ b/docs/swagger.json
@@ -136,13 +136,6 @@
                         "name": "token",
                         "in": "query"
                     },
-                    {
-                        "type": "integer",
-                        "default": 1574483028,
-                        "description": "Start timestamp",
-                        "name": "time_start",
-                        "in": "query"
-                    },
                     {
                         "type": "string",
                         "default": "USD",
@@ -280,42 +273,6 @@
                 }
             }
         },
-        "/v2/collectibles/categories": {
-            "post": {
-                "description": "Get collection categories",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get list of collections from a specific coin and addresses",
-                "operationId": "collection_categories_v2",
-                "parameters": [
-                    {
-                        "default": "{\"60\": [\"0xb3624367b1ab37daef42e1a3a2ced012359659b0\"]}",
-                        "description": "Payload",
-                        "name": "data",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.DocsResponse"
-                        }
-                    }
-                }
-            }
-        },
         "/v2/ns/lookup": {
             "get": {
                 "description": "Lookup ENS/ZNS to find registered addresses for multiple coins",
@@ -431,110 +388,6 @@
                 }
             }
         },
-        "/v2/{coin}/collections/{address}": {
-            "get": {
-                "description": "Get all collections from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collections",
-                "operationId": "collections_v2",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-                        "description": "the query address",
-                        "name": "address",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
-        "/v2/{coin}/collections/{owner}/collection/{collection_id}": {
-            "get": {
-                "description": "Get a collection from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collection",
-                "operationId": "collection_v2",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-                        "description": "the query address",
-                        "name": "owner",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
-                        "description": "the query collection",
-                        "name": "collection_id",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
         "/v2/{coin}/staking/delegations/{address}": {
             "get": {
                 "description": "Get stake delegations from the address",
@@ -719,146 +572,6 @@
                 }
             }
         },
-        "/v3/collectibles/categories": {
-            "post": {
-                "description": "Get collection categories",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get list of collections from a specific coin and addresses",
-                "operationId": "collection_categories_v3",
-                "parameters": [
-                    {
-                        "default": "{\"60\": [\"0xb3624367b1ab37daef42e1a3a2ced012359659b0\"]}",
-                        "description": "Payload",
-                        "name": "data",
-                        "in": "body",
-                        "required": true,
-                        "schema": {
-                            "type": "string"
-                        }
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.DocsResponse"
-                        }
-                    }
-                }
-            }
-        },
-        "/v3/{coin}/collections/{address}": {
-            "get": {
-                "description": "Get all collections from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collections",
-                "operationId": "collections_v3",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-                        "description": "the query address",
-                        "name": "address",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
-        "/v3/{coin}/collections/{owner}/collection/{collection_id}": {
-            "get": {
-                "description": "Get a collection from the address",
-                "consumes": [
-                    "application/json"
-                ],
-                "produces": [
-                    "application/json"
-                ],
-                "tags": [
-                    "Collections"
-                ],
-                "summary": "Get Collection",
-                "operationId": "collection_v3",
-                "parameters": [
-                    {
-                        "type": "string",
-                        "default": "ethereum",
-                        "description": "the coin name",
-                        "name": "coin",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-                        "description": "the query address",
-                        "name": "owner",
-                        "in": "path",
-                        "required": true
-                    },
-                    {
-                        "type": "string",
-                        "default": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
-                        "description": "the query collection",
-                        "name": "collection_id",
-                        "in": "path",
-                        "required": true
-                    }
-                ],
-                "responses": {
-                    "200": {
-                        "description": "OK",
-                        "schema": {
-                            "$ref": "#/definitions/blockatlas.CollectionPage"
-                        }
-                    },
-                    "500": {
-                        "description": "Internal Server Error",
-                        "schema": {
-                            "$ref": "#/definitions/ginutils.ApiError"
-                        }
-                    }
-                }
-            }
-        },
         "/v4/collectibles/categories": {
             "post": {
                 "description": "Get collection categories",
@@ -980,7 +693,8 @@
                     "type": "string"
                 },
                 "type": {
-                    "type": "string"
+                    "type": "object",
+                    "$ref": "#/definitions/blockatlas.CoinStatus"
                 }
             }
         },
@@ -1048,6 +762,17 @@
                 "$ref": "#/definitions/api.Ticker"
             }
         },
+        "blockatlas.CoinStatus": {
+            "type": "object",
+            "properties": {
+                "error": {
+                    "type": "string"
+                },
+                "height": {
+                    "type": "integer"
+                }
+            }
+        },
         "blockatlas.Collection": {
             "type": "object",
             "properties": {
diff --git a/docs/swagger.yaml b/docs/swagger.yaml
index 4ddb42c..0656a0e 100644
--- a/docs/swagger.yaml
+++ b/docs/swagger.yaml
@@ -17,7 +17,8 @@ definitions:
       token_id:
         type: string
       type:
-        type: string
+        $ref: '#/definitions/blockatlas.CoinStatus'
+        type: object
     type: object
   api.Ticker:
     properties:
@@ -61,6 +62,13 @@ definitions:
     items:
       $ref: '#/definitions/api.Ticker'
     type: array
+  blockatlas.CoinStatus:
+    properties:
+      error:
+        type: string
+      height:
+        type: integer
+    type: object
   blockatlas.Collection:
     properties:
       address:
@@ -439,11 +447,6 @@ paths:
         in: query
         name: token
         type: string
-      - default: 1574483028
-        description: Start timestamp
-        in: query
-        name: time_start
-        type: integer
       - default: USD
         description: The currency to show coin info in
         in: query
@@ -482,78 +485,6 @@ paths:
       summary: Get ticker values for a specific market
       tags:
       - Market
-  /v2/{coin}/collections/{address}:
-    get:
-      consumes:
-      - application/json
-      description: Get all collections from the address
-      operationId: collections_v2
-      parameters:
-      - default: ethereum
-        description: the coin name
-        in: path
-        name: coin
-        required: true
-        type: string
-      - default: 0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB
-        description: the query address
-        in: path
-        name: address
-        required: true
-        type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.CollectionPage'
-        "500":
-          description: Internal Server Error
-          schema:
-            $ref: '#/definitions/ginutils.ApiError'
-      summary: Get Collections
-      tags:
-      - Collections
-  /v2/{coin}/collections/{owner}/collection/{collection_id}:
-    get:
-      consumes:
-      - application/json
-      description: Get a collection from the address
-      operationId: collection_v2
-      parameters:
-      - default: ethereum
-        description: the coin name
-        in: path
-        name: coin
-        required: true
-        type: string
-      - default: 0x0875BCab22dE3d02402bc38aEe4104e1239374a7
-        description: the query address
-        in: path
-        name: owner
-        required: true
-        type: string
-      - default: 0x06012c8cf97bead5deae237070f9587f8e7a266d
-        description: the query collection
-        in: path
-        name: collection_id
-        required: true
-        type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.CollectionPage'
-        "500":
-          description: Internal Server Error
-          schema:
-            $ref: '#/definitions/ginutils.ApiError'
-      summary: Get Collection
-      tags:
-      - Collections
   /v2/{coin}/staking/delegations/{address}:
     get:
       consumes:
@@ -680,30 +611,6 @@ paths:
       summary: Get Transactions
       tags:
       - Transactions
-  /v2/collectibles/categories:
-    post:
-      consumes:
-      - application/json
-      description: Get collection categories
-      operationId: collection_categories_v2
-      parameters:
-      - default: '{"60": ["0xb3624367b1ab37daef42e1a3a2ced012359659b0"]}'
-        description: Payload
-        in: body
-        name: data
-        required: true
-        schema:
-          type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.DocsResponse'
-      summary: Get list of collections from a specific coin and addresses
-      tags:
-      - Collections
   /v2/ns/lookup:
     get:
       description: Lookup ENS/ZNS to find registered addresses for multiple coins
@@ -780,102 +687,6 @@ paths:
       summary: Get Multiple Stake Delegations
       tags:
       - Staking
-  /v3/{coin}/collections/{address}:
-    get:
-      consumes:
-      - application/json
-      description: Get all collections from the address
-      operationId: collections_v3
-      parameters:
-      - default: ethereum
-        description: the coin name
-        in: path
-        name: coin
-        required: true
-        type: string
-      - default: 0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB
-        description: the query address
-        in: path
-        name: address
-        required: true
-        type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.CollectionPage'
-        "500":
-          description: Internal Server Error
-          schema:
-            $ref: '#/definitions/ginutils.ApiError'
-      summary: Get Collections
-      tags:
-      - Collections
-  /v3/{coin}/collections/{owner}/collection/{collection_id}:
-    get:
-      consumes:
-      - application/json
-      description: Get a collection from the address
-      operationId: collection_v3
-      parameters:
-      - default: ethereum
-        description: the coin name
-        in: path
-        name: coin
-        required: true
-        type: string
-      - default: 0x0875BCab22dE3d02402bc38aEe4104e1239374a7
-        description: the query address
-        in: path
-        name: owner
-        required: true
-        type: string
-      - default: 0x06012c8cf97bead5deae237070f9587f8e7a266d
-        description: the query collection
-        in: path
-        name: collection_id
-        required: true
-        type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.CollectionPage'
-        "500":
-          description: Internal Server Error
-          schema:
-            $ref: '#/definitions/ginutils.ApiError'
-      summary: Get Collection
-      tags:
-      - Collections
-  /v3/collectibles/categories:
-    post:
-      consumes:
-      - application/json
-      description: Get collection categories
-      operationId: collection_categories_v3
-      parameters:
-      - default: '{"60": ["0xb3624367b1ab37daef42e1a3a2ced012359659b0"]}'
-        description: Payload
-        in: body
-        name: data
-        required: true
-        schema:
-          type: string
-      produces:
-      - application/json
-      responses:
-        "200":
-          description: OK
-          schema:
-            $ref: '#/definitions/blockatlas.DocsResponse'
-      summary: Get list of collections from a specific coin and addresses
-      tags:
-      - Collections
   /v4/{coin}/collections/{owner}/collection/{collection_id}:
     get:
       consumes:
diff --git a/pkg/blockatlas/collectibles.go b/pkg/blockatlas/collectibles.go
index 74a272e..bad0815 100644
--- a/pkg/blockatlas/collectibles.go
+++ b/pkg/blockatlas/collectibles.go
@@ -1,7 +1,7 @@
 package blockatlas
 
 type (
-	Collection struct {
+	CollectionV3 struct {
 		Id              string `json:"id"`
 		Name            string `json:"name"`
 		Symbol          string `json:"symbol"`
@@ -18,14 +18,26 @@ type (
 		Type    string `json:"type"`
 	}
 
+	Collection struct {
+		Id           string `json:"id"`
+		Name         string `json:"name"`
+		ImageUrl     string `json:"image_url"`
+		Description  string `json:"description"`
+		ExternalLink string `json:"external_link"`
+		Total        int    `json:"total"`
+		Address      string `json:"address"`
+		Coin         uint   `json:"coin"`
+		Type         string `json:"-"`
+	}
+
+	CollectionPageV3 []CollectionV3
+
 	CollectionPage []Collection
 
 	Collectible struct {
-		ID               string `json:"id"`
-		CollectionID     string `json:"collection_id"`
-		TokenID          string `json:"token_id"`
-		CategoryContract string `json:"category_contract"`
-		// Deprecated: for support old client, ContractAddress eq CollectionID
+		ID              string `json:"id"`
+		CollectionID    string `json:"collection_id"`
+		TokenID         string `json:"token_id"`
 		ContractAddress string `json:"contract_address"`
 		Category        string `json:"category"`
 		ImageUrl        string `json:"image_url"`
@@ -39,4 +51,23 @@ type (
 	}
 
 	CollectiblePage []Collectible
+
+	CollectibleV3 struct {
+		ID               string `json:"id"`
+		CollectionID     string `json:"collection_id"`
+		TokenID          string `json:"token_id"`
+		CategoryContract string `json:"category_contract"`
+		ContractAddress  string `json:"contract_address"`
+		Category         string `json:"category"`
+		ImageUrl         string `json:"image_url"`
+		ExternalLink     string `json:"external_link"`
+		ProviderLink     string `json:"provider_link"`
+		Type             string `json:"type"`
+		Description      string `json:"description"`
+		Coin             uint   `json:"coin"`
+		Name             string `json:"name"`
+		Version          string `json:"nft_version"`
+	}
+
+	CollectiblePageV3 []CollectibleV3
 )
diff --git a/pkg/blockatlas/marshal.go b/pkg/blockatlas/marshal.go
index 00f6dd8..91b9a54 100644
--- a/pkg/blockatlas/marshal.go
+++ b/pkg/blockatlas/marshal.go
@@ -163,3 +163,35 @@ func (r CollectiblePage) MarshalJSON() ([]byte, error) {
 	page.Status = true
 	return json.Marshal(page)
 }
+
+// MarshalJSON returns a wrapped list of collections in JSON
+func (r CollectionPageV3) MarshalJSON() ([]byte, error) {
+	var page struct {
+		Total  int            `json:"total"`
+		Docs   []CollectionV3 `json:"docs"`
+		Status bool           `json:"status"`
+	}
+	page.Docs = []CollectionV3(r)
+	if page.Docs == nil {
+		page.Docs = make([]CollectionV3, 0)
+	}
+	page.Total = len(page.Docs)
+	page.Status = true
+	return json.Marshal(page)
+}
+
+// MarshalJSON returns a wrapped list of collectibles in JSON
+func (r CollectiblePageV3) MarshalJSON() ([]byte, error) {
+	var page struct {
+		Total  int             `json:"total"`
+		Docs   []CollectibleV3 `json:"docs"`
+		Status bool            `json:"status"`
+	}
+	page.Docs = []CollectibleV3(r)
+	if page.Docs == nil {
+		page.Docs = make([]CollectibleV3, 0)
+	}
+	page.Total = len(page.Docs)
+	page.Status = true
+	return json.Marshal(page)
+}
diff --git a/pkg/blockatlas/platform.go b/pkg/blockatlas/platform.go
index 28bbbb6..43c23e8 100644
--- a/pkg/blockatlas/platform.go
+++ b/pkg/blockatlas/platform.go
@@ -56,11 +56,8 @@ type (
 		GetCollections(owner string) (CollectionPage, error)
 		GetCollectibles(owner, collectibleID string) (CollectiblePage, error)
 
-		OldGetCollections(owner string) (CollectionPage, error)
-		OldGetCollectibles(owner, collectibleID string) (CollectiblePage, error)
-
-		GetCollectionsV4(owner string) (CollectionPage, error)
-		GetCollectiblesV4(owner, collectibleID string) (CollectiblePage, error)
+		GetCollectionsV3(owner string) (CollectionPageV3, error)
+		GetCollectiblesV3(owner, collectibleID string) (CollectiblePageV3, error)
 	}
 
 	// CustomAPI provides custom HTTP routes
diff --git a/platform/ethereum/collection.go b/platform/ethereum/collection.go
index 251df61..7cb836c 100644
--- a/platform/ethereum/collection.go
+++ b/platform/ethereum/collection.go
@@ -1,14 +1,12 @@
 package ethereum
 
 import (
-	"fmt"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
 	"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) {
@@ -16,252 +14,63 @@ func (p *Platform) GetCollections(owner string) (blockatlas.CollectionPage, erro
 	if err != nil {
 		return nil, err
 	}
-	page := NormalizeCollectionPage(collections, p.CoinIndex, owner)
-	return page, nil
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func (p *Platform) OldGetCollections(owner string) (blockatlas.CollectionPage, error) {
-	collections, err := p.collectionsClient.GetCollections(owner)
-	if err != nil {
-		return nil, err
-	}
-	page := OldNormalizeCollectionPage(collections, p.CoinIndex, owner)
-	return page, nil
+	return NormalizeCollections(collections, p.CoinIndex, owner), nil
 }
 
 func (p *Platform) GetCollectibles(owner, collectibleID string) (blockatlas.CollectiblePage, error) {
-	collection, items, err := p.collectionsClient.GetCollectibles(owner, collectibleID)
+	items, err := p.collectionsClient.GetCollectibles(owner, collectibleID)
 	if err != nil {
 		return nil, err
 	}
-	page := NormalizeCollectiblePage(collection, items, p.CoinIndex)
-	return page, nil
+	return NormalizeCollectiblePage(items, p.CoinIndex), nil
 }
 
-func NormalizeCollectionPage(collections []Collection, coinIndex uint, owner string) (page blockatlas.CollectionPage) {
+func NormalizeCollections(collections []Collection, coinIndex uint, owner string) (page blockatlas.CollectionPage) {
 	for _, collection := range collections {
-		if len(collection.Contracts) == 0 {
-			continue
-		}
 		item := NormalizeCollection(collection, coinIndex, owner)
-		if _, ok := supportedTypes[item.Type]; !ok {
-			continue
-		}
 		page = append(page, item)
 	}
-	return
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func (p *Platform) OldGetCollectibles(owner, collectibleID string) (blockatlas.CollectiblePage, error) {
-	collection, items, err := p.collectionsClient.OldGetCollectibles(owner, collectibleID)
-	if err != nil {
-		return nil, err
-	}
-	page := OldNormalizeCollectiblePage(collection, items, p.CoinIndex)
-	return page, nil
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func OldNormalizeCollectionPage(collections []Collection, coinIndex uint, owner string) (page blockatlas.CollectionPage) {
-	for _, collection := range collections {
-		if len(collection.Contracts) == 0 {
-			continue
-		}
-		item := OldNormalizeCollection(collection, coinIndex, owner)
-		if _, ok := supportedTypes[item.Type]; !ok {
-			continue
-		}
-		page = append(page, item)
-	}
-	return
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func OldNormalizeCollection(c Collection, coinIndex uint, owner string) blockatlas.Collection {
-	if len(c.Contracts) == 0 {
-		return blockatlas.Collection{}
-	}
-
-	description := blockatlas.GetValidParameter(c.Description, c.Contracts[0].Description)
-	symbol := blockatlas.GetValidParameter(c.Contracts[0].Symbol, "")
-	collectionId := blockatlas.GetValidParameter(c.Contracts[0].Address, "")
-	version := blockatlas.GetValidParameter(c.Contracts[0].NftVersion, "")
-	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
-	if _, ok := slugTokens[collectionType]; ok {
-		collectionId = createCollectionId(collectionId, c.Slug)
-	}
-
-	return blockatlas.Collection{
-		Name:            c.Name,
-		Symbol:          symbol,
-		Slug:            c.Slug,
-		ImageUrl:        c.ImageUrl,
-		Description:     description,
-		ExternalLink:    c.ExternalUrl,
-		Total:           int(c.Total.Int64()),
-		Id:              collectionId,
-		CategoryAddress: collectionId,
-		Address:         owner,
-		Version:         version,
-		Coin:            coinIndex,
-		Type:            collectionType,
-	}
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func OldNormalizeCollectible(c *Collection, a Collectible, coinIndex uint) blockatlas.Collectible {
-	address := blockatlas.GetValidParameter(c.Contracts[0].Address, "")
-	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
-	collectionID := address
-	if _, ok := slugTokens[collectionType]; ok {
-		collectionID = createCollectionId(address, c.Slug)
-	}
-	externalLink := blockatlas.GetValidParameter(a.ExternalLink, a.AssetContract.ExternalLink)
-	id := strings.Join([]string{a.AssetContract.Address, a.TokenId}, "-")
-	return blockatlas.Collectible{
-		ID:               id,
-		CollectionID:     collectionID,
-		ContractAddress:  address,
-		TokenID:          a.TokenId,
-		CategoryContract: a.AssetContract.Address,
-		Name:             a.Name,
-		Category:         c.Name,
-		ImageUrl:         a.ImagePreviewUrl,
-		ProviderLink:     a.Permalink,
-		ExternalLink:     externalLink,
-		Type:             collectionType,
-		Description:      a.Description,
-		Coin:             coinIndex,
-	}
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func createCollectionId(address, slug string) string {
-	return fmt.Sprintf("%s---%s", address, slug)
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func getCollectionId(collectionId string) string {
-	s := strings.Split(collectionId, "---")
-	if len(s) != 2 {
-		return collectionId
-	}
-	return s[1]
-}
-
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func OldNormalizeCollectiblePage(c *Collection, srcPage []Collectible, coinIndex uint) (page blockatlas.CollectiblePage) {
-	if len(c.Contracts) == 0 {
-		return
-	}
-	for _, src := range srcPage {
-		item := OldNormalizeCollectible(c, src, coinIndex)
-		if _, ok := supportedTypes[item.Type]; !ok {
-			continue
-		}
-		page = append(page, item)
-	}
-	return
+	return page
 }
 
 func NormalizeCollection(c Collection, coinIndex uint, owner string) blockatlas.Collection {
-	normalizeSupportedContracts(&c)
-	if len(c.Contracts) == 0 {
-		return blockatlas.Collection{}
-	}
-
-	description := blockatlas.GetValidParameter(c.Description, c.Contracts[0].Description)
-	symbol := blockatlas.GetValidParameter(c.Contracts[0].Symbol, "")
-	version := blockatlas.GetValidParameter(c.Contracts[0].NftVersion, "")
-	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
-
 	return blockatlas.Collection{
-		Name:            c.Name,
-		Symbol:          symbol,
-		Slug:            c.Slug,
-		ImageUrl:        c.ImageUrl,
-		Description:     description,
-		ExternalLink:    c.ExternalUrl,
-		Total:           int(c.Total.Int64()),
-		Id:              c.Slug,
-		CategoryAddress: c.Slug,
-		Address:         owner,
-		Version:         version,
-		Coin:            coinIndex,
-		Type:            collectionType,
-	}
-}
-
-func normalizeSupportedContracts(c *Collection) {
-	supportedContracts := make([]PrimaryAssetContract, 0)
-	for _, contract := range c.Contracts {
-		if _, ok := supportedTypes[contract.Type]; !ok {
-			continue
+		Name:         c.Name,
+		ImageUrl:     c.ImageUrl,
+		Description:  c.Description,
+		ExternalLink: c.ExternalUrl,
+		Total:        int(c.Total.Int64()),
+		Id:           c.Slug,
+		Address:      owner,
+		Coin:         coinIndex,
+	}
+}
+
+func NormalizeCollectiblePage(collectibles []Collectible, coinIndex uint) (page blockatlas.CollectiblePage) {
+	for _, collectible := range collectibles {
+		item := NormalizeCollectible(collectible, coinIndex)
+		if _, ok := supportedTypes[item.Type]; ok {
+			page = append(page, item)
 		}
-		supportedContracts = append(supportedContracts, contract)
 	}
-	c.Contracts = supportedContracts
+	return page
 }
 
-func NormalizeCollectiblePage(c *Collection, srcPage []Collectible, coinIndex uint) (page blockatlas.CollectiblePage) {
-	normalizeSupportedContracts(c)
-	if len(c.Contracts) == 0 {
-		return
-	}
-	for _, src := range srcPage {
-		item := NormalizeCollectible(c, src, coinIndex)
-		if _, ok := supportedTypes[item.Type]; !ok {
-			continue
-		}
-		page = append(page, item)
-	}
-	return
-}
-
-func NormalizeCollectible(c *Collection, a Collectible, coinIndex uint) blockatlas.Collectible {
-	address := blockatlas.GetValidParameter(c.Contracts[0].Address, "")
-	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
-	externalLink := blockatlas.GetValidParameter(a.ExternalLink, a.AssetContract.ExternalLink)
+func NormalizeCollectible(a Collectible, coinIndex uint) blockatlas.Collectible {
 	id := strings.Join([]string{a.AssetContract.Address, a.TokenId}, "-")
 	return blockatlas.Collectible{
-		ID:               id,
-		CollectionID:     c.Slug,
-		ContractAddress:  address,
-		TokenID:          a.TokenId,
-		CategoryContract: a.AssetContract.Address,
-		Name:             a.Name,
-		Category:         c.Name,
-		ImageUrl:         a.ImagePreviewUrl,
-		ProviderLink:     a.Permalink,
-		ExternalLink:     externalLink,
-		Type:             collectionType,
-		Description:      a.Description,
-		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
-			}
-		}
+		ID:              id,
+		CollectionID:    a.Collection.Slug,
+		TokenID:         a.TokenId,
+		ContractAddress: a.AssetContract.Address,
+		Name:            a.Name,
+		Category:        a.Collection.Name,
+		ImageUrl:        a.ImagePreviewUrl,
+		ProviderLink:    a.Permalink,
+		ExternalLink:    a.Collection.ExternalLink,
+		Type:            a.AssetContract.Type,
+		Description:     a.Description,
+		Coin:            coinIndex,
+		Version:         a.AssetContract.Version,
 	}
-	return nil
 }
diff --git a/platform/ethereum/collectionV3.go b/platform/ethereum/collectionV3.go
new file mode 100644
index 0000000..dc35a5a
--- /dev/null
+++ b/platform/ethereum/collectionV3.go
@@ -0,0 +1,114 @@
+package ethereum
+
+import (
+	"github.com/trustwallet/blockatlas/pkg/blockatlas"
+	"strings"
+)
+
+func (p *Platform) GetCollectionsV3(owner string) (blockatlas.CollectionPageV3, error) {
+	collections, err := p.collectionsClient.GetCollections(owner)
+	if err != nil {
+		return nil, err
+	}
+	page := NormalizeCollectionPageV3(collections, p.CoinIndex, owner)
+	return page, nil
+}
+
+func (p *Platform) GetCollectiblesV3(owner, collectibleID string) (blockatlas.CollectiblePageV3, error) {
+	collection, items, err := p.collectionsClient.GetCollectiblesV3(owner, collectibleID)
+	if err != nil {
+		return nil, err
+	}
+	page := NormalizeCollectiblePageV3(collection, items, p.CoinIndex)
+	return page, nil
+}
+
+func NormalizeCollectionPageV3(collections []Collection, coinIndex uint, owner string) (page blockatlas.CollectionPageV3) {
+	for _, collection := range collections {
+		if len(collection.Contracts) == 0 {
+			continue
+		}
+		item := NormalizeCollectionV3(collection, coinIndex, owner)
+		if _, ok := supportedTypes[item.Type]; !ok {
+			continue
+		}
+		page = append(page, item)
+	}
+	return
+}
+
+func NormalizeCollectionV3(c Collection, coinIndex uint, owner string) blockatlas.CollectionV3 {
+	normalizeSupportedContracts(&c)
+	if len(c.Contracts) == 0 {
+		return blockatlas.CollectionV3{}
+	}
+
+	description := blockatlas.GetValidParameter(c.Description, c.Contracts[0].Description)
+	symbol := blockatlas.GetValidParameter(c.Contracts[0].Symbol, "")
+	version := blockatlas.GetValidParameter(c.Contracts[0].NftVersion, "")
+	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
+
+	return blockatlas.CollectionV3{
+		Name:            c.Name,
+		Symbol:          symbol,
+		Slug:            c.Slug,
+		ImageUrl:        c.ImageUrl,
+		Description:     description,
+		ExternalLink:    c.ExternalUrl,
+		Total:           int(c.Total.Int64()),
+		Id:              c.Slug,
+		CategoryAddress: c.Slug,
+		Address:         owner,
+		Version:         version,
+		Coin:            coinIndex,
+		Type:            collectionType,
+	}
+}
+
+func normalizeSupportedContracts(c *Collection) {
+	supportedContracts := make([]PrimaryAssetContract, 0)
+	for _, contract := range c.Contracts {
+		if _, ok := supportedTypes[contract.Type]; !ok {
+			continue
+		}
+		supportedContracts = append(supportedContracts, contract)
+	}
+	c.Contracts = supportedContracts
+}
+
+func NormalizeCollectiblePageV3(c *Collection, srcPage []Collectible, coinIndex uint) (page blockatlas.CollectiblePageV3) {
+	normalizeSupportedContracts(c)
+	if len(c.Contracts) == 0 {
+		return
+	}
+	for _, src := range srcPage {
+		item := NormalizeCollectibleV3(c, src, coinIndex)
+		if _, ok := supportedTypes[item.Type]; !ok {
+			continue
+		}
+		page = append(page, item)
+	}
+	return
+}
+
+func NormalizeCollectibleV3(c *Collection, a Collectible, coinIndex uint) blockatlas.CollectibleV3 {
+	address := blockatlas.GetValidParameter(c.Contracts[0].Address, "")
+	collectionType := blockatlas.GetValidParameter(c.Contracts[0].Type, "")
+	externalLink := blockatlas.GetValidParameter(a.ExternalLink, a.AssetContract.ExternalLink)
+	id := strings.Join([]string{a.AssetContract.Address, a.TokenId}, "-")
+	return blockatlas.CollectibleV3{
+		ID:               id,
+		CollectionID:     c.Slug,
+		ContractAddress:  address,
+		TokenID:          a.TokenId,
+		CategoryContract: a.AssetContract.Address,
+		Name:             a.Name,
+		Category:         c.Name,
+		ImageUrl:         a.ImagePreviewUrl,
+		ProviderLink:     a.Permalink,
+		ExternalLink:     externalLink,
+		Type:             collectionType,
+		Description:      a.Description,
+		Coin:             coinIndex,
+	}
+}
diff --git a/platform/ethereum/collection_client.go b/platform/ethereum/collection_client.go
index 2c4bdeb..5a049e7 100644
--- a/platform/ethereum/collection_client.go
+++ b/platform/ethereum/collection_client.go
@@ -5,6 +5,7 @@ import (
 	"github.com/trustwallet/blockatlas/pkg/errors"
 	"net/url"
 	"strconv"
+	"strings"
 )
 
 type CollectionsClient struct {
@@ -20,30 +21,7 @@ func (c CollectionsClient) GetCollections(owner string) (page []Collection, err
 	return
 }
 
-func (c CollectionsClient) GetCollectibles(owner string, collectibleID string) (*Collection, []Collectible, error) {
-	collections, err := c.GetCollections(owner)
-	if err != nil {
-		return nil, nil, err
-	}
-	collection := searchCollection(collections, collectibleID)
-	if collection == nil {
-		return nil, nil, errors.E("collectible not found", errors.TypePlatformClient,
-			errors.Params{"collectibleID": collectibleID})
-	}
-
-	query := url.Values{
-		"owner": {owner},
-		"limit": {strconv.Itoa(300)},
-	}
-
-	query.Set("collection", collection.Slug)
-
-	var page CollectiblePage
-	err = c.Get(&page, "api/v1/assets", query)
-	return collection, page.Collectibles, err
-}
-
-func (c CollectionsClient) GetCollectiblesV4(owner string, collectibleID string) ([]Collectible, error) {
+func (c CollectionsClient) GetCollectibles(owner string, collectibleID string) ([]Collectible, error) {
 	query := url.Values{
 		"owner":      {owner},
 		"collection": {collectibleID},
@@ -55,14 +33,12 @@ func (c CollectionsClient) GetCollectiblesV4(owner string, collectibleID string)
 	return page.Collectibles, err
 }
 
-//TODO: remove once most of the clients will be updated (deadline: March 17th)
-func (c CollectionsClient) OldGetCollectibles(owner string, collectibleID string) (*Collection, []Collectible, error) {
+func (c CollectionsClient) GetCollectiblesV3(owner string, collectibleID string) (*Collection, []Collectible, error) {
 	collections, err := c.GetCollections(owner)
 	if err != nil {
 		return nil, nil, err
 	}
-	id := getCollectionId(collectibleID)
-	collection := oldSearchCollection(collections, id)
+	collection := searchCollection(collections, collectibleID)
 	if collection == nil {
 		return nil, nil, errors.E("collectible not found", errors.TypePlatformClient,
 			errors.Params{"collectibleID": collectibleID})
@@ -73,15 +49,18 @@ func (c CollectionsClient) OldGetCollectibles(owner string, collectibleID string
 		"limit": {strconv.Itoa(300)},
 	}
 
-	for _, i := range collection.Contracts {
-		if _, ok := slugTokens[i.Type]; ok {
-			query.Set("collection", collection.Slug)
-			break
-		}
-		query.Add("asset_contract_addresses", i.Address)
-	}
+	query.Set("collection", collection.Slug)
 
 	var page CollectiblePage
 	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
+}
diff --git a/platform/ethereum/collection_v4.go b/platform/ethereum/collection_v4.go
deleted file mode 100644
index 2eed537..0000000
--- a/platform/ethereum/collection_v4.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package ethereum
-
-import (
-	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"strings"
-)
-
-func (p *Platform) GetCollectionsV4(owner string) (blockatlas.CollectionPage, error) {
-	collections, err := p.collectionsClient.GetCollections(owner)
-	if err != nil {
-		return nil, err
-	}
-	return NormalizeCollectionsV4(collections, p.CoinIndex, owner), nil
-}
-
-func (p *Platform) GetCollectiblesV4(owner, collectibleID string) (blockatlas.CollectiblePage, error) {
-	items, err := p.collectionsClient.GetCollectiblesV4(owner, collectibleID)
-	if err != nil {
-		return nil, err
-	}
-	return NormalizeCollectiblePageV4(items, p.CoinIndex), nil
-}
-
-func NormalizeCollectionsV4(collections []Collection, coinIndex uint, owner string) (page blockatlas.CollectionPage) {
-	for _, collection := range collections {
-		item := NormalizeCollectionV4(collection, coinIndex, owner)
-		page = append(page, item)
-	}
-	return page
-}
-
-func NormalizeCollectionV4(c Collection, coinIndex uint, owner string) blockatlas.Collection {
-	return blockatlas.Collection{
-		Name:         c.Name,
-		Slug:         c.Slug,
-		ImageUrl:     c.ImageUrl,
-		Description:  c.Description,
-		ExternalLink: c.ExternalUrl,
-		Total:        int(c.Total.Int64()),
-		Id:           c.Slug,
-		Address:      owner,
-		Coin:         coinIndex,
-	}
-}
-
-func NormalizeCollectiblePageV4(collectibles []Collectible, coinIndex uint) (page blockatlas.CollectiblePage) {
-	for _, collectible := range collectibles {
-		item := NormalizeCollectibleV4(collectible, coinIndex)
-		if _, ok := supportedTypes[item.Type]; ok {
-			page = append(page, item)
-		}
-	}
-	return page
-}
-
-func NormalizeCollectibleV4(a Collectible, coinIndex uint) blockatlas.Collectible {
-	id := strings.Join([]string{a.AssetContract.Address, a.TokenId}, "-")
-	return blockatlas.Collectible{
-		ID:              id,
-		CollectionID:    a.Collection.Slug,
-		TokenID:         a.TokenId,
-		ContractAddress: a.AssetContract.Address,
-		Name:            a.Name,
-		Category:        a.Collection.Name,
-		ImageUrl:        a.ImagePreviewUrl,
-		ProviderLink:    a.Permalink,
-		ExternalLink:    a.Collection.ExternalLink,
-		Type:            a.AssetContract.Type,
-		Description:     a.Description,
-		Coin:            coinIndex,
-		Version:         a.AssetContract.Version,
-	}
-}


Test patch:
diff --git a/platform/ethereum/collection_test.go b/platform/ethereum/collection_test.go
index 056743e..771baa5 100644
--- a/platform/ethereum/collection_test.go
+++ b/platform/ethereum/collection_test.go
@@ -2,142 +2,282 @@ package ethereum
 
 import (
 	"encoding/json"
-	"fmt"
 	"github.com/stretchr/testify/assert"
 	"github.com/trustwallet/blockatlas/coin"
 	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"math/big"
 	"testing"
 )
 
-const collectionsOwner = "0x0875BCab22dE3d02402bc38aEe4104e1239374a7"
+const collectionsOwnerV4 = "0x0875BCab22dE3d02402bc38aEe4104e1239374a7"
 
-const collectionsSrc = `
+const collectionsSrcV4 = `
 [
-  {
-    "primary_asset_contracts": [
-      {
-        "address": "0x06012c8cf97bead5deae237070f9587f8e7a266d",
-        "name": "CryptoKitties",
-        "symbol": "CKITTY",
-        "description": "CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-        "external_link": "https://www.cryptokitties.co/",
-        "nft_version": "1.0",
-        "schema_name": "ERC721",
-        "display_data": {
-          "images": [
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/564155.svg",
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/546630.svg",
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/441529.svg",
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/552435.svg",
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/524748.png",
-            "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/540800.svg"
-          ],
-          "card_display_style": "padded"
-        },
-        "image_url": "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png"
-      }
-    ],
-    "name": "CryptoKitties",
-    "slug": "cryptokitties",
-    "image_url": "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
-    "description": "CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-    "external_url": "https://www.cryptokitties.co/",
-    "featured_image_url": "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556589429.png",
-    "created_date": "2019-04-26T22:13:04.207050",
-    "owned_asset_count": 3
-  },
-  {
-    "primary_asset_contracts": [
-      {
-        "address": "0xf629cbd94d3791c9250152bd8dfbdf380e2a3b9c",
-        "name": "Coin",
-        "symbol": "",
-        "description": null,
-        "external_link": null,
-        "nft_version": null,
-        "schema_name": "ERC20",
-        "display_data": {}
-      }
-    ],
-    "name": "Enjin Token",
-    "slug": "enjin-token",
-    "image_url": "https://storage.googleapis.com/opensea-static/tokens-high-res/ENJ.png",
-    "description": "This is the collection of owners of EnjinCoin",
-    "external_url": null,
-    "owned_asset_count": 20000000000000000000
-  },
-  {
-    "primary_asset_contracts": [
-      {
-        "address": "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-        "name": "Enjin",
-        "symbol": "",
-        "description": "",
-        "external_link": null,
-        "nft_version": null,
-        "schema_name": "ERC1155",
-        "display_data": {},
-        "owner": null,
-        "created_date": "2019-08-02T23:43:14.666153",
-        "asset_contract_type": "semi-fungible"
-      }
-    ],
-    "name": "Age of Rust",
-    "slug": "age-of-rust",
-    "image_url": "https://storage.opensea.io/age-of-rust-1561960816.jpg",
-    "description": "Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
-    "external_url": "https://www.ageofrust.games/",
-    "featured_image_url": null,
-    "created_date": "2019-09-03T02:35:56.063685",
-    "owned_asset_count": 1
-  }
+   {
+      "primary_asset_contracts":[
+         {
+            "address":"0x06012c8cf97bead5deae237070f9587f8e7a266d",
+            "name":"CryptoKitties",
+            "symbol":"CKITTY",
+            "description":"CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
+            "external_link":"https://www.cryptokitties.co/",
+            "nft_version":"1.0",
+            "schema_name":"ERC721",
+            "display_data":{
+               "images":[
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/564155.svg",
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/546630.svg",
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/441529.svg",
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/552435.svg",
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/524748.png",
+                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/540800.svg"
+               ],
+               "card_display_style":"padded"
+            },
+            "image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png"
+         }
+      ],
+      "name":"CryptoKitties",
+      "slug":"cryptokitties",
+      "image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
+      "description":"CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
+      "external_url":"https://www.cryptokitties.co/",
+      "featured_image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556589429.png",
+      "created_date":"2019-04-26T22:13:04.207050",
+      "owned_asset_count":3
+   },
+   {
+      "primary_asset_contracts":[
+         {
+            "address":"0xfaafdc07907ff5120a76b34b731b278c38d6043c",
+            "name":"Enjin",
+            "symbol":"",
+            "description":"",
+            "external_link":null,
+            "nft_version":null,
+            "schema_name":"ERC1155",
+            "display_data":{
+
+            },
+            "owner":null,
+            "created_date":"2019-08-02T23:43:14.666153",
+            "asset_contract_type":"semi-fungible"
+         }
+      ],
+      "name":"Age of Rust",
+      "slug":"age-of-rust",
+      "image_url":"https://storage.opensea.io/age-of-rust-1561960816.jpg",
+      "description":"Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
+      "external_url":"https://www.ageofrust.games/",
+      "featured_image_url":null,
+      "created_date":"2019-09-03T02:35:56.063685",
+      "owned_asset_count":1
+   },
+   {
+      "primary_asset_contracts":[
+         {
+            "address":"0xee85966b4974d3c6f71a2779cc3b6f53afbc2b68",
+            "asset_contract_type":"fungible",
+            "created_date":"2019-10-16T07:36:16.102163",
+            "name":"Rare Chest",
+            "nft_version":null,
+            "opensea_version":null,
+            "owner":1610615,
+            "schema_name":"ERC20",
+            "symbol":"",
+            "total_supply":"1",
+            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+            "default_to_fiat":false,
+            "dev_buyer_fee_basis_points":0,
+            "dev_seller_fee_basis_points":0,
+            "only_proxied_transfers":false,
+            "opensea_buyer_fee_basis_points":0,
+            "opensea_seller_fee_basis_points":250,
+            "buyer_fee_basis_points":0,
+            "seller_fee_basis_points":250,
+            "payout_address":null
+         },
+         {
+            "address":"0x20d4cec36528e1c4563c1bfbe3de06aba70b22b4",
+            "asset_contract_type":"fungible",
+            "created_date":"2019-10-16T08:06:42.727997",
+            "name":"Legendary Chest",
+            "nft_version":null,
+            "opensea_version":null,
+            "owner":1610615,
+            "schema_name":"ERC20",
+            "symbol":"",
+            "total_supply":"1",
+            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+            "default_to_fiat":false,
+            "dev_buyer_fee_basis_points":0,
+            "dev_seller_fee_basis_points":0,
+            "only_proxied_transfers":false,
+            "opensea_buyer_fee_basis_points":0,
+            "opensea_seller_fee_basis_points":250,
+            "buyer_fee_basis_points":0,
+            "seller_fee_basis_points":250,
+            "payout_address":null
+         },
+         {
+            "address":"0x0e3a2a1f2146d86a604adc220b4967a898d7fe07",
+            "asset_contract_type":"non-fungible",
+            "created_date":"2019-11-01T06:39:04.363034",
+            "name":"Gods Unchained Cards",
+            "nft_version":"3.0",
+            "opensea_version":null,
+            "owner":1691695,
+            "schema_name":"ERC721",
+            "symbol":"",
+            "total_supply":"1",
+            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+            "default_to_fiat":false,
+            "dev_buyer_fee_basis_points":0,
+            "dev_seller_fee_basis_points":0,
+            "only_proxied_transfers":false,
+            "opensea_buyer_fee_basis_points":0,
+            "opensea_seller_fee_basis_points":250,
+            "buyer_fee_basis_points":0,
+            "seller_fee_basis_points":250,
+            "payout_address":null
+         },
+         {
+            "address":"0x564cb55c655f727b61d9baf258b547ca04e9e548",
+            "asset_contract_type":"non-fungible",
+            "created_date":"2019-10-29T12:28:37.643714",
+            "name":"Gods Unchained",
+            "nft_version":"3.0",
+            "opensea_version":null,
+            "owner":1691695,
+            "schema_name":"ERC721",
+            "symbol":"",
+            "total_supply":"205",
+            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+            "default_to_fiat":false,
+            "dev_buyer_fee_basis_points":0,
+            "dev_seller_fee_basis_points":0,
+            "only_proxied_transfers":false,
+            "opensea_buyer_fee_basis_points":0,
+            "opensea_seller_fee_basis_points":250,
+            "buyer_fee_basis_points":0,
+            "seller_fee_basis_points":250,
+            "payout_address":null
+         }
+      ],
+      "traits":{
+         "mana":{
+            "min":1,
+            "max":18
+         },
+         "health":{
+            "min":1,
+            "max":16
+         },
+         "attack":{
+            "min":1,
+            "max":16
+         }
+      },
+      "stats":{
+         "seven_day_volume":270.564697067863,
+         "seven_day_change":-0.014179906699531201,
+         "total_volume":11825.0384171324,
+         "count":6835331,
+         "num_owners":10791,
+         "market_cap":150892.14138332763,
+         "average_price":0.0396514694030496,
+         "items_sold":298233
+      },
+      "banner_image_url":null,
+      "chat_url":null,
+      "created_date":"2019-11-13T03:01:42.051246",
+      "default_to_fiat":false,
+      "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+      "dev_buyer_fee_basis_points":"0",
+      "dev_seller_fee_basis_points":"0",
+      "display_data":{
+         "images":[
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/25233.png",
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/152875.png",
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/25669.png",
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9237.png",
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9228.png",
+            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9231.png"
+         ],
+         "card_display_style":"contain"
+      },
+      "external_url":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+      "featured":true,
+      "featured_image_url":"https://storage.opensea.io/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab-featured-1556589419.png",
+      "hidden":false,
+      "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+      "is_subject_to_whitelist":false,
+      "large_image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ",
+      "name":"Gods Unchained",
+      "only_proxied_transfers":false,
+      "opensea_buyer_fee_basis_points":"0",
+      "opensea_seller_fee_basis_points":"250",
+      "payout_address":null,
+      "require_email":false,
+      "short_description":null,
+      "slug":"gods-unchained",
+      "wiki_url":null,
+      "owned_asset_count":535
+   }
 ]
 `
 
-var collection1Dst = blockatlas.Collection{
-	Name:            "CryptoKitties",
-	Symbol:          "CKITTY",
-	Slug:            "cryptokitties",
-	ImageUrl:        "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
-	Description:     "CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-	ExternalLink:    "https://www.cryptokitties.co/",
-	Total:           3,
-	CategoryAddress: "cryptokitties",
-	Id:              "cryptokitties",
-	Address:         "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	Version:         "1.0",
-	Coin:            60,
-	Type:            "ERC721",
+var collection1DstV4 = blockatlas.Collection{
+	Name:         "CryptoKitties",
+	ImageUrl:     "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
+	Description:  "CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
+	ExternalLink: "https://www.cryptokitties.co/",
+	Total:        3,
+	Id:           "cryptokitties",
+	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
+	Coin:         60,
 }
 
-var collection2Dst = blockatlas.Collection{
-	Name:            "Age of Rust",
-	Symbol:          "",
-	Slug:            "age-of-rust",
-	ImageUrl:        "https://storage.opensea.io/age-of-rust-1561960816.jpg",
-	Description:     "Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
-	ExternalLink:    "https://www.ageofrust.games/",
-	Total:           1,
-	CategoryAddress: "age-of-rust",
-	Id:              "age-of-rust",
-	Address:         "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	Version:         "",
-	Coin:            60,
-	Type:            "ERC1155",
+var collection2DstV4 = blockatlas.Collection{
+	Name:         "Age of Rust",
+	ImageUrl:     "https://storage.opensea.io/age-of-rust-1561960816.jpg",
+	Description:  "Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
+	ExternalLink: "https://www.ageofrust.games/",
+	Total:        1,
+	Id:           "age-of-rust",
+	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
+	Coin:         60,
 }
 
-func TestNormalizeCollection(t *testing.T) {
+var collection3DstV4 = blockatlas.Collection{
+	Name:         "Gods Unchained",
+	ImageUrl:     "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
+	Description:  "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
+	ExternalLink: "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
+	Total:        535,
+	Id:           "gods-unchained",
+	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
+	Coin:         60,
+}
+
+func TestNormalizeCollectionV4(t *testing.T) {
 	var collections []Collection
-	err := json.Unmarshal([]byte(collectionsSrc), &collections)
+	err := json.Unmarshal([]byte(collectionsSrcV4), &collections)
 	assert.Nil(t, err)
-	page := NormalizeCollectionPage(collections, coin.ETH, collectionsOwner)
-	assert.Equal(t, len(page), 2, "collections could not be normalized")
-	expected := blockatlas.CollectionPage{collection1Dst, collection2Dst}
+	page := NormalizeCollections(collections, coin.ETH, collectionsOwnerV4)
+	assert.Equal(t, 3, len(page), "collections could not be normalized")
+	expected := blockatlas.CollectionPage{collection1DstV4, collection2DstV4, collection3DstV4}
 	assert.Equal(t, page, expected, "collections don't equal")
 }
 
-const collectibleSrc = `
+const collectibleSrcV4 = `
 [
   {
     "token_id": "54277541829991970107421667568590323026590803461896874578610080514640537714688",
@@ -153,259 +293,38 @@ const collectibleSrc = `
       "nft_version": null,
       "schema_name": "ERC1155",
 	  "display_data": {}
+    },
+	"collection": {
+		"slug": "age-of-rust",
+        "name": "Age of Rust",
+        "external_url": "https://opensea.io/"
     },
     "permalink": "https://opensea.io/assets/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688"
   }
 ]
 `
 
-var collectibleCollectionDst = Collection{
-	Name:        "Age of Rust",
-	ImageUrl:    "https://storage.opensea.io/age-of-rust-1561960816.jpg",
-	Description: "Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
-	ExternalUrl: "https://www.ageofrust.games/",
-	Total:       big.NewInt(1),
-	Slug:        "age-of-rust",
-	Contracts: []PrimaryAssetContract{
-		{
-			Name:        "Age of Rust",
-			Address:     "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-			NftVersion:  "",
-			Symbol:      "",
-			Description: "",
-			Type:        "ERC1155",
-			Url:         "",
-		},
-	},
-}
-
-var collectibleDst = blockatlas.Collectible{
-	ID:               "0xfaafdc07907ff5120a76b34b731b278c38d6043c-54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	CollectionID:     "age-of-rust",
-	TokenID:          "54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	CategoryContract: "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-	ContractAddress:  "0x5574Cd97432cEd0D7Caf58ac3c4fEDB2061C98fB",
-	Category:         "Age of Rust",
-	ImageUrl:         "https://storage.opensea.io/0xfaafdc07907ff5120a76b34b731b278c38d6043c-preview/54277541829991970107421667568590323026590803461896874578610080514640537714688-1564858807.png",
-	ExternalLink:     "",
-	ProviderLink:     "https://opensea.io/assets/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	Type:             "ERC1155",
-	Description:      "Rustbits are the main token of use within the Age of Rust game universe. You need Rustbits to not only play Age of Rust, but also to purchase in-game cryptoitems as well. Rustbits are radioactive rust scraped off of hulls of abandoned ships that are in orbit around a hidden planet, which is also a gas giant. The planet is so radioactive, it damages ships and kills anyone that gets close to it. Getting bits of rust off of ships is highly rare and prized.",
-	Coin:             60,
-	Name:             "Rustbits",
+var collectibleDstV4 = blockatlas.Collectible{
+	ID:              "0xfaafdc07907ff5120a76b34b731b278c38d6043c-54277541829991970107421667568590323026590803461896874578610080514640537714688",
+	CollectionID:    "age-of-rust",
+	TokenID:         "54277541829991970107421667568590323026590803461896874578610080514640537714688",
+	ContractAddress: "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
+	Category:        "Age of Rust",
+	ImageUrl:        "https://storage.opensea.io/0xfaafdc07907ff5120a76b34b731b278c38d6043c-preview/54277541829991970107421667568590323026590803461896874578610080514640537714688-1564858807.png",
+	ExternalLink:    "https://opensea.io/",
+	ProviderLink:    "https://opensea.io/assets/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688",
+	Type:            "ERC1155",
+	Description:     "Rustbits are the main token of use within the Age of Rust game universe. You need Rustbits to not only play Age of Rust, but also to purchase in-game cryptoitems as well. Rustbits are radioactive rust scraped off of hulls of abandoned ships that are in orbit around a hidden planet, which is also a gas giant. The planet is so radioactive, it damages ships and kills anyone that gets close to it. Getting bits of rust off of ships is highly rare and prized.",
+	Coin:            60,
+	Name:            "Rustbits",
 }
 
-func TestNormalizeCollectible(t *testing.T) {
-	var collectible []Collectible
-	err := json.Unmarshal([]byte(collectibleSrc), &collectible)
+func TestNormalizeCollectibleV4(t *testing.T) {
+	var collectibles []Collectible
+	err := json.Unmarshal([]byte(collectibleSrcV4), &collectibles)
 	assert.Nil(t, err)
-	page := NormalizeCollectiblePage(&collectibleCollectionDst, collectible, coin.ETH)
+	page := NormalizeCollectiblePage(collectibles, coin.ETH)
 	assert.Equal(t, len(page), 1, "collectible could not be normalized")
-	expected := blockatlas.CollectiblePage{collectibleDst}
+	expected := blockatlas.CollectiblePage{collectibleDstV4}
 	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) {
-	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)
-		})
-	}
-
-}
-
-func TestNormalizeSupportedContracts(t *testing.T) {
-	var contracts []PrimaryAssetContract
-	err := json.Unmarshal([]byte(rawAssetContracts), &contracts)
-	assert.Nil(t, err)
-	var collection = Collection{}
-	collection.Contracts = contracts
-	normalizeSupportedContracts(&collection)
-	assert.Equal(t, len(collection.Contracts), 2, "normalizeSupportedContracts with incorrect len")
-	var expectedContracts []PrimaryAssetContract
-	err = json.Unmarshal([]byte(rawAssetContractsExpected), &expectedContracts)
-	assert.Nil(t, err)
-	assert.Equal(t, collection.Contracts, expectedContracts, "normalizeSupportedContracts expectedContracts")
-}
-
-const rawAssetContracts = `[
-    {
-        "address": "0xee85966b4974d3c6f71a2779cc3b6f53afbc2b68",
-        "asset_contract_type": "fungible",
-        "created_date": "2019-10-16T07:36:16.102163",
-        "name": "Rare Chest",
-        "nft_version": null,
-        "opensea_version": null,
-        "owner": 1610615,
-        "schema_name": "ERC20",
-        "symbol": "",
-        "total_supply": "1",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    },
-    {
-        "address": "0x20d4cec36528e1c4563c1bfbe3de06aba70b22b4",
-        "asset_contract_type": "fungible",
-        "created_date": "2019-10-16T08:06:42.727997",
-        "name": "Legendary Chest",
-        "nft_version": null,
-        "opensea_version": null,
-        "owner": 1610615,
-        "schema_name": "ERC20",
-        "symbol": "",
-        "total_supply": "1",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    },
-    {
-        "address": "0x0e3a2a1f2146d86a604adc220b4967a898d7fe07",
-        "asset_contract_type": "non-fungible",
-        "created_date": "2019-11-01T06:39:04.363034",
-        "name": "Gods Unchained Cards",
-        "nft_version": "3.0",
-        "opensea_version": null,
-        "owner": 1691695,
-        "schema_name": "ERC721",
-        "symbol": "",
-        "total_supply": "1",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    },
-    {
-        "address": "0x564cb55c655f727b61d9baf258b547ca04e9e548",
-        "asset_contract_type": "non-fungible",
-        "created_date": "2019-10-29T12:28:37.643714",
-        "name": "Gods Unchained",
-        "nft_version": "3.0",
-        "opensea_version": null,
-        "owner": 1691695,
-        "schema_name": "ERC721",
-        "symbol": "",
-        "total_supply": "205",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    }
-]`
-
-const rawAssetContractsExpected = `[{
-        "address": "0x0e3a2a1f2146d86a604adc220b4967a898d7fe07",
-        "asset_contract_type": "non-fungible",
-        "created_date": "2019-11-01T06:39:04.363034",
-        "name": "Gods Unchained Cards",
-        "nft_version": "3.0",
-        "opensea_version": null,
-        "owner": 1691695,
-        "schema_name": "ERC721",
-        "symbol": "",
-        "total_supply": "1",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    },
-    {
-        "address": "0x564cb55c655f727b61d9baf258b547ca04e9e548",
-        "asset_contract_type": "non-fungible",
-        "created_date": "2019-10-29T12:28:37.643714",
-        "name": "Gods Unchained",
-        "nft_version": "3.0",
-        "opensea_version": null,
-        "owner": 1691695,
-        "schema_name": "ERC721",
-        "symbol": "",
-        "total_supply": "205",
-        "description": "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-        "external_link": "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-        "image_url": "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-        "default_to_fiat": false,
-        "dev_buyer_fee_basis_points": 0,
-        "dev_seller_fee_basis_points": 0,
-        "only_proxied_transfers": false,
-        "opensea_buyer_fee_basis_points": 0,
-        "opensea_seller_fee_basis_points": 250,
-        "buyer_fee_basis_points": 0,
-        "seller_fee_basis_points": 250,
-        "payout_address": null
-    }
-]`
diff --git a/platform/ethereum/collection_v4_test.go b/platform/ethereum/collection_v4_test.go
deleted file mode 100644
index ed74216..0000000
--- a/platform/ethereum/collection_v4_test.go
+++ /dev/null
@@ -1,337 +0,0 @@
-package ethereum
-
-import (
-	"encoding/json"
-	"github.com/stretchr/testify/assert"
-	"github.com/trustwallet/blockatlas/coin"
-	"github.com/trustwallet/blockatlas/pkg/blockatlas"
-	"testing"
-)
-
-const collectionsOwnerV4 = "0x0875BCab22dE3d02402bc38aEe4104e1239374a7"
-
-const collectionsSrcV4 = `
-[
-   {
-      "primary_asset_contracts":[
-         {
-            "address":"0x06012c8cf97bead5deae237070f9587f8e7a266d",
-            "name":"CryptoKitties",
-            "symbol":"CKITTY",
-            "description":"CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-            "external_link":"https://www.cryptokitties.co/",
-            "nft_version":"1.0",
-            "schema_name":"ERC721",
-            "display_data":{
-               "images":[
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/564155.svg",
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/546630.svg",
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/441529.svg",
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/552435.svg",
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/524748.png",
-                  "https://storage.googleapis.com/ck-kitty-image/0x06012c8cf97bead5deae237070f9587f8e7a266d/540800.svg"
-               ],
-               "card_display_style":"padded"
-            },
-            "image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png"
-         }
-      ],
-      "name":"CryptoKitties",
-      "slug":"cryptokitties",
-      "image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
-      "description":"CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-      "external_url":"https://www.cryptokitties.co/",
-      "featured_image_url":"https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556589429.png",
-      "created_date":"2019-04-26T22:13:04.207050",
-      "owned_asset_count":3
-   },
-   {
-      "primary_asset_contracts":[
-         {
-            "address":"0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-            "name":"Enjin",
-            "symbol":"",
-            "description":"",
-            "external_link":null,
-            "nft_version":null,
-            "schema_name":"ERC1155",
-            "display_data":{
-
-            },
-            "owner":null,
-            "created_date":"2019-08-02T23:43:14.666153",
-            "asset_contract_type":"semi-fungible"
-         }
-      ],
-      "name":"Age of Rust",
-      "slug":"age-of-rust",
-      "image_url":"https://storage.opensea.io/age-of-rust-1561960816.jpg",
-      "description":"Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
-      "external_url":"https://www.ageofrust.games/",
-      "featured_image_url":null,
-      "created_date":"2019-09-03T02:35:56.063685",
-      "owned_asset_count":1
-   },
-   {
-      "primary_asset_contracts":[
-         {
-            "address":"0xee85966b4974d3c6f71a2779cc3b6f53afbc2b68",
-            "asset_contract_type":"fungible",
-            "created_date":"2019-10-16T07:36:16.102163",
-            "name":"Rare Chest",
-            "nft_version":null,
-            "opensea_version":null,
-            "owner":1610615,
-            "schema_name":"ERC20",
-            "symbol":"",
-            "total_supply":"1",
-            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-            "default_to_fiat":false,
-            "dev_buyer_fee_basis_points":0,
-            "dev_seller_fee_basis_points":0,
-            "only_proxied_transfers":false,
-            "opensea_buyer_fee_basis_points":0,
-            "opensea_seller_fee_basis_points":250,
-            "buyer_fee_basis_points":0,
-            "seller_fee_basis_points":250,
-            "payout_address":null
-         },
-         {
-            "address":"0x20d4cec36528e1c4563c1bfbe3de06aba70b22b4",
-            "asset_contract_type":"fungible",
-            "created_date":"2019-10-16T08:06:42.727997",
-            "name":"Legendary Chest",
-            "nft_version":null,
-            "opensea_version":null,
-            "owner":1610615,
-            "schema_name":"ERC20",
-            "symbol":"",
-            "total_supply":"1",
-            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-            "default_to_fiat":false,
-            "dev_buyer_fee_basis_points":0,
-            "dev_seller_fee_basis_points":0,
-            "only_proxied_transfers":false,
-            "opensea_buyer_fee_basis_points":0,
-            "opensea_seller_fee_basis_points":250,
-            "buyer_fee_basis_points":0,
-            "seller_fee_basis_points":250,
-            "payout_address":null
-         },
-         {
-            "address":"0x0e3a2a1f2146d86a604adc220b4967a898d7fe07",
-            "asset_contract_type":"non-fungible",
-            "created_date":"2019-11-01T06:39:04.363034",
-            "name":"Gods Unchained Cards",
-            "nft_version":"3.0",
-            "opensea_version":null,
-            "owner":1691695,
-            "schema_name":"ERC721",
-            "symbol":"",
-            "total_supply":"1",
-            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-            "default_to_fiat":false,
-            "dev_buyer_fee_basis_points":0,
-            "dev_seller_fee_basis_points":0,
-            "only_proxied_transfers":false,
-            "opensea_buyer_fee_basis_points":0,
-            "opensea_seller_fee_basis_points":250,
-            "buyer_fee_basis_points":0,
-            "seller_fee_basis_points":250,
-            "payout_address":null
-         },
-         {
-            "address":"0x564cb55c655f727b61d9baf258b547ca04e9e548",
-            "asset_contract_type":"non-fungible",
-            "created_date":"2019-10-29T12:28:37.643714",
-            "name":"Gods Unchained",
-            "nft_version":"3.0",
-            "opensea_version":null,
-            "owner":1691695,
-            "schema_name":"ERC721",
-            "symbol":"",
-            "total_supply":"205",
-            "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-            "external_link":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-            "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-            "default_to_fiat":false,
-            "dev_buyer_fee_basis_points":0,
-            "dev_seller_fee_basis_points":0,
-            "only_proxied_transfers":false,
-            "opensea_buyer_fee_basis_points":0,
-            "opensea_seller_fee_basis_points":250,
-            "buyer_fee_basis_points":0,
-            "seller_fee_basis_points":250,
-            "payout_address":null
-         }
-      ],
-      "traits":{
-         "mana":{
-            "min":1,
-            "max":18
-         },
-         "health":{
-            "min":1,
-            "max":16
-         },
-         "attack":{
-            "min":1,
-            "max":16
-         }
-      },
-      "stats":{
-         "seven_day_volume":270.564697067863,
-         "seven_day_change":-0.014179906699531201,
-         "total_volume":11825.0384171324,
-         "count":6835331,
-         "num_owners":10791,
-         "market_cap":150892.14138332763,
-         "average_price":0.0396514694030496,
-         "items_sold":298233
-      },
-      "banner_image_url":null,
-      "chat_url":null,
-      "created_date":"2019-11-13T03:01:42.051246",
-      "default_to_fiat":false,
-      "description":"Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-      "dev_buyer_fee_basis_points":"0",
-      "dev_seller_fee_basis_points":"0",
-      "display_data":{
-         "images":[
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/25233.png",
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/152875.png",
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/25669.png",
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9237.png",
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9228.png",
-            "https://storage.googleapis.com/opensea-prod.appspot.com/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab/9231.png"
-         ],
-         "card_display_style":"contain"
-      },
-      "external_url":"https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-      "featured":true,
-      "featured_image_url":"https://storage.opensea.io/0x6ebeaf8e8e946f0716e6533a6f2cefc83f60e8ab-featured-1556589419.png",
-      "hidden":false,
-      "image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-      "is_subject_to_whitelist":false,
-      "large_image_url":"https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ",
-      "name":"Gods Unchained",
-      "only_proxied_transfers":false,
-      "opensea_buyer_fee_basis_points":"0",
-      "opensea_seller_fee_basis_points":"250",
-      "payout_address":null,
-      "require_email":false,
-      "short_description":null,
-      "slug":"gods-unchained",
-      "wiki_url":null,
-      "owned_asset_count":535
-   }
-]
-`
-
-var collection1DstV4 = blockatlas.Collection{
-	Name:         "CryptoKitties",
-	Symbol:       "",
-	Slug:         "cryptokitties",
-	ImageUrl:     "https://storage.opensea.io/0x06012c8cf97bead5deae237070f9587f8e7a266d-featured-1556588705.png",
-	Description:  "CryptoKitties is a game centered around breedable, collectible, and oh-so-adorable creatures we call CryptoKitties! Each cat is one-of-a-kind and 100% owned by you; it cannot be replicated, taken away, or destroyed.",
-	ExternalLink: "https://www.cryptokitties.co/",
-	Total:        3,
-	Id:           "cryptokitties",
-	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	Coin:         60,
-}
-
-var collection2DstV4 = blockatlas.Collection{
-	Name:         "Age of Rust",
-	Symbol:       "",
-	Slug:         "age-of-rust",
-	ImageUrl:     "https://storage.opensea.io/age-of-rust-1561960816.jpg",
-	Description:  "Year 4424: The search begins for new life on the other side of the galaxy. Explore abandoned space stations, mysterious caverns, and ruins on far away worlds in order to unlock puzzles and secrets! Beware the rogue machines!",
-	ExternalLink: "https://www.ageofrust.games/",
-	Total:        1,
-	Id:           "age-of-rust",
-	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	Coin:         60,
-}
-
-var collection3DstV4 = blockatlas.Collection{
-	Name:         "Gods Unchained",
-	Symbol:       "",
-	Slug:         "gods-unchained",
-	ImageUrl:     "https://lh3.googleusercontent.com/yArciVdcDv3O2R-O8XCxx3YEYZdzpiCMdossjUgv0kpLIluUQ1bYN_dyEk5xcvBEOgeq0zNIoWOh7TL9DvUEv--OLQ=s60",
-	Description:  "Gods Unchained is a free-to-play, turn-based competitive trading card game in which cards can be bought and sold on the OpenSea marketplace. Players use their collection to build decks of cards, and select a God to play with at the start of each match. The goal of the game is to reduce your opponent's life to zero. Each deck contains exactly 30 cards. On OpenSea, cards can be sold for a fixed price, auctioned, or sold in bundles.",
-	ExternalLink: "https://godsunchained.com/?refcode=0x5b3256965e7C3cF26E11FCAf296DfC8807C01073",
-	Total:        535,
-	Id:           "gods-unchained",
-	Address:      "0x0875BCab22dE3d02402bc38aEe4104e1239374a7",
-	Coin:         60,
-}
-
-func TestNormalizeCollectionV4(t *testing.T) {
-	var collections []Collection
-	err := json.Unmarshal([]byte(collectionsSrcV4), &collections)
-	assert.Nil(t, err)
-	page := NormalizeCollectionsV4(collections, coin.ETH, collectionsOwnerV4)
-	assert.Equal(t, 3, len(page), "collections could not be normalized")
-	expected := blockatlas.CollectionPage{collection1DstV4, collection2DstV4, collection3DstV4}
-	assert.Equal(t, page, expected, "collections don't equal")
-}
-
-const collectibleSrcV4 = `
-[
-  {
-    "token_id": "54277541829991970107421667568590323026590803461896874578610080514640537714688",
-    "image_url": "https://storage.opensea.io/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688-1564858806.png",
-    "image_preview_url": "https://storage.opensea.io/0xfaafdc07907ff5120a76b34b731b278c38d6043c-preview/54277541829991970107421667568590323026590803461896874578610080514640537714688-1564858807.png",
-    "name": "Rustbits",
-    "description": "Rustbits are the main token of use within the Age of Rust game universe. You need Rustbits to not only play Age of Rust, but also to purchase in-game cryptoitems as well. Rustbits are radioactive rust scraped off of hulls of abandoned ships that are in orbit around a hidden planet, which is also a gas giant. The planet is so radioactive, it damages ships and kills anyone that gets close to it. Getting bits of rust off of ships is highly rare and prized.",
-    "external_link": "",
-    "asset_contract": {
-      "address": "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-      "name": "Enjin",
-      "external_link": null,
-      "nft_version": null,
-      "schema_name": "ERC1155",
-	  "display_data": {}
-    },
-	"collection": {
-		"slug": "age-of-rust",
-        "name": "Age of Rust",
-        "external_url": "https://opensea.io/"
-    },
-    "permalink": "https://opensea.io/assets/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688"
-  }
-]
-`
-
-var collectibleDstV4 = blockatlas.Collectible{
-	ID:               "0xfaafdc07907ff5120a76b34b731b278c38d6043c-54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	CollectionID:     "age-of-rust",
-	TokenID:          "54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	CategoryContract: "",
-	ContractAddress:  "0xfaafdc07907ff5120a76b34b731b278c38d6043c",
-	Category:         "Age of Rust",
-	ImageUrl:         "https://storage.opensea.io/0xfaafdc07907ff5120a76b34b731b278c38d6043c-preview/54277541829991970107421667568590323026590803461896874578610080514640537714688-1564858807.png",
-	ExternalLink:     "https://opensea.io/",
-	ProviderLink:     "https://opensea.io/assets/0xfaafdc07907ff5120a76b34b731b278c38d6043c/54277541829991970107421667568590323026590803461896874578610080514640537714688",
-	Type:             "ERC1155",
-	Description:      "Rustbits are the main token of use within the Age of Rust game universe. You need Rustbits to not only play Age of Rust, but also to purchase in-game cryptoitems as well. Rustbits are radioactive rust scraped off of hulls of abandoned ships that are in orbit around a hidden planet, which is also a gas giant. The planet is so radioactive, it damages ships and kills anyone that gets close to it. Getting bits of rust off of ships is highly rare and prized.",
-	Coin:             60,
-	Name:             "Rustbits",
-}
-
-func TestNormalizeCollectibleV4(t *testing.T) {
-	var collectibles []Collectible
-	err := json.Unmarshal([]byte(collectibleSrcV4), &collectibles)
-	assert.Nil(t, err)
-	page := NormalizeCollectiblePageV4(collectibles, coin.ETH)
-	assert.Equal(t, len(page), 1, "collectible could not be normalized")
-	expected := blockatlas.CollectiblePage{collectibleDstV4}
-	assert.Equal(t, page, expected, "collectible don't equal")
-}
diff --git a/tests/postman/Blockatlas.postman_collection.json b/tests/postman/Blockatlas.postman_collection.json
index 4386043..884eb88 100644
--- a/tests/postman/Blockatlas.postman_collection.json
+++ b/tests/postman/Blockatlas.postman_collection.json
@@ -1,6 +1,6 @@
 {
 	"info": {
-		"_postman_id": "60e7e382-2f40-487f-a291-3e62daadadd8",
+		"_postman_id": "7c16faaa-629e-4a75-a8bc-2ccaacb540ff",
 		"name": "Blockatlas",
 		"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
 	},
@@ -984,7 +984,7 @@
 					"name": "collection",
 					"item": [
 						{
-							"name": "collection",
+							"name": "collection v4",
 							"event": [
 								{
 									"listen": "prerequest",
@@ -1025,9 +1025,6 @@
 											"          \"token_id\": {",
 											"            \"type\": \"string\"",
 											"          },",
-											"          \"category_contract\": {",
-											"            \"type\": \"string\"",
-											"          },",
 											"          \"contract_address\": {",
 											"            \"type\": \"string\"",
 											"          },",
@@ -1054,134 +1051,8 @@
 											"          },",
 											"          \"name\": {",
 											"            \"type\": \"string\"",
-											"          }",
-											"        }",
-											"      }",
-											"    },",
-											"    \"status\": {",
-											"      \"type\": \"boolean\"",
-											"    }",
-											"  }",
-											"};",
-											"",
-											"let handler = pm.variables.get(\"handler\");",
-											"let address = pm.variables.get(\"address\");",
-											"var jsonData = pm.response.json();",
-											"",
-											"pm.test(handler + \" - response must be valid and have a body: \" + address, function () {",
-											"    pm.response.to.have.status(200);",
-											"    pm.response.to.be.ok;",
-											"    pm.response.to.not.be.error;",
-											"    pm.response.to.be.withBody;",
-											"    pm.response.to.be.json;",
-											"});",
-											"",
-											"pm.test(handler + \" - schema is valid: \" + address, function() {",
-											"    pm.expect(ajv.validate(schema, jsonData)).to.be.true;",
-											"    console.log(JSON.stringify(jsonData))",
-											"});",
-											""
-										],
-										"type": "text/javascript"
-									}
-								}
-							],
-							"request": {
-								"method": "GET",
-								"header": [
-									{
-										"key": "Authorization",
-										"value": "Bearer {{platform_auth}}",
-										"type": "text"
-									}
-								],
-								"url": {
-									"raw": "{{host}}/v2/{{handler}}/collections/{{address}}/collection/{{collection}}",
-									"host": [
-										"{{host}}"
-									],
-									"path": [
-										"v2",
-										"{{handler}}",
-										"collections",
-										"{{address}}",
-										"collection",
-										"{{collection}}"
-									]
-								}
-							},
-							"response": []
-						},
-						{
-							"name": "collection v4",
-							"event": [
-								{
-									"listen": "prerequest",
-									"script": {
-										"id": "7312c9e7-5330-421a-9ca8-171101050e38",
-										"exec": [
-											""
-										],
-										"type": "text/javascript"
-									}
-								},
-								{
-									"listen": "test",
-									"script": {
-										"id": "acec8baa-0ef0-4703-ab83-c555260deffa",
-										"exec": [
-											"var Ajv = require('ajv');",
-											"var ajv = new Ajv({logger: console});",
-											"let schema = {",
-											"  \"type\": \"object\",",
-											"  \"properties\": {",
-											"    \"total\": {",
-											"      \"type\": \"integer\"",
-											"    },",
-											"    \"docs\": {",
-											"      \"type\": \"array\",",
-											"      \"minItems\": 1,",
-											"      \"uniqueItems\": true,",
-											"      \"items\": {",
-											"        \"type\": \"object\",",
-											"        \"properties\": {",
-											"          \"id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"collection_id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"token_id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"category_contract\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"contract_address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"category\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"image_url\": {",
-											"            \"type\": \"string\"",
 											"          },",
-											"          \"external_link\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"provider_link\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"type\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"description\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"coin\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"name\": {",
+											"          \"nft_version\": {",
 											"            \"type\": \"string\"",
 											"          }",
 											"        }",
@@ -1225,12 +1096,12 @@
 									}
 								],
 								"url": {
-									"raw": "{{host}}/v2/{{handler}}/collections/{{address}}/collection/{{collection}}",
+									"raw": "{{host}}/v4/{{handler}}/collections/{{address}}/collection/{{collection}}",
 									"host": [
 										"{{host}}"
 									],
 									"path": [
-										"v2",
+										"v4",
 										"{{handler}}",
 										"collections",
 										"{{address}}",
@@ -1242,7 +1113,7 @@
 							"response": []
 						},
 						{
-							"name": "collections",
+							"name": "collections batch v4",
 							"event": [
 								{
 									"listen": "prerequest",
@@ -1280,12 +1151,6 @@
 											"          \"name\": {",
 											"            \"type\": \"string\"",
 											"          },",
-											"          \"symbol\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"slug\": {",
-											"            \"type\": \"string\"",
-											"          },",
 											"          \"image_url\": {",
 											"            \"type\": \"string\"",
 											"          },",
@@ -1295,415 +1160,9 @@
 											"          \"total\": {",
 											"            \"type\": \"integer\"",
 											"          },",
-											"          \"category_address\": {",
-											"            \"type\": \"string\"",
-											"          },",
 											"          \"address\": {",
 											"            \"type\": \"string\"",
 											"          },",
-											"          \"nft_version\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"coin\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"type\": {",
-											"            \"type\": \"string\"",
-											"          }",
-											"        }",
-											"      }",
-											"    },",
-											"    \"status\": {",
-											"      \"type\": \"boolean\"",
-											"    }",
-											"  }",
-											"};",
-											"",
-											"let handler = pm.variables.get(\"handler\");",
-											"let address = pm.variables.get(\"address\");",
-											"var jsonData = pm.response.json();",
-											"",
-											"pm.test(handler + \" - response must be valid and have a body: \" + address, function () {",
-											"    pm.response.to.have.status(200);",
-											"    pm.response.to.be.ok;",
-											"    pm.response.to.not.be.error;",
-											"    pm.response.to.be.withBody;",
-											"    pm.response.to.be.json;",
-											"});",
-											"",
-											"pm.test(handler + \" - schema is valid: \" + address, function() {",
-											"    pm.expect(ajv.validate(schema, jsonData)).to.be.true;",
-											"    console.log(JSON.stringify(jsonData))",
-											"});",
-											""
-										],
-										"type": "text/javascript"
-									}
-								}
-							],
-							"request": {
-								"method": "GET",
-								"header": [
-									{
-										"key": "Authorization",
-										"value": "Bearer {{platform_auth}}",
-										"type": "text"
-									}
-								],
-								"url": {
-									"raw": "{{host}}/v2/{{handler}}/collections/{{address}}",
-									"host": [
-										"{{host}}"
-									],
-									"path": [
-										"v2",
-										"{{handler}}",
-										"collections",
-										"{{address}}"
-									]
-								}
-							},
-							"response": []
-						},
-						{
-							"name": "collections batch",
-							"event": [
-								{
-									"listen": "prerequest",
-									"script": {
-										"id": "7312c9e7-5330-421a-9ca8-171101050e38",
-										"exec": [
-											""
-										],
-										"type": "text/javascript"
-									}
-								},
-								{
-									"listen": "test",
-									"script": {
-										"id": "acec8baa-0ef0-4703-ab83-c555260deffa",
-										"exec": [
-											"var Ajv = require('ajv');",
-											"var ajv = new Ajv({logger: console});",
-											"let schema = {",
-											"  \"type\": \"object\",",
-											"  \"properties\": {",
-											"    \"total\": {",
-											"      \"type\": \"integer\"",
-											"    },",
-											"    \"docs\": {",
-											"      \"type\": \"array\",",
-											"      \"minItems\": 1,",
-											"      \"uniqueItems\": true,",
-											"      \"items\": {",
-											"        \"type\": \"object\",",
-											"        \"properties\": {",
-											"          \"id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"name\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"symbol\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"slug\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"image_url\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"external_link\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"total\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"category_address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"nft_version\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"coin\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"type\": {",
-											"            \"type\": \"string\"",
-											"          }",
-											"        }",
-											"      }",
-											"    },",
-											"    \"status\": {",
-											"      \"type\": \"boolean\"",
-											"    }",
-											"  }",
-											"};",
-											"",
-											"let handler = pm.variables.get(\"handler\");",
-											"let address = pm.variables.get(\"address\");",
-											"var jsonData = pm.response.json();",
-											"",
-											"pm.test(handler + \" - response must be valid and have a body: \" + address, function () {",
-											"    pm.response.to.have.status(200);",
-											"    pm.response.to.be.ok;",
-											"    pm.response.to.not.be.error;",
-											"    pm.response.to.be.withBody;",
-											"    pm.response.to.be.json;",
-											"});",
-											"",
-											"pm.test(handler + \" - schema is valid: \" + address, function() {",
-											"    pm.expect(ajv.validate(schema, jsonData)).to.be.true;",
-											"    console.log(JSON.stringify(jsonData))",
-											"});",
-											""
-										],
-										"type": "text/javascript"
-									}
-								}
-							],
-							"request": {
-								"method": "POST",
-								"header": [
-									{
-										"key": "Authorization",
-										"value": "Bearer {{platform_auth}}",
-										"type": "text"
-									},
-									{
-										"key": "Content-Type",
-										"name": "Content-Type",
-										"value": "application/json",
-										"type": "text"
-									}
-								],
-								"body": {
-									"mode": "raw",
-									"raw": "{\n\t\"{{coin}}\": [\"{{address}}\"]\n}",
-									"options": {
-										"raw": {
-											"language": "json"
-										}
-									}
-								},
-								"url": {
-									"raw": "{{host}}/v2/collectibles/categories",
-									"host": [
-										"{{host}}"
-									],
-									"path": [
-										"v2",
-										"collectibles",
-										"categories"
-									]
-								}
-							},
-							"response": []
-						},
-						{
-							"name": "collections batch v3 ",
-							"event": [
-								{
-									"listen": "prerequest",
-									"script": {
-										"id": "7312c9e7-5330-421a-9ca8-171101050e38",
-										"exec": [
-											""
-										],
-										"type": "text/javascript"
-									}
-								},
-								{
-									"listen": "test",
-									"script": {
-										"id": "acec8baa-0ef0-4703-ab83-c555260deffa",
-										"exec": [
-											"var Ajv = require('ajv');",
-											"var ajv = new Ajv({logger: console});",
-											"let schema = {",
-											"  \"type\": \"object\",",
-											"  \"properties\": {",
-											"    \"total\": {",
-											"      \"type\": \"integer\"",
-											"    },",
-											"    \"docs\": {",
-											"      \"type\": \"array\",",
-											"      \"minItems\": 1,",
-											"      \"uniqueItems\": true,",
-											"      \"items\": {",
-											"        \"type\": \"object\",",
-											"        \"properties\": {",
-											"          \"id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"name\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"symbol\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"slug\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"image_url\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"external_link\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"total\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"category_address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"nft_version\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"coin\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"type\": {",
-											"            \"type\": \"string\"",
-											"          }",
-											"        }",
-											"      }",
-											"    },",
-											"    \"status\": {",
-											"      \"type\": \"boolean\"",
-											"    }",
-											"  }",
-											"};",
-											"",
-											"let handler = pm.variables.get(\"handler\");",
-											"let address = pm.variables.get(\"address\");",
-											"var jsonData = pm.response.json();",
-											"",
-											"pm.test(handler + \" - response must be valid and have a body: \" + address, function () {",
-											"    pm.response.to.have.status(200);",
-											"    pm.response.to.be.ok;",
-											"    pm.response.to.not.be.error;",
-											"    pm.response.to.be.withBody;",
-											"    pm.response.to.be.json;",
-											"});",
-											"",
-											"pm.test(handler + \" - schema is valid: \" + address, function() {",
-											"    pm.expect(ajv.validate(schema, jsonData)).to.be.true;",
-											"    console.log(JSON.stringify(jsonData))",
-											"});",
-											""
-										],
-										"type": "text/javascript"
-									}
-								}
-							],
-							"request": {
-								"method": "POST",
-								"header": [
-									{
-										"key": "Authorization",
-										"value": "Bearer {{platform_auth}}",
-										"type": "text"
-									},
-									{
-										"key": "Content-Type",
-										"name": "Content-Type",
-										"value": "application/json",
-										"type": "text"
-									}
-								],
-								"body": {
-									"mode": "raw",
-									"raw": "{\n\t\"{{coin}}\": [\"{{address}}\"]\n}",
-									"options": {
-										"raw": {
-											"language": "json"
-										}
-									}
-								},
-								"url": {
-									"raw": "{{host}}/v3/collectibles/categories",
-									"host": [
-										"{{host}}"
-									],
-									"path": [
-										"v3",
-										"collectibles",
-										"categories"
-									]
-								}
-							},
-							"response": []
-						},
-						{
-							"name": "collections batch v4",
-							"event": [
-								{
-									"listen": "prerequest",
-									"script": {
-										"id": "7312c9e7-5330-421a-9ca8-171101050e38",
-										"exec": [
-											""
-										],
-										"type": "text/javascript"
-									}
-								},
-								{
-									"listen": "test",
-									"script": {
-										"id": "acec8baa-0ef0-4703-ab83-c555260deffa",
-										"exec": [
-											"var Ajv = require('ajv');",
-											"var ajv = new Ajv({logger: console});",
-											"let schema = {",
-											"  \"type\": \"object\",",
-											"  \"properties\": {",
-											"    \"total\": {",
-											"      \"type\": \"integer\"",
-											"    },",
-											"    \"docs\": {",
-											"      \"type\": \"array\",",
-											"      \"minItems\": 1,",
-											"      \"uniqueItems\": true,",
-											"      \"items\": {",
-											"        \"type\": \"object\",",
-											"        \"properties\": {",
-											"          \"id\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"name\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"symbol\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"slug\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"image_url\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"external_link\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"total\": {",
-											"            \"type\": \"integer\"",
-											"          },",
-											"          \"category_address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"address\": {",
-											"            \"type\": \"string\"",
-											"          },",
-											"          \"nft_version\": {",
-											"            \"type\": \"string\"",
-											"          },",
 											"          \"coin\": {",
 											"            \"type\": \"integer\"",
 											"          },",
@@ -1766,12 +1225,12 @@
 									}
 								},
 								"url": {
-									"raw": "{{host}}/v3/collectibles/categories",
+									"raw": "{{host}}/v4/collectibles/categories",
 									"host": [
 										"{{host}}"
 									],
 									"path": [
-										"v3",
+										"v4",
 										"collectibles",
 										"categories"
 									]
@@ -2587,29 +2046,29 @@
 	],
 	"variable": [
 		{
-			"id": "ad2326b7-fb90-4b11-a1e3-dfe8ea3aae65",
+			"id": "c6d3b134-d68f-4747-b5c5-a687412f2166",
 			"key": "host",
 			"value": "http://localhost:8420",
 			"type": "string"
 		},
 		{
-			"id": "7ae1014f-44e1-4c16-aacd-2be971ee15ef",
+			"id": "cfde5bdb-eecf-472c-b1bf-d944a63befff",
 			"key": "observer_auth",
 			"value": "test",
 			"type": "string"
 		},
 		{
-			"id": "0d3dcacb-5b63-409a-8150-9e06d24e1eb4",
+			"id": "1455449b-6372-4c16-81d9-ed93ab4aafd7",
 			"key": "market_auth",
 			"value": "",
 			"type": "string"
 		},
 		{
-			"id": "c42f8723-ad35-45ea-9dd2-066ced0780bc",
+			"id": "4180368d-8afe-4432-8ecb-48db2f778a72",
 			"key": "platform_auth",
 			"value": "",
 			"type": "string"
 		}
 	],
 	"protocolProfileBehavior": {}
-}
+}
\ No newline at end of file
diff --git a/tests/postman/collection_data.json b/tests/postman/collection_data.json
index 0213e2d..4e09b01 100644
--- a/tests/postman/collection_data.json
+++ b/tests/postman/collection_data.json
@@ -2,7 +2,7 @@
     {
         "coin": 60,
         "handler": "ethereum",
-        "address": "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1",
-        "collection": "0x35b7838dd7507ada69610397a85310ae0abd5034"
+        "address": "0x84E79D544B4b13bC3560069cfD56A9D5bbE7521d",
+        "collection": "unstoppable-domains"
     }
 ]