Repository: trustwallet/blockatlas
Pull request #1393 (2021-01-28 06:37:59)

[Collectible] Return category and add tests

codecov[bot]: # [Codecov](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=h1) Report
> Merging [#1393](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=desc) (189d0aa) into [master](https://codecov.io/gh/trustwallet/blockatlas/commit/5d978d39a8c30352870cd98e5aa03f8829724152?el=desc) (5d978d3) will **increase** coverage by `0%`.
> The diff coverage is `80%`.

[![Impacted file tree graph](https://codecov.io/gh/trustwallet/blockatlas/pull/1393/graphs/tree.svg?width=650&height=150&src=pr&token=jnrjoEQAqv)](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=tree)

```diff
@@          Coverage Diff           @@
##           master   #1393   +/-   ##
======================================
  Coverage      44%     44%           
======================================
  Files         147     147           
  Lines        4251    4256    +5     
======================================
+ Hits         1874    1892   +18     
+ Misses       2207    2195   -12     
+ Partials      170     169    -1     
```


| [Impacted Files](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [platform/ethereum/bounce/collection.go](https://codecov.io/gh/trustwallet/blockatlas/pull/1393/diff?src=pr&el=tree#diff-cGxhdGZvcm0vZXRoZXJldW0vYm91bmNlL2NvbGxlY3Rpb24uZ28=) | `26% <80%> (+26%)` | :arrow_up: |
| [platform/ethereum/bounce/client.go](https://codecov.io/gh/trustwallet/blockatlas/pull/1393/diff?src=pr&el=tree#diff-cGxhdGZvcm0vZXRoZXJldW0vYm91bmNlL2NsaWVudC5nbw==) | `20% <0%> (+5%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=footer). Last update [5d978d3...189d0aa](https://codecov.io/gh/trustwallet/blockatlas/pull/1393?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/go.sum b/go.sum
index 1d2f80d..f9689a0 100644
--- a/go.sum
+++ b/go.sum
@@ -642,13 +642,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/postgres v1.0.6 h1:9sqNcNC9PCkZ6tMzWF1cEE2PARlCONgSqRobszSTffw=
-gorm.io/driver/postgres v1.0.6/go.mod h1:r0nvX27yHDNbVeXMM9Y+9i5xSePcT18RfH8clP6wpwI=
+gorm.io/driver/postgres v1.0.7 h1:uCVjh1w7DSZ20Duo10JadA+1a0OZpgJk/o/z8pFpNQs=
 gorm.io/driver/postgres v1.0.7/go.mod h1:4eOzrI1MUfm6ObJU/UcmbXyiHSs8jSwH95G5P5dxcAg=
-gorm.io/gorm v1.20.8 h1:iToaOdZgjNvlc44NFkxfLa3U9q63qwaxt0FdNCiwOMs=
-gorm.io/gorm v1.20.8/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
-gorm.io/gorm v1.20.11 h1:jYHQ0LLUViV85V8dM1TP9VBBkfzKTnuTXDjYObkI6yc=
-gorm.io/gorm v1.20.11/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.20.12 h1:ebZ5KrSHzet+sqOCVdH9mTjW91L298nX3v5lVxAzSUY=
 gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
 gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/platform/ethereum/bounce/collection.go b/platform/ethereum/bounce/collection.go
index e03f870..1fd17a6 100644
--- a/platform/ethereum/bounce/collection.go
+++ b/platform/ethereum/bounce/collection.go
@@ -16,7 +16,7 @@ func (c *Client) GetCollections(owner string, coinIndex uint) (types.CollectionP
 	if err != nil {
 		return nil, err
 	}
-	return c.NormalizeCollections(collections, coinIndex, owner)
+	return c.processCollections(collections, coinIndex, owner)
 
 }
 
@@ -25,10 +25,10 @@ func (c *Client) GetCollectibles(owner, collectionID string, coinIndex uint) (ty
 	if err != nil {
 		return nil, err
 	}
-	return c.NormalizeCollectibles(collectibles, coinIndex)
+	return c.processCollectibles(collectibles, coinIndex)
 }
 
-func (c *Client) NormalizeCollections(collections []Collection, coinIndex uint, owner string) (types.CollectionPage, error) {
+func (c *Client) processCollections(collections []Collection, coinIndex uint, owner string) (types.CollectionPage, error) {
 	page := make(types.CollectionPage, 0)
 	category := map[string]bool{}
 	for _, cl := range collections {
@@ -73,7 +73,7 @@ func (c *Client) NormalizeCollections(collections []Collection, coinIndex uint,
 	return page, nil
 }
 
-func (c *Client) NormalizeCollectibles(collectibles []Collectible, coinIndex uint) (types.CollectiblePage, error) {
+func (c *Client) processCollectibles(collectibles []Collectible, coinIndex uint) (types.CollectiblePage, error) {
 	if len(collectibles) == 0 {
 		return types.CollectiblePage{}, nil
 	}
@@ -83,19 +83,29 @@ func (c *Client) NormalizeCollectibles(collectibles []Collectible, coinIndex uin
 		return nil, err
 	}
 	for _, c := range collectibles {
-		page = append(page, types.Collectible{
-			ID:              blockatlas.GenCollectibleId(c.ContractAddr, c.TokenID),
-			CollectionID:    c.ContractAddr,
-			TokenID:         c.TokenID,
-			ContractAddress: c.ContractAddr,
-			ImageUrl:        normalizeUrl(info.Image),
-			ExternalLink:    normalizeUrl(c.TokenURI),
-			Type:            string(types.ERC721),
-			Description:     info.Description,
-			Coin:            coinIndex,
-			Name:            info.Name,
-			Version:         nftVersion,
-		})
+		normalized := normalizeCollectible(c, coinIndex, info)
+		page = append(page, normalized)
 	}
 	return page, nil
 }
+
+func normalizeCollectible(c Collectible, coinIndex uint, info CollectionInfo) types.Collectible {
+	category := c.ContractName
+	if len(category) == 0 {
+		category = info.Name
+	}
+	return types.Collectible{
+		ID:              blockatlas.GenCollectibleId(c.ContractAddr, c.TokenID),
+		CollectionID:    c.ContractAddr,
+		TokenID:         c.TokenID,
+		ContractAddress: c.ContractAddr,
+		Category:        category,
+		ImageUrl:        normalizeUrl(info.Image),
+		ExternalLink:    normalizeUrl(c.TokenURI),
+		Type:            string(types.ERC721),
+		Description:     info.Description,
+		Coin:            coinIndex,
+		Name:            info.Name,
+		Version:         nftVersion,
+	}
+}
diff --git a/platform/ethereum/bounce/mocks/artwork_nft.json b/platform/ethereum/bounce/mocks/artwork_nft.json
new file mode 100644
index 0000000..1cc7521
--- /dev/null
+++ b/platform/ethereum/bounce/mocks/artwork_nft.json
@@ -0,0 +1,22 @@
+{
+  "id": 63039,
+  "contract_addr": "0x5Bc94e9347F3b9Be8415bDfd24af16666704E44f",
+  "contract_name": "",
+  "token_type": "721",
+  "token_id": "450",
+  "owner_addr": "0x7d8bf18C7cE84b3E175b339c4Ca93aEd1dD166F1",
+  "balance": "1",
+  "token_uri": "https://www.bakeryswap.org/api/v1/artworks/fb4576253e3d45cebf0ac4c8df8f1743",
+  "name": null,
+  "description": null,
+  "image": null,
+  "metadata": {
+    "description": "Animal Series",
+    "image": "https://d3ggs2vjn5heyw.cloudfront.net/static/nfts/artworks/d9dc679ec0614eb78b479aed21694305.jpg",
+    "name": "Hungry",
+    "properties": {
+      "artist": "srnArt",
+      "public_profile_link": "https://twitter.com/srn_art?s=09"
+    }
+  }
+}
diff --git a/platform/ethereum/bounce/mocks/pancake_bunny.json b/platform/ethereum/bounce/mocks/pancake_bunny.json
new file mode 100644
index 0000000..decb0f8
--- /dev/null
+++ b/platform/ethereum/bounce/mocks/pancake_bunny.json
@@ -0,0 +1,21 @@
+{
+  "id": 48963,
+  "contract_addr": "0xDf7952B35f24aCF7fC0487D01c8d5690a60DBa07",
+  "contract_name": "Pancake Bunnies",
+  "token_type": "721",
+  "token_id": "409",
+  "owner_addr": "0x7d8bf18c7ce84b3e175b339c4ca93aed1dd166f1",
+  "balance": "1",
+  "token_uri": "ipfs://QmYu9WwPNKNSZQiTCDfRk7aCR472GURavR9M1qosDmqpev/swapsies.json",
+  "name": null,
+  "description": null,
+  "image": null,
+  "metadata": {
+    "attributes": {
+      "bunnyId": "0"
+    },
+    "description": "These bunnies love nothing more than swapping pancakes. Especially on BSC.",
+    "image": "ipfs://QmXdHqg3nywpNJWDevJQPtkz93vpfoHcZWQovFz2nmtPf5/swapsies.png",
+    "name": "Swapsies"
+  }
+}
diff --git a/platform/ethereum/bounce/model.go b/platform/ethereum/bounce/model.go
index 88c9fc1..cbec7cc 100644
--- a/platform/ethereum/bounce/model.go
+++ b/platform/ethereum/bounce/model.go
@@ -7,6 +7,7 @@ type Response struct {
 
 type Collectible struct {
 	ContractAddr string `json:"contract_addr"`
+	ContractName string `json:"contract_name,omitempty"`
 	TokenID      string `json:"token_id"`
 	OwnerAddr    string `json:"owner_addr"`
 	TokenURI     string `json:"token_uri"`


Test patch:
diff --git a/platform/ethereum/bounce/collection_test.go b/platform/ethereum/bounce/collection_test.go
new file mode 100644
index 0000000..a7da964
--- /dev/null
+++ b/platform/ethereum/bounce/collection_test.go
@@ -0,0 +1,87 @@
+package bounce
+
+import (
+	"reflect"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/trustwallet/golibs/coin"
+	"github.com/trustwallet/golibs/mock"
+	"github.com/trustwallet/golibs/types"
+)
+
+func Test_normalizeCollectible(t *testing.T) {
+	type args struct {
+		filename  string
+		coinIndex uint
+		info      CollectionInfo
+	}
+	tests := []struct {
+		name string
+		args args
+		want types.Collectible
+	}{
+		{
+			name: "Test empty contract name",
+			args: args{
+				filename:  "artwork_nft.json",
+				coinIndex: coin.SMARTCHAIN,
+				info: CollectionInfo{
+					Name:        "Hungry",
+					Description: "Animal Series",
+					Image:       "https://d3ggs2vjn5heyw.cloudfront.net/static/nfts/artworks/d9dc679ec0614eb78b479aed21694305.jpg",
+				},
+			},
+			want: types.Collectible{
+				ID:              "0x5Bc94e9347F3b9Be8415bDfd24af16666704E44f-450",
+				CollectionID:    "0x5Bc94e9347F3b9Be8415bDfd24af16666704E44f",
+				TokenID:         "450",
+				ContractAddress: "0x5Bc94e9347F3b9Be8415bDfd24af16666704E44f",
+				Category:        "Hungry",
+				ImageUrl:        "https://d3ggs2vjn5heyw.cloudfront.net/static/nfts/artworks/d9dc679ec0614eb78b479aed21694305.jpg",
+				ExternalLink:    "https://www.bakeryswap.org/api/v1/artworks/fb4576253e3d45cebf0ac4c8df8f1743",
+				Type:            string(types.ERC721),
+				Description:     "Animal Series",
+				Coin:            coin.SMARTCHAIN,
+				Name:            "Hungry",
+				Version:         "3.0",
+			},
+		},
+		{
+			name: "Test pancake bunny",
+			args: args{
+				filename:  "pancake_bunny.json",
+				coinIndex: coin.SMARTCHAIN,
+				info: CollectionInfo{
+					Name:        "Swapsies",
+					Description: "These bunnies love nothing more than swapping pancakes. Especially on BSC.",
+					Image:       "https://ipfs.io/ipfs/QmXdHqg3nywpNJWDevJQPtkz93vpfoHcZWQovFz2nmtPf5/swapsies.png",
+				},
+			},
+			want: types.Collectible{
+				ID:              "0xDf7952B35f24aCF7fC0487D01c8d5690a60DBa07-409",
+				CollectionID:    "0xDf7952B35f24aCF7fC0487D01c8d5690a60DBa07",
+				TokenID:         "409",
+				ContractAddress: "0xDf7952B35f24aCF7fC0487D01c8d5690a60DBa07",
+				Category:        "Pancake Bunnies",
+				ImageUrl:        "https://ipfs.io/ipfs/QmXdHqg3nywpNJWDevJQPtkz93vpfoHcZWQovFz2nmtPf5/swapsies.png",
+				ExternalLink:    "https://ipfs.io/ipfs/QmYu9WwPNKNSZQiTCDfRk7aCR472GURavR9M1qosDmqpev/swapsies.json",
+				Type:            string(types.ERC721),
+				Description:     "These bunnies love nothing more than swapping pancakes. Especially on BSC.",
+				Coin:            coin.SMARTCHAIN,
+				Name:            "Swapsies",
+				Version:         "3.0",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var c Collectible
+			err := mock.JsonModelFromFilePath("mocks/"+tt.args.filename, &c)
+			assert.Nil(t, err)
+			if got := normalizeCollectible(c, tt.args.coinIndex, tt.args.info); !reflect.DeepEqual(got, tt.want) {
+				t.Errorf("normalizeCollectible() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}