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

feat: alliance initial delegations #48

Merged
merged 4 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
#################################################
### START ###
#################################################
start-alliance-initial-delegation:
go run ./cmd/feeder/feeder.go alliance-initial-delegation

start-alliance-oracle-feeder:
go run ./cmd/feeder/feeder.go alliance-oracle-feeder

Expand Down
8 changes: 8 additions & 0 deletions cmd/price-server/price_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ func main() {
}
c.JSON(http.StatusOK, allianceRebalanceVals)
})
r.GET("/alliance/delegations", func(c *gin.Context) {
allianceDelegatios, err := allianceProvider.GetAllianceInitialDelegations(ctx)
if err != nil {
c.JSON(http.StatusInternalServerError, err)
return
}
c.JSON(http.StatusOK, allianceDelegatios)
})
if os.Getenv("PRICE_SERVER_PORT") == "" {
os.Setenv("PORT", "8532") // use 8532 by default
} else {
Expand Down
2 changes: 1 addition & 1 deletion config/alliance_default_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package config
var PHOENIX_GRPC = "terra-grpc.polkachu.com:11790"
var MIGALOO_GRPC = "migaloo-grpc.polkachu.com:20790"
var KUJIRA_GRPC = "kujira-grpc.polkachu.com:11890"
var CARBON_GRPC = "query-grpc.carbon.network:443"
var CARBON_GRPC = "carbon-grpc.terra.dev:443"

var AllianceDefaultConfig = AllianceConfig{
GRPCUrls: []string{MIGALOO_GRPC, KUJIRA_GRPC, CARBON_GRPC},
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/terra-money/oracle-feeder-go
go 1.20

require (
cosmossdk.io/math v1.0.0-rc.0
github.com/CosmWasm/wasmd v1.0.0
github.com/cosmos/cosmos-sdk v0.46.13
github.com/gin-gonic/gin v1.9.0
Expand All @@ -18,6 +17,7 @@ require (

require (
cosmossdk.io/errors v1.0.0-beta.7 // indirect
cosmossdk.io/math v1.0.0-rc.0 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
Expand Down
286 changes: 0 additions & 286 deletions go.sum

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions internal/provider/alliance/alliance_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,7 @@ func (p *allianceProvider) GetProtocolsInfo(ctx context.Context) (*types.MsgUpda
func (p *allianceProvider) GetAllianceRedelegateReq(ctx context.Context) (*types.MsgAllianceRedelegate, error) {
return p.allianceValidatorsProvider.GetAllianceRedelegateReq(ctx)
}

func (p *allianceProvider) GetAllianceInitialDelegations(ctx context.Context) (*types.MsgAllianceDelegations, error) {
return p.allianceValidatorsProvider.GetAllianceInitialDelegations(ctx)
}
180 changes: 142 additions & 38 deletions internal/provider/alliance/alliance_validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"net/http"
"os"
"sort"
"strconv"
"strings"

Expand All @@ -21,7 +22,6 @@ import (
"github.com/cosmos/cosmos-sdk/types/query"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
alliancetypes "github.com/terra-money/alliance/x/alliance/types"
)
Expand All @@ -31,13 +31,19 @@ type allianceValidatorsProvider struct {
config *config.AllianceConfig
providerManager *provider.ProviderManager
nodeGrpcUrl string
terraLcdUrl string
stationApiUrl string
allianceHubContractAddress string
blocksToBeSeniorValidator int64
voteOnProposalsToBeSeniorValidator int64
}

func NewAllianceValidatorsProvider(config *config.AllianceConfig, providerManager *provider.ProviderManager) *allianceValidatorsProvider {
var terraLcdUrl string
if terraLcdUrl = os.Getenv("TERRA_LCD_URL"); len(terraLcdUrl) == 0 {
panic("TERRA_LCD_URL env variable is not set!")
}

var nodeGrpcUrl string
if nodeGrpcUrl = os.Getenv("NODE_GRPC_URL"); len(nodeGrpcUrl) == 0 {
panic("NODE_GRPC_URL env variable is not set!")
Expand Down Expand Up @@ -78,6 +84,7 @@ func NewAllianceValidatorsProvider(config *config.AllianceConfig, providerManage
return &allianceValidatorsProvider{
BaseGrpc: *internal.NewBaseGrpc(),
config: config,
terraLcdUrl: terraLcdUrl,
nodeGrpcUrl: nodeGrpcUrl,
stationApiUrl: stationApiUrl,
providerManager: providerManager,
Expand All @@ -87,6 +94,47 @@ func NewAllianceValidatorsProvider(config *config.AllianceConfig, providerManage
}
}

// Query Terra GRPC, station API and return the list of alliance
// redelegations that can be submitted to Alliance Hub applying
// the following rules:
// (1) - be part of the active validator set,
// (2) - to do not be jailed,
// (3) - commission rate to be lower than 10%,
// (4) - Participate in the latest 3 gov proposals,
// (5) - have been in the active validator set 100 000 blocks before the current one, (1 week approx)
func (p *allianceValidatorsProvider) GetAllianceInitialDelegations(ctx context.Context) (*pkgtypes.MsgAllianceDelegations, error) {
smartContractRes, err := p.querySmartContractConfig(ctx)
if err != nil {
return nil, err
}

stakingValidators, seniorValidators, proposalsVotes, _, err := p.queryValidatorsData(ctx)
if err != nil {
return nil, err
}
compliantVals := p.GetCompliantValidators(stakingValidators, proposalsVotes, seniorValidators)
allianceTokenSupply, err := strconv.ParseInt(smartContractRes.AllianceTokenSupply, 10, 64)
if err != nil {
panic(err)
}
tokensPerVal := allianceTokenSupply / int64(len(compliantVals))

var delegations []types.Delegation
for i := 0; i < len(compliantVals); i++ {
delegations = append(
delegations,
types.NewDelegation(
compliantVals[i].OperatorAddress,
fmt.Sprint(tokensPerVal),
),
)
}

res := pkgtypes.NewMsgAllianceDelegations(delegations)

return &res, nil
}

// Query Terra GRPC, station API and return the list of alliance
// redelegations that can be submitted to Alliance Hub applying
// the following rules:
Expand All @@ -106,11 +154,38 @@ func (p *allianceValidatorsProvider) GetAllianceRedelegateReq(ctx context.Contex
return nil, err
}

// Apply the previous rules to filter the list of validators
compliantVals := p.GetCompliantValidators(stakingValidators, proposalsVotes, seniorValidators)

valsWithAllianceTokens, totalAllianceStakedTokens := FilterAllianceValsWithStake(allianceVals, smartContractRes.AllianceTokenDenom)
compliantValsWithAllianceTokens,
nonCompliantValsWithAllianceTokens := ParseAllianceValsByCompliance(compliantVals, valsWithAllianceTokens, smartContractRes.AllianceTokenDenom)
avgTokensPerCompliantVal := totalAllianceStakedTokens.Quo(sdktypes.NewDec(int64(len(compliantVals))))

redelegations := RebalanceVals(
compliantValsWithAllianceTokens,
nonCompliantValsWithAllianceTokens,
avgTokensPerCompliantVal,
)

res := pkgtypes.NewMsgAllianceRedelegate(redelegations)

return &res, nil
}

// Apply the rules to filter the list of validators
// (1) - be part of the active validator set,
// (2) - to do not be jailed,
// (3) - commission rate to be lower than 10%,
// (4) - Participate in the latest 3 gov proposals,
// (5) - have been in the active validator set 100 000 blocks before the current one, (1 week approx)
func (p *allianceValidatorsProvider) GetCompliantValidators(stakingValidators []stakingtypes.Validator, proposalsVotes []types.StationVote, seniorValidators []*tmservice.Validator) []stakingtypes.Validator {
var compliantVals []stakingtypes.Validator
// Apply the previous rules to filter the list

// of all blockchain validators to keep the ones
// that are compliant
for _, val := range stakingValidators {

// (1) skip if status is not bonded (again in case the api have a bug with the query)
if val.GetStatus() != stakingtypes.Bonded {
continue
Expand All @@ -131,30 +206,16 @@ func (p *allianceValidatorsProvider) GetAllianceRedelegateReq(ctx context.Contex
continue
}

// (5) skip if it have not been in the active validator set 100 000 blocks before the current one
for _, seniorValidator := range seniorValidators {
if val.OperatorAddress != seniorValidator.Address {
// (5) if the validator is a senior one add to the array
if seniorValidator.PubKey.Equal(val.ConsensusPubkey) {
compliantVals = append(compliantVals, val)
continue
}
}

compliantVals = append(compliantVals, val)
}

valsWithAllianceTokens, totalAllianceStakedTokens := FilterAllianceValsWithStake(allianceVals, smartContractRes.AllianceTokenDenom)
compliantValsWithAllianceTokens,
nonCompliantValsWithAllianceTokens := ParseAllianceValsByCompliance(compliantVals, valsWithAllianceTokens, smartContractRes.AllianceTokenDenom)
avgTokensPerCompliantVal := totalAllianceStakedTokens.Quo(sdktypes.NewDec(int64(len(compliantVals))))

redelegations := RebalanceVals(
compliantValsWithAllianceTokens,
nonCompliantValsWithAllianceTokens,
avgTokensPerCompliantVal,
)

res := pkgtypes.NewMsgAllianceRedelegate(redelegations)

return &res, nil
return compliantVals
}

// In charge of rebalancing the stake from non-compliant validators to compliant ones.
Expand Down Expand Up @@ -366,7 +427,6 @@ func (p *allianceValidatorsProvider) queryValidatorsData(ctx context.Context) (
defer grpcConn.Close()

nodeClient := tmservice.NewServiceClient(grpcConn)
govClient := govtypes.NewQueryClient(grpcConn)
stakingClient := stakingtypes.NewQueryClient(grpcConn)
allianceClient := alliancetypes.NewQueryClient(grpcConn)

Expand All @@ -381,17 +441,6 @@ func (p *allianceValidatorsProvider) queryValidatorsData(ctx context.Context) (
return nil, nil, nil, nil, err
}

govPropsRes, err := govClient.Proposals(ctx, &govtypes.QueryProposalsRequest{
Pagination: &query.PageRequest{
Limit: 3,
Reverse: true,
},
})
if err != nil {
fmt.Printf("govPropsRes: %v \n", err)
return nil, nil, nil, nil, err
}

latestHeightRes, err := nodeClient.GetLatestBlock(ctx, &tmservice.GetLatestBlockRequest{})
if err != nil {
fmt.Printf("latestHeightRes: %v \n", err)
Expand All @@ -406,7 +455,7 @@ func (p *allianceValidatorsProvider) queryValidatorsData(ctx context.Context) (
return nil, nil, nil, nil, err
}

proposalsVotesRes, err := p.getProposalsVotesFromStationAPI(ctx, govPropsRes.Proposals)
proposalsVotesRes, err := p.getProposals(ctx)
if err != nil {
fmt.Printf("proposalsVotesRes: %v \n", err)
return nil, nil, nil, nil, err
Expand All @@ -429,19 +478,74 @@ func (p *allianceValidatorsProvider) queryValidatorsData(ctx context.Context) (
nil
}

func (p *allianceValidatorsProvider) getProposalsVotesFromStationAPI(ctx context.Context, proposals []*govtypes.Proposal) (stationProposals []types.StationVote, err error) {
for _, proposal := range proposals {
stationProposalsRes, err := p.queryStation(proposal.Id)
func (p *allianceValidatorsProvider) getProposals(ctx context.Context) (stationProposals []types.StationVote, err error) {
passedProposalsUrl := "/cosmos/gov/v1/proposals?proposal_status=3&pagination.limit=2&pagination.reverse=true"
rejectedProposalsUrl := "/cosmos/gov/v1/proposals?proposal_status=4&pagination.limit=2&pagination.reverse=true"
passedProposalsIDs, err := p.queryProposalIDs(passedProposalsUrl)
if err != nil {
return stationProposals, err
}
rejectedProposalsIDs, err := p.queryProposalIDs(rejectedProposalsUrl)
if err != nil {
return stationProposals, err
}

// Merge the passed and rejected proposals IDs
// and sort them to get the latest 3 proposals
proposalsIDs := append(passedProposalsIDs, rejectedProposalsIDs...)
sort.Slice(proposalsIDs, func(i, j int) bool {
return proposalsIDs[i] > proposalsIDs[j]
})
proposalsIDs = proposalsIDs[:3]

// Iterate over the proposals and request the latest votes
for _, proposalID := range proposalsIDs {
stationProposalsRes, err := p.queryStation(proposalID)
if err != nil {
return stationProposals, err
}
stationProposals = append(stationProposals, *stationProposalsRes...)
}

return stationProposals, err
}

func (p allianceValidatorsProvider) queryStation(propId uint64) (res *[]types.StationVote, err error) {
func (p allianceValidatorsProvider) queryProposalIDs(urlSuffix string) (proposalIDs []int64, err error) {
url := p.terraLcdUrl + urlSuffix

// Send GET request
res, err := http.Get(url)
if err != nil {
return nil, err
}
defer res.Body.Close()

// Read response body
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, err
}
var resGov *types.GovRes

// Parse JSON response into struct
err = json.Unmarshal(body, &resGov)
if err != nil {
return nil, err
}

for _, proposal := range resGov.Proposals {
propID, err := strconv.ParseInt(proposal.Id, 10, 64)
if err != nil {
fmt.Println("Error:", err)
return nil, err
}
proposalIDs = append(proposalIDs, propID)
}

// Access parsed data
return proposalIDs, nil
}

func (p allianceValidatorsProvider) queryStation(propId int64) (res *[]types.StationVote, err error) {
url := p.stationApiUrl + "/proposals/" + fmt.Sprint(propId)
// Send GET request
resp, err := http.Get(url)
Expand Down
Loading
Loading