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 all 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
6 changes: 6 additions & 0 deletions airdrop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#Airdrop tool
airdropd
balance.json
rewards.json
balancenft.json
.env
3 changes: 3 additions & 0 deletions airdrop/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
build: airdrop-tool
airdrop-tool:
go build -o airdropd ./cmd/airdrop
24 changes: 24 additions & 0 deletions airdrop/backoff/backoff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package backoff

import (
"context"
"time"

"github.com/cenkalti/backoff/v4"
)

var globalBackoffOptions = []backoff.ExponentialBackOffOpts{
func(b *backoff.ExponentialBackOff) {
b.InitialInterval = 1 * time.Second
},
func(b *backoff.ExponentialBackOff) {
b.MaxInterval = 15 * time.Second
},
func(b *backoff.ExponentialBackOff) {
b.Multiplier = 2
},
}

func NewBackoff(ctx context.Context) *backoff.ExponentialBackOff {
return backoff.NewExponentialBackOff(globalBackoffOptions...)
}
125 changes: 125 additions & 0 deletions airdrop/chains/akash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package chains

import (
"fmt"
"log"

"github.com/eve-network/eve/airdrop/config"
"github.com/eve-network/eve/airdrop/utils"
"github.com/joho/godotenv"

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 Akash() ([]banktypes.Balance, []config.Reward, int, error) {
// Load environment variables
if err := godotenv.Load(); err != nil {
log.Printf("Error loading Akash environment variables: %v", err)
return nil, nil, 0, fmt.Errorf("failed to load env: %w", err)
}

// Get latest block height
blockHeight, err := utils.GetLatestHeight(config.GetAkashConfig().RPC + "/status")
if err != nil {
log.Printf("Failed to get latest height for Akash: %v", err)
return nil, nil, 0, fmt.Errorf("failed to get latest height for Akash: %w", err)
}

// Setup gRPC connection
grpcAddr := config.GetAkashConfig().GRPCAddr
grpcConn, err := utils.SetupGRPCConnection(grpcAddr)
if err != nil {
log.Printf("Failed to connect to gRPC Akash: %v", err)
return nil, nil, 0, fmt.Errorf("failed to connect to gRPC Akash: %w", err)
}
defer grpcConn.Close()

stakingClient := stakingtypes.NewQueryClient(grpcConn)
delegators := []stakingtypes.DelegationResponse{}

// Get validators
validators, err := utils.GetValidators(stakingClient, blockHeight)
if err != nil {
log.Printf("Failed to get Akash validators: %v", err)
return nil, nil, 0, fmt.Errorf("failed to get Akash validators: %w", err)
}
log.Println("Validators: ", len(validators))

// Get delegations for each validator
for validatorIndex, validator := range validators {
delegationsResponse, err := utils.GetValidatorDelegations(stakingClient, validator.OperatorAddress, blockHeight)
if err != nil {
log.Printf("Failed to query delegate info for Akash validator: %v", err)
return nil, nil, 0, fmt.Errorf("failed to query delegate info for Akash validator: %w", err)
}
total := delegationsResponse.Pagination.Total
log.Printf("Akash validator %d has %d delegators", validatorIndex, total)
delegators = append(delegators, delegationsResponse.DelegationResponses...)
}

// Calculate token price and threshold
minimumTokensThreshold, err := utils.GetMinimumTokensThreshold(config.GetAkashConfig().CoinID)
if err != nil {
log.Printf("Failed to fetch Akash token price: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch Akash token price: %w", err)
}

// Prepare for airdrop calculation
rewardInfo := []config.Reward{}
balanceInfo := []banktypes.Balance{}
totalTokenDelegate := sdkmath.LegacyMustNewDecFromStr("0")

for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
totalTokenDelegate = totalTokenDelegate.Add(token)
}

eveAirdrop, err := sdkmath.LegacyNewDecFromStr(config.EveAirdrop)
if err != nil {
log.Printf("Failed to convert EveAirdrop string to dec: %v", err)
return nil, nil, 0, fmt.Errorf("failed to convert EveAirdrop string to dec: %w", err)
}

testAmount := sdkmath.LegacyMustNewDecFromStr("0")

for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)

if token.LT(minimumTokensThreshold) {
continue
}

