diff --git a/Makefile b/Makefile index 46208b4..9d1bf29 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/cmd/price-server/price_server.go b/cmd/price-server/price_server.go index 9b17b3b..f8aff9a 100644 --- a/cmd/price-server/price_server.go +++ b/cmd/price-server/price_server.go @@ -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 { diff --git a/internal/provider/alliance/alliance_provider.go b/internal/provider/alliance/alliance_provider.go index cc499ef..253e5e5 100644 --- a/internal/provider/alliance/alliance_provider.go +++ b/internal/provider/alliance/alliance_provider.go @@ -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) +} diff --git a/internal/provider/alliance/alliance_validators.go b/internal/provider/alliance/alliance_validators.go index 211d223..3a8c22f 100644 --- a/internal/provider/alliance/alliance_validators.go +++ b/internal/provider/alliance/alliance_validators.go @@ -87,6 +87,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: @@ -106,11 +147,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 @@ -131,8 +199,8 @@ 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 { + // (5) skip if it have not been in the active validator set 100 000 blocks before the current one if val.OperatorAddress != seniorValidator.Address { continue } @@ -140,21 +208,7 @@ func (p *allianceValidatorsProvider) GetAllianceRedelegateReq(ctx context.Contex 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. diff --git a/internal/provider/transactions_provider.go b/internal/provider/transactions_provider.go index ce75049..e089f9a 100644 --- a/internal/provider/transactions_provider.go +++ b/internal/provider/transactions_provider.go @@ -252,7 +252,8 @@ func (p *TransactionsProvider) getTxClients() (client.TxBuilder, client.TxConfig func (p *TransactionsProvider) getContractAddress() string { if p.feederType == types.AllianceUpdateRewards || p.feederType == types.AllianceRebalanceEmissions || - p.feederType == types.AllianceRebalanceFeeder { + p.feederType == types.AllianceRebalanceFeeder || + p.feederType == types.AllianceInitialDelegation { return p.allianceHubContractAddress } else if p.feederType == types.AllianceOracleFeeder { return p.oracleAddress diff --git a/internal/types/alliance_redelegate.go b/internal/types/alliance_redelegate.go index 20dda5a..fa7419e 100644 --- a/internal/types/alliance_redelegate.go +++ b/internal/types/alliance_redelegate.go @@ -2,6 +2,18 @@ package types import "github.com/cosmos/cosmos-sdk/types" +type Delegation struct { + Validator string `json:"validator"` + Amount string `json:"amount"` +} + +func NewDelegation(validator string, amount string) Delegation { + return Delegation{ + Validator: validator, + Amount: amount, + } +} + type Redelegation struct { SrcValidator string `json:"src_validator"` DstValidator string `json:"dst_validator"` diff --git a/internal/types/feeder_type.go b/internal/types/feeder_type.go index 2228114..6d9784e 100644 --- a/internal/types/feeder_type.go +++ b/internal/types/feeder_type.go @@ -5,6 +5,7 @@ import "fmt" type FeederType string const ( + AllianceInitialDelegation FeederType = "alliance-initial-delegation" AllianceUpdateRewards FeederType = "alliance-update-rewards" AllianceRebalanceEmissions FeederType = "alliance-rebalance-emissions" AllianceOracleFeeder FeederType = "alliance-oracle-feeder" @@ -14,6 +15,8 @@ const ( // parse from string to FeederType func ParseFeederTypeFromString(s string) (FeederType, error) { switch s { + case string(AllianceInitialDelegation): + return AllianceInitialDelegation, nil case string(AllianceUpdateRewards): return AllianceUpdateRewards, nil case string(AllianceRebalanceEmissions): @@ -38,6 +41,8 @@ func FromFeederTypeToPriceServerUrl(feederType FeederType) string { return "/alliance/protocol" case AllianceRebalanceFeeder: return "/alliance/rebalance" + case AllianceInitialDelegation: + return "/alliance/rebalance" default: return "" } diff --git a/params.json b/params.json index 3ca9463..07558b5 100644 --- a/params.json +++ b/params.json @@ -9,8 +9,8 @@ }, "develop": { "price_server_port": "8532", - "oracle_address": "terra1jf3nndysevley5p3wnajkjvjxcql9d00gpj4en3xwp7yrkrdqess48rr27", - "alliance_hub_contract_address": "terra1majrm6e6n0eg760n9fs4g5jvwzh4ytp8e2d99mfgzv2e7mjmdwxse0ty73", + "oracle_address": "terra19cs0qcqpa5n7wlqjlhvfe235kc20g52enwr0d5cdar5zkmza7skqv54070", + "alliance_hub_contract_address": "terra1q95pe55eea0akft0xezak2s50l4vkkquve5emw7gzw65a7ptdl8qel50ea", "feeder_retries": 4, "blocks_to_be_senior_validator": 10000, "vote_on_proposals_to_be_senior_validator": 0 diff --git a/pkg/types/execute.go b/pkg/types/execute.go index 117b088..aad47a9 100644 --- a/pkg/types/execute.go +++ b/pkg/types/execute.go @@ -20,6 +20,21 @@ func NewMsgUpdateChainsInfo(data types.AllianceProtocolRes) MsgUpdateChainsInfo } } +type AllianceDelegations struct { + AllianceDelegations []types.Delegation `json:"delegations"` +} +type MsgAllianceDelegations struct { + AllianceDelegations AllianceDelegations `json:"alliance_delegate"` +} + +func NewMsgAllianceDelegations(dedelegations []types.Delegation) MsgAllianceDelegations { + return MsgAllianceDelegations{ + AllianceDelegations: AllianceDelegations{ + AllianceDelegations: dedelegations, + }, + } +} + type AllianceRedelegate struct { Redelegations []types.Redelegation `json:"redelegations"` }