github.com/status-im/status-go@v1.1.0/services/wallet/thirdparty/alchemy/types.go (about) 1 package alchemy 2 3 import ( 4 "encoding/json" 5 "strconv" 6 "strings" 7 8 "github.com/ethereum/go-ethereum/common" 9 10 "github.com/status-im/status-go/services/wallet/bigint" 11 walletCommon "github.com/status-im/status-go/services/wallet/common" 12 "github.com/status-im/status-go/services/wallet/thirdparty" 13 14 "golang.org/x/text/cases" 15 "golang.org/x/text/language" 16 ) 17 18 const AlchemyID = "alchemy" 19 20 type TokenBalance struct { 21 TokenID *bigint.BigInt `json:"tokenId"` 22 Balance *bigint.BigInt `json:"balance"` 23 } 24 25 type CollectibleOwner struct { 26 OwnerAddress common.Address `json:"ownerAddress"` 27 TokenBalances []TokenBalance `json:"tokenBalances"` 28 } 29 30 type CollectibleContractOwnership struct { 31 Owners []CollectibleOwner `json:"owners"` 32 PageKey string `json:"pageKey"` 33 } 34 35 func alchemyCollectibleOwnersToCommon(alchemyOwners []CollectibleOwner) []thirdparty.CollectibleOwner { 36 owners := make([]thirdparty.CollectibleOwner, 0, len(alchemyOwners)) 37 for _, alchemyOwner := range alchemyOwners { 38 balances := make([]thirdparty.TokenBalance, 0, len(alchemyOwner.TokenBalances)) 39 40 for _, alchemyBalance := range alchemyOwner.TokenBalances { 41 balances = append(balances, thirdparty.TokenBalance{ 42 TokenID: &bigint.BigInt{Int: alchemyBalance.TokenID.Int}, 43 Balance: alchemyBalance.Balance, 44 }) 45 } 46 owner := thirdparty.CollectibleOwner{ 47 OwnerAddress: alchemyOwner.OwnerAddress, 48 TokenBalances: balances, 49 } 50 51 owners = append(owners, owner) 52 } 53 return owners 54 } 55 56 type AttributeValue string 57 58 func (st *AttributeValue) UnmarshalJSON(b []byte) error { 59 var item interface{} 60 if err := json.Unmarshal(b, &item); err != nil { 61 return err 62 } 63 64 switch v := item.(type) { 65 case float64: 66 *st = AttributeValue(strconv.FormatFloat(v, 'f', 2, 64)) 67 case int: 68 *st = AttributeValue(strconv.Itoa(v)) 69 case string: 70 *st = AttributeValue(v) 71 } 72 return nil 73 } 74 75 type Attribute struct { 76 TraitType string `json:"trait_type"` 77 Value AttributeValue `json:"value"` 78 } 79 80 type RawMetadata struct { 81 Attributes []Attribute `json:"attributes"` 82 } 83 84 type RawFull struct { 85 RawMetadata RawMetadata `json:"metadata"` 86 } 87 88 type Raw struct { 89 RawMetadata interface{} `json:"metadata"` 90 } 91 92 func (r *Raw) UnmarshalJSON(b []byte) error { 93 raw := RawFull{ 94 RawMetadata{ 95 Attributes: make([]Attribute, 0), 96 }, 97 } 98 99 // Field structure is not known in advance 100 _ = json.Unmarshal(b, &raw) 101 102 r.RawMetadata = raw.RawMetadata 103 return nil 104 } 105 106 type OpenSeaMetadata struct { 107 ImageURL string `json:"imageUrl"` 108 TwitterUsername string `json:"twitterUsername"` 109 ExternalURL string `json:"externalUrl"` 110 } 111 112 type Contract struct { 113 Address common.Address `json:"address"` 114 Name string `json:"name"` 115 Symbol string `json:"symbol"` 116 TokenType string `json:"tokenType"` 117 OpenSeaMetadata OpenSeaMetadata `json:"openseaMetadata"` 118 } 119 120 type ContractList struct { 121 Contracts []Contract `json:"contracts"` 122 } 123 124 type Image struct { 125 ImageURL string `json:"pngUrl"` 126 CachedAnimationURL string `json:"cachedUrl"` 127 OriginalAnimationURL string `json:"originalUrl"` 128 } 129 130 type Asset struct { 131 Contract Contract `json:"contract"` 132 TokenID *bigint.BigInt `json:"tokenId"` 133 Name string `json:"name"` 134 Description string `json:"description"` 135 Image Image `json:"image"` 136 Raw Raw `json:"raw"` 137 TokenURI string `json:"tokenUri"` 138 Balance *bigint.BigInt `json:"balance,omitempty"` 139 } 140 141 type OwnedNFTList struct { 142 OwnedNFTs []Asset `json:"ownedNfts"` 143 TotalCount *bigint.BigInt `json:"totalCount"` 144 PageKey string `json:"pageKey"` 145 } 146 147 type NFTList struct { 148 NFTs []Asset `json:"nfts"` 149 } 150 151 type BatchContractAddresses struct { 152 Addresses []common.Address `json:"contractAddresses"` 153 } 154 155 type BatchTokenIDs struct { 156 IDs []TokenID `json:"tokens"` 157 } 158 159 type TokenID struct { 160 ContractAddress common.Address `json:"contractAddress"` 161 TokenID *bigint.BigInt `json:"tokenId"` 162 } 163 164 func alchemyToCollectibleTraits(attributes []Attribute) []thirdparty.CollectibleTrait { 165 ret := make([]thirdparty.CollectibleTrait, 0, len(attributes)) 166 caser := cases.Title(language.Und, cases.NoLower) 167 for _, orig := range attributes { 168 dest := thirdparty.CollectibleTrait{ 169 TraitType: strings.Replace(orig.TraitType, "_", " ", 1), 170 Value: caser.String(string(orig.Value)), 171 } 172 173 ret = append(ret, dest) 174 } 175 return ret 176 } 177 178 func alchemyToContractType(tokenType string) walletCommon.ContractType { 179 switch tokenType { 180 case "ERC721": 181 return walletCommon.ContractTypeERC721 182 case "ERC1155": 183 return walletCommon.ContractTypeERC1155 184 default: 185 return walletCommon.ContractTypeUnknown 186 } 187 } 188 189 func (c *Contract) toCollectionSocials() *thirdparty.CollectionSocials { 190 return &thirdparty.CollectionSocials{ 191 Website: c.OpenSeaMetadata.ExternalURL, 192 TwitterHandle: c.OpenSeaMetadata.TwitterUsername, 193 Provider: AlchemyID, 194 } 195 } 196 197 func (c *Contract) toCollectionData(id thirdparty.ContractID) thirdparty.CollectionData { 198 ret := thirdparty.CollectionData{ 199 ID: id, 200 ContractType: alchemyToContractType(c.TokenType), 201 Provider: AlchemyID, 202 Name: c.Name, 203 ImageURL: c.OpenSeaMetadata.ImageURL, 204 Traits: make(map[string]thirdparty.CollectionTrait, 0), 205 Socials: c.toCollectionSocials(), 206 } 207 return ret 208 } 209 210 func (c *Asset) toCollectiblesData(id thirdparty.CollectibleUniqueID) thirdparty.CollectibleData { 211 rawMetadata := c.Raw.RawMetadata.(RawMetadata) 212 213 return thirdparty.CollectibleData{ 214 ID: id, 215 ContractType: alchemyToContractType(c.Contract.TokenType), 216 Provider: AlchemyID, 217 Name: c.Name, 218 Description: c.Description, 219 ImageURL: c.Image.ImageURL, 220 AnimationURL: c.Image.CachedAnimationURL, 221 Traits: alchemyToCollectibleTraits(rawMetadata.Attributes), 222 TokenURI: c.TokenURI, 223 } 224 } 225 226 func (c *Asset) toCommon(id thirdparty.CollectibleUniqueID) thirdparty.FullCollectibleData { 227 contractData := c.Contract.toCollectionData(id.ContractID) 228 return thirdparty.FullCollectibleData{ 229 CollectibleData: c.toCollectiblesData(id), 230 CollectionData: &contractData, 231 AccountBalance: c.Balance, 232 } 233 } 234 235 func alchemyToCollectiblesData(chainID walletCommon.ChainID, l []Asset) []thirdparty.FullCollectibleData { 236 ret := make([]thirdparty.FullCollectibleData, 0, len(l)) 237 for _, asset := range l { 238 id := thirdparty.CollectibleUniqueID{ 239 ContractID: thirdparty.ContractID{ 240 ChainID: chainID, 241 Address: asset.Contract.Address, 242 }, 243 TokenID: asset.TokenID, 244 } 245 item := asset.toCommon(id) 246 ret = append(ret, item) 247 } 248 return ret 249 } 250 251 func alchemyToCollectionsData(chainID walletCommon.ChainID, l []Contract) []thirdparty.CollectionData { 252 ret := make([]thirdparty.CollectionData, 0, len(l)) 253 for _, contract := range l { 254 id := thirdparty.ContractID{ 255 ChainID: chainID, 256 Address: contract.Address, 257 } 258 item := contract.toCollectionData(id) 259 ret = append(ret, item) 260 } 261 return ret 262 }