eveAirdropTokens := (eveAirdrop.MulInt64(int64(config.GetAkashConfig().Percent))).QuoInt64(100).Mul(token).QuoTruncate(totalTokenDelegate)
eveBech32Address, err := utils.ConvertBech32Address(delegator.Delegation.DelegatorAddress)
if err != nil {
log.Printf("Failed to convert Akash bech32 address: %v", err)
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: eveAirdropTokens,
ChainID: config.GetAkashConfig().ChainID,
})
testAmount = eveAirdropTokens.Add(testAmount)
balanceInfo = append(balanceInfo, banktypes.Balance{
Address: eveBech32Address,
Coins: sdk.NewCoins(sdk.NewCoin("eve", eveAirdropTokens.TruncateInt())),
})
}

log.Println("Akash balance: ", testAmount)

return balanceInfo, rewardInfo, len(balanceInfo), nil
}
104 changes: 104 additions & 0 deletions airdrop/chains/bostrom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package chains

import (
"fmt"
"log"

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

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{}

// Fetch validators
validatorsResponse, err := utils.FetchValidators(config.GetBostromConfig().API, config.LimitPerPage)
if err != nil {
log.Printf("Failed to fetch Bostrom validators: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch validators for Bostrom: %w", err)
}
trungnt1811 marked this conversation as resolved.
Show resolved Hide resolved
validators := validatorsResponse.Validators
log.Println("Validators: ", len(validators))

// Fetch delegations for each validator
for validatorIndex, validator := range validators {
delegations, total, err := utils.FetchDelegations(
config.GetBostromConfig().API,
validator.OperatorAddress,
config.LimitPerPage,
)
if err != nil {
log.Printf("Failed to fetch delegations for Bostrom: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch delegations for Bostrom: %w", err)
}
trungnt1811 marked this conversation as resolved.
Show resolved Hide resolved
log.Printf("Bostrom validator %d has %d delegations", validatorIndex, total)
delegators = append(delegators, delegations...)
}

// Calculate token price and threshold
minimumTokensThreshold, err := utils.GetMinimumTokensThreshold(config.GetBostromConfig().CoinID)
if err != nil {
log.Printf("Failed to fetch Bostrom token price: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch Bostrom token price: %w", err)
}
trungnt1811 marked this conversation as resolved.
Show resolved Hide resolved

rewardInfo := []config.Reward{}
balanceInfo := []banktypes.Balance{}
totalTokenDelegate := sdkmath.LegacyMustNewDecFromStr("0")

// Calculate total tokens delegated
for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfoCustomType(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
totalTokenDelegate = totalTokenDelegate.Add(token)
}

eveAirdrop, err := sdkmath.LegacyNewDecFromStr(config.EveAirdrop)
if err != nil {
log.Printf("Failed to convert EveAirdrop string to dec: %v", err)
return nil, nil, 0, fmt.Errorf("failed to convert EveAirdrop string to dec: %w", err)
}

testAmount := sdkmath.LegacyMustNewDecFromStr("0")

// Calculate rewards and balances for delegators
for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfoCustomType(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := (delegator.Delegation.Shares.MulInt(validatorInfo.Tokens)).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(minimumTokensThreshold) {
continue
}

eveAirdropTokens := (eveAirdrop.MulInt64(int64(config.GetBostromConfig().Percent))).QuoInt64(100).Mul(token).QuoTruncate(totalTokenDelegate)
eveBech32Address, err := utils.ConvertBech32Address(delegator.Delegation.DelegatorAddress)
if err != nil {
log.Printf("Failed to convert Bostrom bech32 address: %v", err)
return nil, nil, 0, fmt.Errorf("failed to convert Bech32Address: %w", err)
}
trungnt1811 marked this conversation as resolved.
Show resolved Hide resolved

rewardInfo = append(rewardInfo, config.Reward{
Address: delegator.Delegation.DelegatorAddress,
EveAddress: eveBech32Address,
Shares: delegator.Delegation.Shares,
Token: token,
EveAirdropToken: eveAirdropTokens,
ChainID: config.GetBostromConfig().ChainID,
})
testAmount = eveAirdropTokens.Add(testAmount)
balanceInfo = append(balanceInfo, banktypes.Balance{
Address: eveBech32Address,
Coins: sdk.NewCoins(sdk.NewCoin("eve", eveAirdropTokens.TruncateInt())),
})
}

log.Println("Bostrom balance: ", testAmount)
return balanceInfo, rewardInfo, len(balanceInfo), nil
}
124 changes: 124 additions & 0 deletions airdrop/chains/celestia.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package chains

