Skip to content

Commit

Permalink
refactor: fetch token info funcs
Browse files Browse the repository at this point in the history
  • Loading branch information
trungnt1811 committed May 31, 2024
1 parent 89850c7 commit 543cb9f
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 123 deletions.
2 changes: 1 addition & 1 deletion airdrop/backoff/backoff.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var globalBackoffOptions = []backoff.ExponentialBackOffOpts{
b.InitialInterval = 1 * time.Second
},
func(b *backoff.ExponentialBackOff) {
b.MaxInterval = 32 * time.Second
b.MaxInterval = 15 * time.Second
},
func(b *backoff.ExponentialBackOff) {
b.Multiplier = 2
Expand Down
121 changes: 3 additions & 118 deletions airdrop/chains/cosmosnft.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package chains

import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"math"
"sync"
"time"

"github.com/eve-network/eve/airdrop/config"
"github.com/eve-network/eve/airdrop/utils"
Expand All @@ -19,7 +14,7 @@ import (
)

func Cosmosnft(contract string, percent int64) ([]banktypes.Balance, []config.Reward, int, error) {
tokenIds, err := fetchTokenIdsWithRetry(contract)
tokenIds, err := utils.FetchTokenIds(contract, config.GetStargazeConfig().API)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to fetch token ids: %w", err)
}
Expand All @@ -41,7 +36,7 @@ func Cosmosnft(contract string, percent int64) ([]banktypes.Balance, []config.Re
defer func() { <-semaphore }() // Release semaphore when done
defer wg.Done()

nftHolders, err := fetchTokenInfoWithRetry(token, contract)
nftHolders, err := utils.FetchTokenInfo(token, contract, config.GetStargazeConfig().API)
if err != nil {
fmt.Printf("Error fetching token info for %s: %v\n", token, err)
return
Expand Down Expand Up @@ -103,114 +98,4 @@ func Cosmosnft(contract string, percent int64) ([]banktypes.Balance, []config.Re
return balanceInfo, rewardInfo, len(balanceInfo), nil
}
}
}

func fetchTokenInfoWithRetry(token, contract string) (config.NftHolder, error) {
var data config.NftHolder
var err error
for attempt := 1; attempt <= config.MaxRetries; attempt++ {
data, err = fetchTokenInfo(token, contract)
if err == nil {
return data, nil
}

fmt.Printf("Error fetch token info (attempt %d/%d): %v\n", attempt, config.MaxRetries, err)

if attempt < config.MaxRetries {
// Calculate backoff duration using exponential backoff strategy
backoffDuration := time.Duration(config.BackOff.Seconds() * math.Pow(2, float64(attempt)))
fmt.Printf("Retrying after %s...\n", backoffDuration)
time.Sleep(backoffDuration)
}
}
return config.NftHolder{}, fmt.Errorf("failed to fetch token info after %d attempts", config.MaxRetries)
}

func fetchTokenInfo(token, contract string) (config.NftHolder, error) {
queryString := fmt.Sprintf(`{"all_nft_info":{"token_id":%s}}`, token)
encodedQuery := base64.StdEncoding.EncodeToString([]byte(queryString))
apiURL := config.GetStargazeConfig().API + "/cosmwasm/wasm/v1/contract/" + contract + "/smart/" + encodedQuery
response, err := utils.MakeGetRequest(apiURL)
if err != nil {
return config.NftHolder{}, fmt.Errorf("error making GET request to fetch token info: %w", err)
}
defer response.Body.Close()

var data config.TokenInfoResponse
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return config.NftHolder{}, fmt.Errorf("error reading response body when fetch token info: %w", err)
}
// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return config.NftHolder{}, fmt.Errorf("error unmarshalling JSON when fetch token info: %w", err)
}
return config.NftHolder{
Address: data.Data.Access.Owner,
TokenID: token,
}, nil
}

func fetchTokenIdsWithRetry(contract string) ([]string, error) {
var tokenIds []string
var err error
for attempt := 1; attempt <= config.MaxRetries; attempt++ {
tokenIds, err = fetchTokenIds(contract)
if err == nil {
return tokenIds, nil
}

fmt.Printf("Error fetch token ids (attempt %d/%d): %v\n", attempt, config.MaxRetries, err)

if attempt < config.MaxRetries {
// Calculate backoff duration using exponential backoff strategy
backoffDuration := time.Duration(config.BackOff.Seconds() * math.Pow(2, float64(attempt)))
fmt.Printf("Retrying after %s...\n", backoffDuration)
time.Sleep(backoffDuration)
}
}
return nil, fmt.Errorf("failed to fetch token ids after %d attempts", config.MaxRetries)
}

