Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add airdrop tool #204

Open
wants to merge 65 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
d983dce
add desc
tungleanh0902 Mar 19, 2024
d05f433
add feature for other chains
tungleanh0902 Mar 20, 2024
58c8764
update fetch logic for other chains
tungleanh0902 Mar 21, 2024
8211fc6
feat: 90%
tungleanh0902 Mar 22, 2024
d9e1f98
feat: add snapshot for nft on cosmos chain
tungleanh0902 Mar 25, 2024
f912f56
feat: add snapshot for nft on ethereum
tungleanh0902 Mar 26, 2024
86ee66a
fix: lint
tungleanh0902 Mar 27, 2024
73b8d1c
chore: correct lint naming convention
trungnt1811 May 2, 2024
0fd538b
refactor: use a map called balanceFunctions to associate function nam…
trungnt1811 May 2, 2024
073e948
refactor: fetching balances concurrently
trungnt1811 May 2, 2024
ff0137d
chore: avoid division by 0
trungnt1811 May 2, 2024
8c6b692
fix: grpc no transport security set
trungnt1811 May 2, 2024
b4dac3e
fix: deadlock goroutines
trungnt1811 May 2, 2024
a6218af
refactor: add retryable and return errors
trungnt1811 May 3, 2024
d3b29dc
refactor: implement error handling for converting Bech32 addresses an…
trungnt1811 May 3, 2024
791ae8c
refactor: change the exponential backoff calculation
trungnt1811 May 4, 2024
adf87a8
refactor: remove redundant fetchTokenPriceWithRetry functions
trungnt1811 May 4, 2024
afd465b
chore: change const name
trungnt1811 May 4, 2024
222fe0c
Merge remote-tracking branch 'origin/main' into trung/airdrop
faddat May 4, 2024
5b0713d
fix conflict
faddat May 4, 2024
b365abf
Merge branch 'main' into trung/airdrop
faddat May 4, 2024
dfa8fcb
add error handling in goroutines (#208)
trungnt1811 May 6, 2024
2b1ba2d
Merge remote-tracking branch 'origin/main' into trung/airdrop
trungnt1811 May 6, 2024
affbf61
fix: ignore tokens are not NFT
trungnt1811 May 8, 2024
f2b7340
refactor: fetch token info concurrently
trungnt1811 May 8, 2024
0a83c0e
chore: fix lint
trungnt1811 May 8, 2024
33d328b
Merge branch 'main' into trung/airdrop
faddat May 13, 2024
2b1ad81
Merge branch 'main' into trung/airdrop
faddat May 15, 2024
8cdce99
Merge remote-tracking branch 'origin/HEAD' into trung/airdrop
trungnt1811 May 31, 2024
cb5afae
chore: update go.mod
trungnt1811 May 31, 2024
0ebce15
refactor: add utils
trungnt1811 May 31, 2024
7982b32
add exponential backoff
trungnt1811 May 31, 2024
918ba83
refactor code base
trungnt1811 May 31, 2024
f82b8ea
refactor: implement common FetchTokenPrice func
trungnt1811 May 31, 2024
6cc858c
refactor: remove redundant GetValidatorDelegations func
trungnt1811 May 31, 2024
2cc0c53
fix: total delegated tokens calculation is incorrect
trungnt1811 May 31, 2024
cb7ce89
chore: fix lint
trungnt1811 May 31, 2024
89850c7
chore: fix lint
trungnt1811 May 31, 2024
543cb9f
refactor: fetch token info funcs
trungnt1811 May 31, 2024
cc95125
chore: fix lint
trungnt1811 May 31, 2024
9d94cd4
chore: add doc
trungnt1811 May 31, 2024
1878c54
refactor: using log over fmt print func
trungnt1811 May 31, 2024
1949b6a
fix: use Quo instead of QuoTruncate for more precise calculations
trungnt1811 May 31, 2024
4519d85
refactor: add more logs
trungnt1811 May 31, 2024
02941b1
chore: fix lint
trungnt1811 May 31, 2024
b0000d9
refactor: enhance error logging for better traceability
trungnt1811 May 31, 2024
45ff366
refactor: hande potential division by zero
trungnt1811 May 31, 2024
9a4b51d
refactor: optimize error handling
trungnt1811 May 31, 2024
acb2fe9
chore: update const
trungnt1811 Jun 1, 2024
0161730
Merge remote-tracking branch 'origin/main' into trung/airdrop
faddat Jul 2, 2024
2de323a
Merge branch 'main' into trung/airdrop
faddat Jul 4, 2024
819c759
Merge branch 'main' into trung/airdrop
trungnt1811 Jul 4, 2024
854748c
Merge branch 'main' into trung/airdrop
trungnt1811 Jul 5, 2024
1656d76
refactor code
trungnt1811 Jul 5, 2024
a7ba8e0
refactor: add airdrop tool as a submodule
trungnt1811 Jul 5, 2024
1435957
refactor: update airdrop submodule
trungnt1811 Jul 5, 2024
c0965a4
Merge branch 'main' into trung/airdrop
trungnt1811 Jul 5, 2024
6352bed
refactor: update go.mod
trungnt1811 Jul 5, 2024
86a0091
chore: update .gitignore
trungnt1811 Jul 5, 2024
72e651c
chore: update .gitignore
trungnt1811 Jul 5, 2024
d86e348
refactor FetchValidators func
trungnt1811 Jul 8, 2024
35e34ce
refactor FetchDelegations func
trungnt1811 Jul 8, 2024
63ad7ae
chore: update airdrop go.mod
trungnt1811 Jul 8, 2024
e072fbd
chore: update go.mod
trungnt1811 Jul 8, 2024
9968d3a
refactor: add GetMinimumTokensThreshold func
trungnt1811 Jul 8, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,10 @@ dependency-graph.png
*.out
*.synctex.gz
contract_tests/*
scripts/wasm/*.wasm

#Airdrop tool
balance.json
rewards.json
balancenft.json
.env
scripts/wasm/*.wasm
161 changes: 161 additions & 0 deletions airdrop/akash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package main

import (
"context"
"encoding/json"
"fmt"
"io"
"strconv"

"github.com/eve-network/eve/airdrop/config"
"github.com/joho/godotenv"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"

sdkmath "cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
"github.com/cosmos/cosmos-sdk/types/query"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func akash() ([]banktypes.Balance, []config.Reward, int, error) {
err := godotenv.Load()
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to load env: %w", err)
}

blockHeight, err := getLatestHeightWithRetry(config.GetAkashConfig().RPC + "/status")
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to get latest height for Akash: %w", err)
}

grpcAddr := config.GetAkashConfig().GRPCAddr
grpcConn, err := grpc.Dial(grpcAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to connect to gRPC Akash: %w", err)
}
defer grpcConn.Close()
stakingClient := stakingtypes.NewQueryClient(grpcConn)

delegators := []stakingtypes.DelegationResponse{}

validators, err := getValidators(stakingClient, blockHeight)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to get Akash validators: %w", err)
}
fmt.Println("Validators: ", len(validators))
for validatorIndex, validator := range validators {
var header metadata.MD
delegationsResponse, err := stakingClient.ValidatorDelegations(
metadata.AppendToOutgoingContext(context.Background(), grpctypes.GRPCBlockHeightHeader, blockHeight), // Add metadata to request
&stakingtypes.QueryValidatorDelegationsRequest{
ValidatorAddr: validator.OperatorAddress,
Pagination: &query.PageRequest{
CountTotal: true,
Limit: LimitPerPage,
},
},
grpc.Header(&header), // Retrieve header from response
)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to query delegate info for Akash validator: %w", err)
}
total := delegationsResponse.Pagination.Total
fmt.Println("Response ", len(delegationsResponse.DelegationResponses))
fmt.Println("Akash validator "+strconv.Itoa(validatorIndex)+" ", total)
delegators = append(delegators, delegationsResponse.DelegationResponses...)
}

usd := sdkmath.LegacyMustNewDecFromStr("20")

apiURL := APICoingecko + config.GetAkashConfig().CoinID + "&vs_currencies=usd"
fetchTokenPrice := fetchTokenPriceWithRetry(fetchAkashTokenPrice)
tokenInUsd, err := fetchTokenPrice(apiURL)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to fetch Akash token price: %w", err)
}
tokenIn20Usd := usd.QuoTruncate(tokenInUsd)

rewardInfo := []config.Reward{}
balanceInfo := []banktypes.Balance{}

totalTokenDelegate := sdkmath.LegacyMustNewDecFromStr("0")
for _, delegator := range delegators {
validatorIndex := findValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(tokenIn20Usd) {
continue
}
totalTokenDelegate = totalTokenDelegate.Add(token)
}
eveAirdrop := sdkmath.LegacyMustNewDecFromStr(EveAirdrop)
testAmount, err := sdkmath.LegacyNewDecFromStr("0")
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to convert string to dec: %w", err)
}
for _, delegator := range delegators {
validatorIndex := findValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(tokenIn20Usd) {
continue
}
eveAirdrop := (eveAirdrop.MulInt64(int64(config.GetAkashConfig().Percent))).QuoInt64(100).Mul(token).QuoTruncate(totalTokenDelegate)
eveBech32Address, err := convertBech32Address(delegator.Delegation.DelegatorAddress)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to convert Bech32Address: %w", err)
}
rewardInfo = append(rewardInfo, config.Reward{
Address: delegator.Delegation.DelegatorAddress,
EveAddress: eveBech32Address,
Shares: delegator.Delegation.Shares,
Token: token,
EveAirdropToken: eveAirdrop,
ChainID: config.GetAkashConfig().ChainID,
})
testAmount = eveAirdrop.Add(testAmount)
balanceInfo = append(balanceInfo, banktypes.Balance{
Address: eveBech32Address,
Coins: sdk.NewCoins(sdk.NewCoin("eve", eveAirdrop.TruncateInt())),
})
}
fmt.Println("Akash ", testAmount)
// Write delegations to file
// fileForDebug, _ := json.MarshalIndent(rewardInfo, "", " ")
// _ = os.WriteFile("rewards.json", fileForDebug, 0644)

// fileBalance, _ := json.MarshalIndent(balanceInfo, "", " ")
// _ = os.WriteFile("balance.json", fileBalance, 0644)
return balanceInfo, rewardInfo, len(balanceInfo), nil
}

func fetchAkashTokenPrice(apiURL string) (sdkmath.LegacyDec, error) {
// Make a GET request to the API
response, err := makeGetRequest(apiURL)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error making GET request to fetch Akash token price: %w", err)
}
defer response.Body.Close()

// Read the response body
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error reading response body for Akash token price: %w", err)
}

var data config.AkashPrice

// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error unmarshalling JSON for Akash token price: %w", err)
}

tokenInUsd := sdkmath.LegacyMustNewDecFromStr(data.Token.USD.String())
return tokenInUsd, nil
}
131 changes: 131 additions & 0 deletions airdrop/bostrom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package main

import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"

"github.com/eve-network/eve/airdrop/config"

sdkmath "cosmossdk.io/math"

sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
)

func bostrom() ([]banktypes.Balance, []config.Reward, int, error) {
delegators := []stakingtypes.DelegationResponse{}

rpc := config.GetBostromConfig().API + "/cosmos/staking/v1beta1/validators?pagination.limit=" + strconv.Itoa(LimitPerPage) + "&pagination.count_total=true"
validatorsResponse, err := fetchValidators(rpc)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to fetch validators for Bostrom: %w", err)
}
validators := validatorsResponse.Validators
fmt.Println("Validators: ", len(validators))
for validatorIndex, validator := range validators {
url := config.GetBostromConfig().API + "/cosmos/staking/v1beta1/validators/" + validator.OperatorAddress + "/delegations?pagination.limit=" + strconv.Itoa(LimitPerPage) + "&pagination.count_total=true"
delegations, total, err := fetchDelegationsWithRetry(url)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to fetch delegations for Bostrom: %w", err)
}
fmt.Println(validator.OperatorAddress)
fmt.Println("Response ", len(delegations))
fmt.Println("Bostrom validator "+strconv.Itoa(validatorIndex)+" ", total)
delegators = append(delegators, delegations...)
}

usd := sdkmath.LegacyMustNewDecFromStr("20")

apiURL := APICoingecko + config.GetBostromConfig().CoinID + "&vs_currencies=usd"
fetchTokenPrice := fetchTokenPriceWithRetry(fetchBostromTokenPrice)
tokenInUsd, err := fetchTokenPrice(apiURL)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to fetch Bostrom token price: %w", err)
}
tokenIn20Usd := usd.QuoTruncate(tokenInUsd)

rewardInfo := []config.Reward{}
balanceInfo := []banktypes.Balance{}

totalTokenDelegate := sdkmath.LegacyMustNewDecFromStr("0")
for _, delegator := range delegators {
validatorIndex := findValidatorInfoCustomType(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(tokenIn20Usd) {
continue
}
totalTokenDelegate = totalTokenDelegate.Add(token)
}
eveAirdrop := sdkmath.LegacyMustNewDecFromStr(EveAirdrop)
testAmount, _ := sdkmath.LegacyNewDecFromStr("0")
for _, delegator := range delegators {
validatorIndex := findValidatorInfoCustomType(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(tokenIn20Usd) {
continue
}
eveAirdrop := (eveAirdrop.MulInt64(int64(config.GetBostromConfig().Percent))).QuoInt64(100).Mul(token).QuoTruncate(totalTokenDelegate)
eveBech32Address, err := convertBech32Address(delegator.Delegation.DelegatorAddress)
if err != nil {
return nil, nil, 0, fmt.Errorf("failed to convert Bech32Address: %w", err)
}
rewardInfo = append(rewardInfo, config.Reward{
Address: delegator.Delegation.DelegatorAddress,
EveAddress: eveBech32Address,
Shares: delegator.Delegation.Shares,
Token: token,
EveAirdropToken: eveAirdrop,
ChainID: config.GetBostromConfig().ChainID,
})
testAmount = eveAirdrop.Add(testAmount)
balanceInfo = append(balanceInfo, banktypes.Balance{
Address: eveBech32Address,
Coins: sdk.NewCoins(sdk.NewCoin("eve", eveAirdrop.TruncateInt())),
})
}
fmt.Println("Bostrom ", testAmount)
// Write delegations to file
// fileForDebug, _ := json.MarshalIndent(rewardInfo, "", " ")
// _ = os.WriteFile("rewards.json", fileForDebug, 0644)

// fileBalance, _ := json.MarshalIndent(balanceInfo, "", " ")
// _ = os.WriteFile("balance.json", fileBalance, 0644)
return balanceInfo, rewardInfo, len(balanceInfo), nil
}

func fetchBostromTokenPrice(apiURL string) (sdkmath.LegacyDec, error) {
// Make a GET request to the API
response, err := makeGetRequest(apiURL)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error making GET request to fetch Bostrom token price: %w", err)
}
defer response.Body.Close()

// Read the response body
responseBody, err := io.ReadAll(response.Body)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error reading response body for Bostrom token price: %w", err)
}

var data config.BostromPrice

// Unmarshal the JSON byte slice into the defined struct
err = json.Unmarshal(responseBody, &data)
if err != nil {
return sdkmath.LegacyDec{}, fmt.Errorf("error unmarshalling JSON for Bostrom token price: %w", err)
}
rawPrice := strings.Split(data.Token.USD.String(), "e-")
base := rawPrice[0]
power := rawPrice[1]
powerInt, _ := strconv.ParseUint(power, 10, 64)
baseDec, _ := sdkmath.LegacyNewDecFromStr(base)
tenDec, _ := sdkmath.LegacyNewDecFromStr("10")
tokenInUsd := baseDec.Quo(tenDec.Power(powerInt))
return tokenInUsd, nil
}
Loading
Loading