import (
"fmt"
"log"

"github.com/eve-network/eve/airdrop/config"
"github.com/eve-network/eve/airdrop/utils"
"github.com/joho/godotenv"

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 Celestia() ([]banktypes.Balance, []config.Reward, int, error) {
// Load environment variables
if err := godotenv.Load(); err != nil {
log.Printf("Error loading Celestial environment variables: %v", err)
return nil, nil, 0, fmt.Errorf("failed to load env: %w", err)
}

// Get the latest block height
blockHeight, err := utils.GetLatestHeight(config.GetCelestiaConfig().RPC + "/status")
if err != nil {
log.Printf("Failed to get latest height for Celestial: %v", err)
return nil, nil, 0, fmt.Errorf("failed to get latest height for Celestia: %w", err)
}

// Setup gRPC connection
grpcConn, err := utils.SetupGRPCConnection(config.GetCelestiaConfig().GRPCAddr)
if err != nil {
log.Printf("Failed to connect to gRPC Celestial: %v", err)
return nil, nil, 0, fmt.Errorf("failed to connect to gRPC Celestia: %w", err)
}
defer grpcConn.Close()
stakingClient := stakingtypes.NewQueryClient(grpcConn)

// Fetch validators
validators, err := utils.GetValidators(stakingClient, blockHeight)
if err != nil {
log.Printf("Failed to get Celestial validators: %v", err)
return nil, nil, 0, fmt.Errorf("failed to get Celestia validators: %w", err)
}
log.Println("Validators: ", len(validators))

// Fetch delegations for each validator
var delegators []stakingtypes.DelegationResponse
for validatorIndex, validator := range validators {
delegations, total, err := utils.FetchDelegations(
config.GetCelestiaConfig().API,
validator.OperatorAddress,
config.LimitPerPage,
)
if err != nil {
log.Printf("Failed to query delegate info for Celestial validator: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch delegations for Celestia: %w", err)
}
log.Println("Validator:", validator.OperatorAddress, "Index:", validatorIndex, "Total:", total)
delegators = append(delegators, delegations...)
}

// Calculate token price and threshold
minimumTokensThreshold, err := utils.GetMinimumTokensThreshold(config.GetCelestiaConfig().CoinID)
if err != nil {
log.Printf("Failed to fetch Celestial token price: %v", err)
return nil, nil, 0, fmt.Errorf("failed to fetch Celestial token price: %w", err)
}

// Process delegations and calculate rewards
totalTokenDelegate := sdkmath.LegacyMustNewDecFromStr("0")
for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := delegator.Delegation.Shares.MulInt(validatorInfo.Tokens).QuoTruncate(validatorInfo.DelegatorShares)
totalTokenDelegate = totalTokenDelegate.Add(token)
}

eveAirdrop, err := sdkmath.LegacyNewDecFromStr(config.EveAirdrop)
if err != nil {
log.Printf("Failed to convert EveAirdrop string to dec: %v", err)
return nil, nil, 0, fmt.Errorf("failed to convert EveAirdrop string to dec: %w", err)
}

var rewardInfo []config.Reward
var balanceInfo []banktypes.Balance
testAmount := sdkmath.LegacyMustNewDecFromStr("0")

for _, delegator := range delegators {
validatorIndex := utils.FindValidatorInfo(validators, delegator.Delegation.ValidatorAddress)
validatorInfo := validators[validatorIndex]
token := delegator.Delegation.Shares.MulInt(validatorInfo.Tokens).QuoTruncate(validatorInfo.DelegatorShares)
if token.LT(minimumTokensThreshold) {
continue
}
eveAirdropToken := eveAirdrop.MulInt64(int64(config.GetCelestiaConfig().Percent)).QuoInt64(100).Mul(token).QuoTruncate(totalTokenDelegate)
eveBech32Address, err := utils.ConvertBech32Address(delegator.Delegation.DelegatorAddress)
if err != nil {
log.Printf("Failed to convert Celestial bech32 address: %v", err)
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: eveAirdropToken,
ChainID: config.GetCelestiaConfig().ChainID,
})

testAmount = testAmount.Add(eveAirdropToken)
balanceInfo = append(balanceInfo, banktypes.Balance{
Address: eveBech32Address,
Coins: sdk.NewCoins(sdk.NewCoin("eve", eveAirdropToken.TruncateInt())),
})
}
trungnt1811 marked this conversation as resolved.
Show resolved Hide resolved

log.Println("Celestia balance: ", testAmount)

return balanceInfo, rewardInfo, len(balanceInfo), nil
}
Loading
Loading