func fetchTokenIds(contract string) ([]string, error) {
// Make a GET request to the API
paginationKey := "0"
index := 0
tokenIds := []string{}
for {
index++
queryString := fmt.Sprintf(`{"all_tokens":{"limit":1000,"start_after":"%s"}}`, paginationKey)
encodedQuery := base64.StdEncoding.EncodeToString([]byte(queryString))
apiURL := config.GetStargazeConfig().API + "/cosmwasm/wasm/v1/contract/" + contract + "/smart/" + encodedQuery
response, err := utils.MakeGetRequest(apiURL)
if err != nil {
return nil, fmt.Errorf("error making GET request to fetch token ids: %w", err)
}
defer response.Body.Close()

var data config.TokenIdsResponse
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body when fetch token ids: %w", err)
}
// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return nil, fmt.Errorf("error error unmarshalling JSON when fetch token ids: %w", err)
}
tokenIds = append(tokenIds, data.Data.Token...)
if len(data.Data.Token) == 0 {
break
} else {
paginationKey = data.Data.Token[len(data.Data.Token)-1]
fmt.Println("pagination key:", paginationKey)
if len(paginationKey) == 0 {
break
}
}
}

fmt.Println(len(tokenIds))
return tokenIds, nil
}
}
3 changes: 0 additions & 3 deletions airdrop/config/constant.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package config

import "time"

const (
LimitPerPage = 100000000
EveAirdrop = "1000000000" // 1,000,000,000
Badkids = "stars19jq6mj84cnt9p7sagjxqf8hxtczwc8wlpuwe4sh62w45aheseues57n420"
Cryptonium = "stars1g2ptrqnky5pu70r3g584zpk76cwqplyc63e8apwayau6l3jr8c0sp9q45u"
APICoingecko = "https://api.coingecko.com/api/v3/simple/price?ids="
MaxRetries = 5
BackOff = 200 * time.Millisecond
)
98 changes: 97 additions & 1 deletion airdrop/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
Expand Down Expand Up @@ -227,7 +228,7 @@ func FetchDelegations(rpcURL string) (stakingtypes.DelegationResponses, uint64,
}

if err := backoff.Retry(retryableRequest, exponentialBackoff); err != nil {
return nil, 0, fmt.Errorf("error making GET request to get fetch delegations: %w", err)
return nil, 0, fmt.Errorf("error making GET request to fetch delegations: %w", err)
}
defer response.Body.Close()

Expand Down Expand Up @@ -324,3 +325,98 @@ func FetchTokenPrice(apiURL, coinID string) (sdkmath.LegacyDec, error) {
func SetupGRPCConnection(address string) (*grpc.ClientConn, error) {
return grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
}

func FetchTokenInfo(token, contractAddress, apiFromConfig string) (config.NftHolder, error) {
queryString := fmt.Sprintf(`{"all_nft_info":{"token_id":%s}}`, token)
encodedQuery := base64.StdEncoding.EncodeToString([]byte(queryString))
apiURL := apiFromConfig + "/cosmwasm/wasm/v1/contract/" + contractAddress + "/smart/" + encodedQuery

ctx := context.Background()

var response *http.Response
var err error

exponentialBackoff := airdropBackoff.NewBackoff(ctx)

retryableRequest := func() error {
// Make a GET request to the API
response, err = MakeGetRequest(apiURL)
return err
}

if err := backoff.Retry(retryableRequest, exponentialBackoff); err != nil {
return config.NftHolder{}, fmt.Errorf("error making GET request to fetch token info: %w", err)
}

defer response.Body.Close()

var data config.TokenInfoResponse
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return config.NftHolder{}, fmt.Errorf("error reading response body when fetch token info: %w", err)
}
// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return config.NftHolder{}, fmt.Errorf("error unmarshalling JSON when fetch token info: %w", err)
}
return config.NftHolder{
Address: data.Data.Access.Owner,
TokenID: token,
}, nil
}

func FetchTokenIds(contractAddress string, apiFromConfig string) ([]string, error) {
// Make a GET request to the API
paginationKey := "0"
index := 0
tokenIds := []string{}
for {
index++
queryString := fmt.Sprintf(`{"all_tokens":{"limit":1000,"start_after":"%s"}}`, paginationKey)
encodedQuery := base64.StdEncoding.EncodeToString([]byte(queryString))
apiURL := apiFromConfig + "/cosmwasm/wasm/v1/contract/" + contractAddress + "/smart/" + encodedQuery
ctx := context.Background()

var response *http.Response
var err error

exponentialBackoff := airdropBackoff.NewBackoff(ctx)

retryableRequest := func() error {
// Make a GET request to the API
response, err = MakeGetRequest(apiURL)
return err
}

if err := backoff.Retry(retryableRequest, exponentialBackoff); err != nil {
return nil, fmt.Errorf("error making GET request to fetch token ids: %w", err)
}

defer response.Body.Close()

var data config.TokenIdsResponse
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("error reading response body when fetch token ids: %w", err)
}
// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return nil, fmt.Errorf("error error unmarshalling JSON when fetch token ids: %w", err)
}
tokenIds = append(tokenIds, data.Data.Token...)
if len(data.Data.Token) == 0 {
break
} else {
paginationKey = data.Data.Token[len(data.Data.Token)-1]
fmt.Println("pagination key:", paginationKey)
if len(paginationKey) == 0 {
break
}
}
}

fmt.Println(len(tokenIds))
return tokenIds, nil
}

0 comments on commit 543cb9f

Please sign in to comment.