From 543cb9f15be3bd121e39c01b3e9742baf624a700 Mon Sep 17 00:00:00 2001 From: trung Date: Fri, 31 May 2024 19:32:23 +0700 Subject: [PATCH] refactor: fetch token info funcs --- airdrop/backoff/backoff.go | 2 +- airdrop/chains/cosmosnft.go | 121 +----------------------------------- airdrop/config/constant.go | 3 - airdrop/utils/utils.go | 98 ++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 123 deletions(-) diff --git a/airdrop/backoff/backoff.go b/airdrop/backoff/backoff.go index 77e684d9..107659b1 100644 --- a/airdrop/backoff/backoff.go +++ b/airdrop/backoff/backoff.go @@ -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 diff --git a/airdrop/chains/cosmosnft.go b/airdrop/chains/cosmosnft.go index 029f7d09..bca6a41d 100644 --- a/airdrop/chains/cosmosnft.go +++ b/airdrop/chains/cosmosnft.go @@ -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" @@ -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) } @@ -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 @@ -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 -} +} \ No newline at end of file diff --git a/airdrop/config/constant.go b/airdrop/config/constant.go index b27ac2f3..b4148c7d 100644 --- a/airdrop/config/constant.go +++ b/airdrop/config/constant.go @@ -1,7 +1,5 @@ package config -import "time" - const ( LimitPerPage = 100000000 EveAirdrop = "1000000000" // 1,000,000,000 @@ -9,5 +7,4 @@ const ( Cryptonium = "stars1g2ptrqnky5pu70r3g584zpk76cwqplyc63e8apwayau6l3jr8c0sp9q45u" APICoingecko = "https://api.coingecko.com/api/v3/simple/price?ids=" MaxRetries = 5 - BackOff = 200 * time.Millisecond ) diff --git a/airdrop/utils/utils.go b/airdrop/utils/utils.go index ac954616..c715c160 100644 --- a/airdrop/utils/utils.go +++ b/airdrop/utils/utils.go @@ -2,6 +2,7 @@ package utils import ( "context" + "encoding/base64" "encoding/json" "fmt" "io" @@ -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() @@ -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 +}