Add Injective assets (#27191)

* Create logo .png

* Create logo.png

* Create info.json

* Create logo.png

* Create info.json

* Create info.json

* Create logo.png

* Create info.json

* Create logo.png

* Create info.json

* Update info.json

* Update info.json

* Update info.json

* Update info.json

* Update info.json

* Create logo.png

* Delete logo .png

* tokens

* tokens

* tokens

* tokens

* tokens

* tokens

---------

Co-authored-by: Denis <136321897+defisaur@users.noreply.github.com>
This commit is contained in:
Nazar
2024-01-12 15:49:54 +04:00
committed by GitHub
co-authored by Denis
parent f82bade254
commit 2204745e34
26 changed files with 975 additions and 72 deletions
+26 -24
View File
@@ -1,25 +1,27 @@
name: Fix (Dry run) #Disabled, we have 'check' job, 'fix' job do invisible changes which is not convenient to debug
on:
pull_request:
branches: [ master ]
jobs: #name: Fix (Dry run)
fix-dryrun: #on:
runs-on: ubuntu-latest # pull_request:
steps: # branches: [ master ]
- name: Set up Go #
uses: actions/setup-go@v4 #jobs:
with: # fix-dryrun:
go-version: 1.18 # runs-on: ubuntu-latest
# steps:
- name: Check out code # - name: Set up Go
uses: actions/checkout@v3 # uses: actions/setup-go@v4
# with:
- name: Run fix # go-version: 1.18
run: make fix #
# - name: Check out code
- name: Show fix result (diff) # uses: actions/checkout@v3
run: | #
git config core.ignorecase false # - name: Run fix
git status # run: make fix
git diff #
# - name: Show fix result (diff)
# run: |
# git config core.ignorecase false
# git status
# git diff
+47 -45
View File
@@ -1,46 +1,48 @@
name: Fix All #Disabled, we have 'check' job, 'fix' job do invisible changes which is not convenient to debug
on: #name: Fix All
push: #
branches: #on:
- '*' # push:
workflow_dispatch: # branches:
# - '*'
jobs: # workflow_dispatch:
fix-all: #
runs-on: ubuntu-latest #jobs:
# fix-all:
if: github.repository_owner == 'trustwallet' && github.event.repository.fork == false # runs-on: ubuntu-latest
#
steps: # if: github.repository_owner == 'trustwallet' && github.event.repository.fork == false
- name: Checkout (trustwallet repo, secret token) #
uses: actions/checkout@v3 # steps:
with: # - name: Checkout (trustwallet repo, secret token)
# Use trust-ci fine-grained PAT to checkout and later commit the code # uses: actions/checkout@v3
# Do not use for forked repos # with:
token: ${{ secrets.CI_GITHUB_TOKEN }} # # Use trust-ci fine-grained PAT to checkout and later commit the code
ref: ${{ github.ref }} # # Do not use for forked repos
# token: ${{ secrets.CI_GITHUB_TOKEN }}
- name: Set up Go # ref: ${{ github.ref }}
uses: actions/setup-go@v4 #
with: # - name: Set up Go
go-version: 1.18 # uses: actions/setup-go@v4
# with:
- name: Run fix # go-version: 1.18
run: make fix #
# - name: Run fix
- name: Show fix result (diff) # run: make fix
run: | #
git status # - name: Show fix result (diff)
git diff # run: |
# git status
- name: Run check # git diff
run: make check #
# - name: Run check
- name: Commit changes # run: make check
if: success() #
uses: stefanzweifel/git-auto-commit-action@v4.16.0 # - name: Commit changes
with: # if: success()
commit_user_name: trust-ci # uses: stefanzweifel/git-auto-commit-action@v4.16.0
commit_user_email: "117647528+trust-ci@users.noreply.github.com" # with:
commit_message: Auto fix all (sanity and consistency) # commit_user_name: trust-ci
# commit_user_email: "117647528+trust-ci@users.noreply.github.com"
# commit_message: Auto fix all (sanity and consistency)
-1
View File
@@ -192,7 +192,6 @@ linters:
- deadcode - deadcode
- depguard - depguard
- dogsled - dogsled
- dupl
- errcheck - errcheck
- exportloopref - exportloopref
- exhaustive - exhaustive
@@ -0,0 +1,29 @@
{
"name": "Arbitrum",
"type": "INJECTIVE",
"symbol": "ARB",
"decimals": 8,
"website": "https://arbitrum.foundation/",
"description": "$ARB tokens can be used to vote on Arbitrum DAO governance proposals, allowing $ARB holders to collectively shape the future of Arbitrum protocols and chains. Token holders can also delegate their voting power to delegates.",
"explorer": "https://explorer.injective.network/asset/?tokenType=tokenFactory&denom=factory/inj14ejqjyq8um4p3xfqj74yld5waqljf88f9eneuk/inj1d5vz0uzwlpfvgwrwulxg6syy82axa58y4fuszd",
"status": "active",
"id": "inj1d5vz0uzwlpfvgwrwulxg6syy82axa58y4fuszd",
"links": [
{
"name": "twitter",
"url": "https://twitter.com/arbitrum"
},
{
"name": "whitepaper",
"url": "https://docs.arbitrum.foundation/deployment-addresses"
},
{
"name": "discord",
"url": "https://discord.com/arbitrum"
},
{
"name": "coinmarketcap",
"url": "https://coinmarketcap.com/ru/currencies/arbitrum-iou/"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@@ -0,0 +1,21 @@
{
"name": "Talis",
"website": "https://injective.talis.art",
"description": "In the dynamic landscape of NFTs and cryptocurrency, platforms that offer innovative solutions and tools play a crucial role in shaping the ecosystem.",
"explorer": "https://explorer.injective.network/asset/?tokenType=tokenFactory&denom=factory/inj1maeyvxfamtn8lfyxpjca8kuvauuf2qeu6gtxm3/Talis",
"type": "INJECTIVE",
"symbol": "TALIS",
"decimals": 6,
"status": "active",
"id": "inj1maeyvxfamtn8lfyxpjca8kuvauuf2qeu6gtxm3",
"links": [
{
"name": "twitter",
"url": "https://twitter.com/ProtocolTalis"
},
{
"name": "telegram",
"url": "https://t.me/talisprotocol"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

@@ -0,0 +1,40 @@
{
"name": "Chiliz",
"website": "https://chiliz.com",
"description": "Chiliz, powering Socios.com, aims to give sports and esports fans the ability to crowd-manage their favorite teams, games, leagues, and events.",
"explorer": "https://explorer.injective.network/asset/?tokenType=tokenFactory&denom=factory/inj14ejqjyq8um4p3xfqj74yld5waqljf88f9eneuk/inj1q6kpxy6ar5lkxqudjvryarrrttmakwsvzkvcyh",
"type": "INJECTIVE",
"symbol": "CHZ",
"decimals": 8,
"status": "active",
"id": "inj1q6kpxy6ar5lkxqudjvryarrrttmakwsvzkvcyh",
"tags": [
"nft"
],
"links": [
{
"name": "telegram",
"url": "https://t.me/chiliz_io"
},
{
"name": "twitter",
"url": "https://twitter.com/chiliZ/"
},
{
"name": "facebook",
"url": "https://facebook.com/chiliZdotcom/"
},
{
"name": "blog",
"url": "https://medium.com/chiliz"
},
{
"name": "whitepaper",
"url": "https://chiliz.com/docs/CHZ_whitepaper.pdf"
},
{
"name": "coingecko",
"url": "https://coingecko.com/en/coins/chiliz/"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

@@ -0,0 +1,37 @@
{
"name": "USD Coin",
"website": "https://centre.io/usdc",
"description": "USDC is a fully collateralized US dollar stablecoin, an Ethereum powered coin and is the brainchild of CENTRE, an open source project bootstrapped by contributions from Circle and Coinbase.",
"explorer": "https://explorer.injective.network/asset/?tokenType=tokenFactory&denom=factory/inj14ejqjyq8um4p3xfqj74yld5waqljf88f9eneuk/inj1q6zlut7gtkzknkk773jecujwsdkgq882akqksk",
"type": "INJECTIVE",
"symbol": "USDCET",
"decimals": 6,
"status": "active",
"id": "inj1q6zlut7gtkzknkk773jecujwsdkgq882akqksk",
"tags": [
"defi",
"stablecoin"
],
"links": [
{
"name": "github",
"url": "https://github.com/centrehq"
},
{
"name": "medium",
"url": "https://medium.com/centre-blog"
},
{
"name": "whitepaper",
"url": "https://centre.io/pdfs/centre-whitepaper.pdf"
},
{
"name": "coinmarketcap",
"url": "https://coinmarketcap.com/currencies/usd-coin/"
},
{
"name": "coingecko",
"url": "https://coingecko.com/en/coins/usd-coin/"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

@@ -0,0 +1,32 @@
{
"name": "Solana",
"website": "https://solana.com/",
"description": "Solana is the worlds most performant blockchain in the world at 710k transactions per second. 710k TPS is achieved by encoding the passage of time as data.",
"explorer": "https://explorer.injective.network/asset/?tokenType=tokenFactory&denom=factory/inj14ejqjyq8um4p3xfqj74yld5waqljf88f9eneuk/inj1sthrn5ep8ls5vzz8f9gp89khhmedahhdkqa8z3",
"symbol": "SOL",
"type": "INJECTIVE",
"decimals": 8,
"status": "active",
"id": "inj1sthrn5ep8ls5vzz8f9gp89khhmedahhdkqa8z3",
"tags": [
"staking-native"
],
"links": [
{
"name": "github",
"url": "https://github.com/solana-labs"
},
{
"name": "twitter",
"url": "https://twitter.com/solana"
},
{
"name": "reddit",
"url": "https://reddit.com/solana"
},
{
"name": "whitepaper",
"url": "https://github.com/solana-labs/whitepaper"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

+1 -1
View File
@@ -7,7 +7,7 @@ require (
github.com/spf13/cobra v1.7.0 github.com/spf13/cobra v1.7.0
github.com/trustwallet/assets-go-libs v0.3.7 github.com/trustwallet/assets-go-libs v0.3.7
github.com/trustwallet/go-libs v0.7.3 github.com/trustwallet/go-libs v0.7.3
github.com/trustwallet/go-primitives v0.1.16 github.com/trustwallet/go-primitives v0.1.17
) )
require ( require (
+8
View File
@@ -264,6 +264,14 @@ github.com/trustwallet/go-primitives v0.1.16-0.20240109124104-7cbe07e453d4 h1:sY
github.com/trustwallet/go-primitives v0.1.16-0.20240109124104-7cbe07e453d4/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc= github.com/trustwallet/go-primitives v0.1.16-0.20240109124104-7cbe07e453d4/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/trustwallet/go-primitives v0.1.16 h1:vpHVXlAYYK0o7GpWYadbauY+H4GOJ0bKVp/k6gZbH6I= github.com/trustwallet/go-primitives v0.1.16 h1:vpHVXlAYYK0o7GpWYadbauY+H4GOJ0bKVp/k6gZbH6I=
github.com/trustwallet/go-primitives v0.1.16/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc= github.com/trustwallet/go-primitives v0.1.16/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/trustwallet/go-primitives v0.1.17-0.20240112104809-d94379365003 h1:0XkIncqfu2hBCkkhMrVw7hx/lraoyZ+G1BENh+CFtVA=
github.com/trustwallet/go-primitives v0.1.17-0.20240112104809-d94379365003/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/trustwallet/go-primitives v0.1.17-0.20240112110133-ea920360531b h1:akbAfXaD987dDl5nbQbCiBSRP65pIWZdg9SNqY/pkfY=
github.com/trustwallet/go-primitives v0.1.17-0.20240112110133-ea920360531b/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/trustwallet/go-primitives v0.1.17-0.20240112111413-aca39bd29cc2 h1:3tt6/BPEtZWpQlagvqEoGz9Z53Jx0vrU+uPtxT/lCWg=
github.com/trustwallet/go-primitives v0.1.17-0.20240112111413-aca39bd29cc2/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/trustwallet/go-primitives v0.1.17 h1:X0vVRbJZoDcrQJTQ2vi5gubPfXEn43M9mpxkfRg/Fok=
github.com/trustwallet/go-primitives v0.1.17/go.mod h1:GN9WNIOvtz9j77FSYLEqC7apbMlzQnFLNO9LOCMy5Mc=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+56
View File
@@ -0,0 +1,56 @@
package info
import (
"github.com/trustwallet/assets-go-libs/validation"
"github.com/trustwallet/go-primitives/coin"
)
func ValidateAsset(a AssetModel, chain coin.Coin, addr string) error {
if err := ValidateAssetRequiredKeys(a); err != nil {
return err
}
// All fields validated for nil and can be safety used.
compErr := validation.NewErrComposite()
if err := ValidateAssetType(*a.Type, chain); err != nil {
compErr.Append(err)
}
if err := ValidateAssetID(*a.ID, addr); err != nil {
compErr.Append(err)
}
if err := ValidateDecimals(*a.Decimals); err != nil {
compErr.Append(err)
}
if err := ValidateAssetDecimalsAccordingType(*a.Type, *a.Decimals); err != nil {
compErr.Append(err)
}
if err := ValidateStatus(*a.Status); err != nil {
compErr.Append(err)
}
if err := ValidateDescription(*a.Description); err != nil {
compErr.Append(err)
}
if err := ValidateDescriptionWebsite(*a.Description, *a.Website); err != nil {
compErr.Append(err)
}
if err := ValidateExplorer(*a.Explorer, *a.Name, chain, addr, *a.Type); err != nil {
compErr.Append(err)
}
if err := ValidateLinks(a.Links); err != nil {
compErr.Append(err)
}
if compErr.Len() > 0 {
return compErr
}
return nil
}
+47
View File
@@ -0,0 +1,47 @@
package info
import (
"github.com/trustwallet/assets-go-libs/validation"
)
func ValidateCoin(c CoinModel, allowedTags []string) error {
if err := ValidateCoinRequiredKeys(c); err != nil {
return err
}
// All fields validated for nil and can be safety used.
compErr := validation.NewErrComposite()
if err := ValidateCoinType(*c.Type); err != nil {
compErr.Append(err)
}
if err := ValidateDecimals(*c.Decimals); err != nil {
compErr.Append(err)
}
if err := ValidateStatus(*c.Status); err != nil {
compErr.Append(err)
}
if err := ValidateTags(c.Tags, allowedTags); err != nil {
compErr.Append(err)
}
if err := ValidateDescription(*c.Description); err != nil {
compErr.Append(err)
}
if err := ValidateDescriptionWebsite(*c.Description, *c.Website); err != nil {
compErr.Append(err)
}
if err := ValidateLinks(c.Links); err != nil {
compErr.Append(err)
}
if compErr.Len() > 0 {
return compErr
}
return nil
}
+35
View File
@@ -0,0 +1,35 @@
package external
import (
"fmt"
"strconv"
"github.com/trustwallet/assets-go-libs/http"
)
const ethAPIURL = "https://api.ethplorer.io/getTokenInfo/%s?apiKey=freekey"
type TokenInfoERC20 struct {
Decimals string `json:"decimals"`
HoldersCount int `json:"holdersCount"`
}
func GetTokenInfoForERC20(tokenID string) (*TokenInfo, error) {
url := fmt.Sprintf(ethAPIURL, tokenID)
var result TokenInfoERC20
err := http.GetHTTPResponse(url, &result)
if err != nil {
return nil, err
}
decimals, err := strconv.Atoi(result.Decimals)
if err != nil {
return nil, err
}
return &TokenInfo{
Decimals: decimals,
HoldersCount: result.HoldersCount,
}, nil
}
+85
View File
@@ -0,0 +1,85 @@
package external
import (
"fmt"
"regexp"
"strconv"
"strings"
"github.com/trustwallet/assets-go-libs/http"
)
var (
holdersRegexp = regexp.MustCompile(`(\d+)\saddresses`)
decimalsRegexp = regexp.MustCompile(`(\d+)\s+<\/div>`)
symbolRegexp = regexp.MustCompile(`<b>(\w+)<\/b>\s<span`)
)
type TokenInfo struct {
Symbol string
Decimals int
HoldersCount int
}
func GetTokenInfo(tokenID, tokentType string) (*TokenInfo, error) {
switch strings.ToLower(tokentType) {
case "erc20":
return GetTokenInfoForERC20(tokenID)
case "bep20":
return GetTokenInfoByScraping(fmt.Sprintf("https://bscscan.com/token/%s", tokenID))
case "fantom":
return GetTokenInfoByScraping(fmt.Sprintf("https://ftmscan.com/token/%s", tokenID))
case "polygon":
return GetTokenInfoByScraping(fmt.Sprintf("https://polygonscan.com/token/%s", tokenID))
case "avalanche":
return GetTokenInfoByScraping(fmt.Sprintf("https://snowtrace.io/token/%s", tokenID))
case "spl":
return GetTokenInfoForSPL(tokenID)
case "trc20":
return GetTokenInfoForTRC20(tokenID)
case "trc10":
return GetTokenInfoForTRC10(tokenID)
}
return nil, nil
}
func GetTokenInfoByScraping(url string) (*TokenInfo, error) {
data, err := http.GetHTTPResponseBytes(url)
if err != nil {
return nil, err
}
// Remove all "," from content.
pageContent := strings.ReplaceAll(string(data), ",", "")
var holders, decimals int
var symbol string
match := symbolRegexp.FindStringSubmatch(pageContent)
if len(match) > 1 {
symbol = match[1]
}
match = decimalsRegexp.FindStringSubmatch(pageContent)
if len(match) > 1 {
decimals, err = strconv.Atoi(match[1])
if err != nil {
return nil, err
}
}
match = holdersRegexp.FindStringSubmatch(pageContent)
if len(match) > 1 {
holders, err = strconv.Atoi(match[1])
if err != nil {
return nil, err
}
}
return &TokenInfo{
Symbol: symbol,
Decimals: decimals,
HoldersCount: holders,
}, nil
}
+37
View File
@@ -0,0 +1,37 @@
package external
import (
"fmt"
"github.com/trustwallet/assets-go-libs/http"
)
const splAPIURL = "https://public-api.solscan.io/token/holders?tokenAddress=%s"
type TokenInfoSPL struct {
Data []Data `json:"data"`
HoldersCount int `json:"total"`
}
type Data struct {
Decimals int `json:"decimals"`
}
func GetTokenInfoForSPL(tokenID string) (*TokenInfo, error) {
url := fmt.Sprintf(splAPIURL, tokenID)
var result TokenInfoSPL
err := http.GetHTTPResponse(url, &result)
if err != nil {
return nil, err
}
if len(result.Data) == 0 {
return nil, fmt.Errorf("failed to get token info for SPL token")
}
return &TokenInfo{
Decimals: result.Data[0].Decimals,
HoldersCount: result.HoldersCount,
}, nil
}
+38
View File
@@ -0,0 +1,38 @@
package external
import (
"errors"
"fmt"
"github.com/trustwallet/assets-go-libs/http"
)
const trc10APIURL = "https://apilist.tronscan.io/api/token?id=%s"
type TRC10TokensResponse struct {
Data []struct {
Symbol string `json:"abbr"`
Decimals int `json:"precision"`
HoldersCount int `json:"nrOfTokenHolders"`
} `json:"data"`
}
func GetTokenInfoForTRC10(tokenID string) (*TokenInfo, error) {
url := fmt.Sprintf(trc10APIURL, tokenID)
var res TRC10TokensResponse
err := http.GetHTTPResponse(url, &res)
if err != nil {
return nil, err
}
if len(res.Data) == 0 {
return nil, errors.New("not found")
}
return &TokenInfo{
Symbol: res.Data[0].Symbol,
Decimals: res.Data[0].Decimals,
HoldersCount: res.Data[0].HoldersCount,
}, nil
}
+38
View File
@@ -0,0 +1,38 @@
package external
import (
"errors"
"fmt"
"github.com/trustwallet/assets-go-libs/http"
)
const trc20APIURL = "https://apilist.tronscan.io/api/token_trc20?contract=%s"
type TRC20TokensResponse struct {
TRC20Tokens []struct {
Symbol string `json:"symbol"`
Decimals int `json:"decimals"`
HoldersCount int `json:"holders_count"`
} `json:"trc20_tokens"`
}
func GetTokenInfoForTRC20(tokenID string) (*TokenInfo, error) {
url := fmt.Sprintf(trc20APIURL, tokenID)
var res TRC20TokensResponse
err := http.GetHTTPResponse(url, &res)
if err != nil {
return nil, err
}
if len(res.TRC20Tokens) == 0 {
return nil, errors.New("not found")
}
return &TokenInfo{
Symbol: res.TRC20Tokens[0].Symbol,
Decimals: res.TRC20Tokens[0].Decimals,
HoldersCount: res.TRC20Tokens[0].HoldersCount,
}, nil
}
+263
View File
@@ -0,0 +1,263 @@
package info
import (
"fmt"
"strings"
str "github.com/trustwallet/assets-go-libs/strings"
"github.com/trustwallet/assets-go-libs/validation"
"github.com/trustwallet/go-primitives/coin"
"github.com/trustwallet/go-primitives/types"
)
// Asset info specific validators.
func ValidateAssetRequiredKeys(a AssetModel) error {
var fields []string
if a.Name != nil && !isEmpty(*a.Name) {
fields = append(fields, "name")
}
if a.Symbol != nil && !isEmpty(*a.Symbol) {
fields = append(fields, "symbol")
}
if a.Type != nil && !isEmpty(*a.Type) {
fields = append(fields, "type")
}
if a.Decimals != nil {
fields = append(fields, "decimals")
}
if a.Description != nil && !isEmpty(*a.Description) {
fields = append(fields, "description")
}
if a.Website != nil {
fields = append(fields, "website")
}
if a.Explorer != nil && !isEmpty(*a.Explorer) {
fields = append(fields, "explorer")
}
if a.Status != nil && !isEmpty(*a.Status) {
fields = append(fields, "status")
}
if a.ID != nil && !isEmpty(*a.ID) {
fields = append(fields, "id")
}
if len(fields) != len(requiredAssetFields) {
return fmt.Errorf("%w: %s", validation.ErrMissingField,
strings.Join(str.Difference(requiredAssetFields, fields), ", "))
}
return nil
}
func ValidateAssetType(assetType string, chain coin.Coin) error {
chainFromType, err := types.GetChainFromAssetType(assetType)
if err != nil {
return fmt.Errorf("failed to get chain from asset type: %w", err)
}
if chainFromType != chain {
return fmt.Errorf("%w: asset type field", validation.ErrInvalidField)
}
if strings.ToUpper(assetType) != assetType {
return fmt.Errorf("%w: asset type should be ALLCAPS", validation.ErrInvalidField)
}
return nil
}
func ValidateAssetID(id, address string) error {
if id != address {
if !strings.EqualFold(id, address) {
return fmt.Errorf("%w: invalid id field", validation.ErrInvalidField)
}
return fmt.Errorf("%w: invalid case for id field", validation.ErrInvalidField)
}
return nil
}
func ValidateAssetDecimalsAccordingType(assetType string, decimals int) error {
if assetType == "BEP2" && decimals != 8 {
return fmt.Errorf("%w: invalid decimals field, BEP2 tokens have 8 decimals", validation.ErrInvalidField)
}
return nil
}
// CoinModel info specific validators.
func ValidateCoinRequiredKeys(c CoinModel) error {
var fields []string
if c.Name != nil && !isEmpty(*c.Name) {
fields = append(fields, "name")
}
if c.Symbol != nil && !isEmpty(*c.Symbol) {
fields = append(fields, "symbol")
}
if c.Type != nil && !isEmpty(*c.Type) {
fields = append(fields, "type")
}
if c.Decimals != nil {
fields = append(fields, "decimals")
}
if c.Description != nil && !isEmpty(*c.Description) {
fields = append(fields, "description")
}
if c.Website != nil && !isEmpty(*c.Website) {
fields = append(fields, "website")
}
if c.Explorer != nil && !isEmpty(*c.Explorer) {
fields = append(fields, "explorer")
}
if c.Status != nil && !isEmpty(*c.Status) {
fields = append(fields, "status")
}
if len(fields) != len(requiredCoinFields) {
return fmt.Errorf("%w: %s", validation.ErrMissingField,
strings.Join(str.Difference(requiredCoinFields, fields), ", "))
}
return nil
}
func ValidateLinks(links []Link) error {
if len(links) < 2 {
return nil
}
for _, l := range links {
if l.Name == nil || l.URL == nil {
return fmt.Errorf("%w: missing required fields links.url and links.name", validation.ErrMissingField)
}
if !linkNameAllowed(*l.Name) {
return fmt.Errorf("invalid value for links.name filed, allowed only: %s",
strings.Join(supportedLinkNames(), ", "))
}
prefix := allowedLinkKeys[*l.Name]
if prefix != "" {
if !strings.HasPrefix(*l.URL, prefix) {
return fmt.Errorf("invalid value '%s' for %s link url, allowed only with prefix: %s",
*l.URL, *l.Name, prefix)
}
}
if !strings.HasPrefix(*l.URL, "https://") {
return fmt.Errorf("invalid value for links.url field, allowed only with https:// prefix")
}
if *l.Name == "medium" {
if !strings.Contains(*l.URL, "medium.com") {
return fmt.Errorf("invalid value for links.url field, should contain medium.com")
}
}
}
return nil
}
func ValidateCoinType(assetType string) error {
if assetType != "coin" {
return fmt.Errorf("%w: only \"coin\" type allowed for coin field", validation.ErrInvalidField)
}
return nil
}
func ValidateTags(tags, allowedTags []string) error {
for _, t := range tags {
if !str.Contains(t, allowedTags) {
return fmt.Errorf("%w: tag '%s' not allowed", validation.ErrInvalidField, t)
}
}
return nil
}
// Both infos can be validated by this validators.
func ValidateDecimals(decimals int) error {
if decimals > 30 || decimals < 0 {
return fmt.Errorf("%w: decimals field", validation.ErrInvalidField)
}
return nil
}
func ValidateStatus(status string) error {
for _, f := range allowedStatusValues {
if f == status {
return nil
}
}
return fmt.Errorf("%w: allowed status field values: %s", validation.ErrInvalidField,
strings.Join(allowedStatusValues, ", "))
}
func ValidateDescription(description string) error {
if len(description) > 600 {
return fmt.Errorf("%w: invalid length for description field", validation.ErrInvalidField)
}
for _, ch := range whiteSpaceCharacters {
if strings.Contains(description, ch) {
return fmt.Errorf("%w: description contains not allowed characters (new line, double space)",
validation.ErrInvalidField)
}
}
return nil
}
func ValidateDescriptionWebsite(description, website string) error {
if description != "-" && website == "" {
return fmt.Errorf("%w: website field", validation.ErrMissingField)
}
return nil
}
func ValidateExplorer(explorer, name string, chain coin.Coin, addr, tokenType string) error {
// explorer url is composed with values outside info.json.
if chain == coin.Nativeinjective() {
return nil
}
explorerExpected, err := coin.GetCoinExploreURL(chain, addr, tokenType)
if err != nil {
explorerExpected = ""
}
explorerActual := explorer
if !strings.EqualFold(explorerActual, explorerExpected) {
explorerAlt := explorerURLAlternatives(chain.Handle, name)
if len(explorerAlt) == 0 {
return nil
}
var matchCount int
for _, e := range explorerAlt {
if strings.EqualFold(e, explorerActual) {
matchCount++
}
}
if matchCount == 0 {
return fmt.Errorf("invalid value for explorer field, %s insted of %s", explorerActual, explorerExpected)
}
}
return nil
}
func isEmpty(field string) bool {
return field == ""
}
+55
View File
@@ -0,0 +1,55 @@
package info
type (
CoinModel struct {
Name *string `json:"name,omitempty"`
Website *string `json:"website,omitempty"`
Description *string `json:"description,omitempty"`
Explorer *string `json:"explorer,omitempty"`
Research string `json:"research,omitempty"`
Symbol *string `json:"symbol,omitempty"`
Type *string `json:"type,omitempty"`
Decimals *int `json:"decimals,omitempty"`
Status *string `json:"status,omitempty"`
Tags []string `json:"tags,omitempty"`
Links []Link `json:"links,omitempty"`
}
AssetModel struct {
Name *string `json:"name,omitempty"`
Symbol *string `json:"symbol,omitempty"`
Type *string `json:"type,omitempty"`
Decimals *int `json:"decimals,omitempty"`
Description *string `json:"description,omitempty"`
Website *string `json:"website,omitempty"`
Explorer *string `json:"explorer,omitempty"`
Research string `json:"research,omitempty"`
Status *string `json:"status,omitempty"`
ID *string `json:"id,omitempty"`
Links []Link `json:"links,omitempty"`
ShortDesc *string `json:"short_desc,omitempty"`
Audit *string `json:"audit,omitempty"`
AuditReport *string `json:"audit_report,omitempty"`
Tags []string `json:"tags,omitempty"`
Code *string `json:"code,omitempty"`
Ticker *string `json:"ticker,omitempty"`
ExplorerEth *string `json:"explorer-ETH,omitempty"`
Address *string `json:"address,omitempty"`
Twitter *string `json:"twitter,omitempty"`
CoinMarketcap *string `json:"coinmarketcap,omitempty"`
DataSource *string `json:"data_source,omitempty"`
}
Link struct {
Name *string `json:"name,omitempty"`
URL *string `json:"url,omitempty"`
}
)
func (a *AssetModel) GetStatus() string {
if a.Status == nil {
return ""
}
return *a.Status
}
+79
View File
@@ -0,0 +1,79 @@
package info
import (
"fmt"
"strings"
"github.com/trustwallet/go-primitives/coin"
)
//nolint:gochecknoglobals
var (
requiredCoinFields = []string{
"name", "type", "symbol", "decimals",
"description", "website", "explorer", "status",
}
requiredAssetFields = []string{
"name", "type", "symbol", "decimals",
"description", "website", "explorer", "status", "id",
}
allowedStatusValues = []string{"active", "spam", "abandoned"}
allowedLinkKeys = map[string]string{
"github": "https://github.com/",
"whitepaper": "",
"twitter": "https://twitter.com/",
"telegram": "https://t.me/",
"telegram_news": "https://t.me/", // Read-only announcement channel.
"medium": "", // URL contains 'medium.com'.
"discord": "https://discord.com/",
"reddit": "https://reddit.com/",
"facebook": "https://facebook.com/",
"youtube": "https://youtube.com/",
"coinmarketcap": "https://coinmarketcap.com/",
"coingecko": "https://coingecko.com/",
"blog": "", // Blog, other than medium.
"forum": "", // Community site.
"docs": "",
"source_code": "", // Other than github.
}
whiteSpaceCharacters = []string{"\n", " "}
)
func explorerURLAlternatives(chain, name string) []string {
var altUrls []string
if name != "" {
nameNorm := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(
strings.ToLower(name), " ", ""), ")", ""), "(", "")
if strings.ToLower(chain) == coin.Coins[coin.ETHEREUM].Name {
altUrls = append(altUrls, fmt.Sprintf("https://etherscan.io/token/%s", nameNorm))
}
altUrls = append(altUrls, fmt.Sprintf("https://explorer.%s.io", nameNorm))
altUrls = append(altUrls, fmt.Sprintf("https://scan.%s.io", nameNorm))
}
return altUrls
}
func linkNameAllowed(str string) bool {
if _, exists := allowedLinkKeys[str]; !exists {
return false
}
return true
}
func supportedLinkNames() []string {
names := make([]string, 0, len(allowedLinkKeys))
for k := range allowedLinkKeys {
names = append(names, k)
}
return names
}
+1 -1
View File
@@ -8,10 +8,10 @@ import (
"github.com/trustwallet/assets-go-libs/file" "github.com/trustwallet/assets-go-libs/file"
"github.com/trustwallet/assets-go-libs/path" "github.com/trustwallet/assets-go-libs/path"
"github.com/trustwallet/assets-go-libs/validation" "github.com/trustwallet/assets-go-libs/validation"
"github.com/trustwallet/assets-go-libs/validation/info"
"github.com/trustwallet/assets-go-libs/validation/list" "github.com/trustwallet/assets-go-libs/validation/list"
"github.com/trustwallet/assets-go-libs/validation/tokenlist" "github.com/trustwallet/assets-go-libs/validation/tokenlist"
"github.com/trustwallet/assets/internal/config" "github.com/trustwallet/assets/internal/config"
"github.com/trustwallet/assets/internal/info"
"github.com/trustwallet/go-primitives/coin" "github.com/trustwallet/go-primitives/coin"
) )