diff --git a/pkg/data/manager.go b/pkg/data/manager.go index 6a3567e..bb65688 100644 --- a/pkg/data/manager.go +++ b/pkg/data/manager.go @@ -4,7 +4,9 @@ import ( "fmt" "main/pkg/tendermint" "main/pkg/types" + "strconv" "sync" + "time" "github.com/rs/zerolog" ) @@ -158,3 +160,136 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { return tallyInfos, nil } + +func (m *Manager) GetChainParams(chain *types.Chain) (*types.ChainWithVotingParams, []error) { + var wg sync.WaitGroup + var mutex sync.Mutex + + errors := make([]error, 0) + params := &types.ParamsResponse{} + + rpc := tendermint.NewRPC(chain, m.Logger) + + wg.Add(3) + + go func() { + defer wg.Done() + + votingParams, err := rpc.GetGovParams("voting") + mutex.Lock() + defer mutex.Unlock() + + if err != nil { + errors = append(errors, err) + return + } + + params.VotingParams = votingParams.VotingParams + }() + + go func() { + defer wg.Done() + + depositParams, err := rpc.GetGovParams("deposit") + mutex.Lock() + defer mutex.Unlock() + + if err != nil { + errors = append(errors, err) + return + } + + params.DepositParams = depositParams.DepositParams + }() + + go func() { + defer wg.Done() + + tallyingParams, err := rpc.GetGovParams("tallying") + mutex.Lock() + defer mutex.Unlock() + + if err != nil { + errors = append(errors, err) + return + } + + params.TallyParams = tallyingParams.TallyParams + }() + + wg.Wait() + + if len(errors) > 0 { + return nil, errors + } + + quorum, err := strconv.ParseFloat(params.TallyParams.Quorum, 64) + if err != nil { + return nil, []error{err} + } + + threshold, err := strconv.ParseFloat(params.TallyParams.Threshold, 64) + if err != nil { + return nil, []error{err} + } + + vetoThreshold, err := strconv.ParseFloat(params.TallyParams.VetoThreshold, 64) + if err != nil { + return nil, []error{err} + } + + votingPeriod, err := time.ParseDuration(params.VotingParams.VotingPeriod) + if err != nil { + return nil, []error{err} + } + + maxDepositPeriod, err := time.ParseDuration(params.DepositParams.MaxDepositPeriod) + if err != nil { + return nil, []error{err} + } + + return &types.ChainWithVotingParams{ + Chain: chain, + VotingPeriod: votingPeriod, + MaxDepositPeriod: maxDepositPeriod, + MinDepositAmount: params.DepositParams.MinDepositAmount, + Quorum: quorum, + Threshold: threshold, + VetoThreshold: vetoThreshold, + }, nil +} + +func (m *Manager) GetParams() (map[string]types.ChainWithVotingParams, error) { + var wg sync.WaitGroup + var mutex sync.Mutex + + params := make(map[string]types.ChainWithVotingParams, 0) + errors := make([]error, 0) + + for _, chain := range m.Chains { + wg.Add(1) + + go func(chain *types.Chain) { + defer wg.Done() + + chainParams, errs := m.GetChainParams(chain) + mutex.Lock() + defer mutex.Unlock() + + if len(errs) > 0 { + errors = append(errors, errs...) + return + } + + params[chainParams.Chain.Name] = *chainParams + }(chain) + } + + wg.Wait() + + if len(errors) > 0 { + return nil, fmt.Errorf("got %d errors when fetching chain params", len(errors)) + } + + return params, nil +} diff --git a/pkg/reporters/telegram/params.go b/pkg/reporters/telegram/params.go new file mode 100644 index 0000000..9f415ee --- /dev/null +++ b/pkg/reporters/telegram/params.go @@ -0,0 +1,36 @@ +package telegram + +import ( + "bytes" + "fmt" + + tele "gopkg.in/telebot.v3" +) + +func (reporter *Reporter) HandleParams(c tele.Context) error { + reporter.Logger.Info(). + Str("sender", c.Sender().Username). + Str("text", c.Text()). + Msg("Got params query") + + params, err := reporter.DataManager.GetParams() + if err != nil { + return reporter.BotReply(c, fmt.Sprintf("Error getting chain params: %s", err)) + } + + template, err := reporter.GetTemplate("params") + if err != nil { + reporter.Logger.Error(). + Err(err). + Msg("Error rendering params template") + return reporter.BotReply(c, "Error rendering params template") + } + + var buffer bytes.Buffer + if err := template.Execute(&buffer, params); err != nil { + reporter.Logger.Error().Err(err).Msg("Error rendering params template") + return err + } + + return reporter.BotReply(c, buffer.String()) +} diff --git a/pkg/reporters/telegram/telegram.go b/pkg/reporters/telegram/telegram.go index 18ae9d9..b0d3243 100644 --- a/pkg/reporters/telegram/telegram.go +++ b/pkg/reporters/telegram/telegram.go @@ -8,6 +8,7 @@ import ( mutes "main/pkg/mutes" "main/pkg/report/entry" "main/pkg/state" + "main/pkg/utils" "strings" "time" @@ -79,6 +80,7 @@ func (reporter *Reporter) Init() error { bot.Handle("/proposals_mutes", reporter.HandleListMutes) bot.Handle("/proposals", reporter.HandleProposals) bot.Handle("/tally", reporter.HandleTally) + bot.Handle("/params", reporter.HandleParams) reporter.TelegramBot = bot go reporter.TelegramBot.Start() @@ -101,7 +103,8 @@ func (reporter *Reporter) GetTemplate(tmlpType string) (*template.Template, erro filename := fmt.Sprintf("%s.html", tmlpType) t, err := template.New(filename).Funcs(template.FuncMap{ - "SerializeLink": reporter.SerializeLink, + "SerializeLink": reporter.SerializeLink, + "FormatDuration": utils.FormatDuration, }).ParseFS(templates.TemplatesFs, "telegram/"+filename) if err != nil { return nil, err diff --git a/pkg/tendermint/tendermint.go b/pkg/tendermint/tendermint.go index 1991b8e..9a09cc5 100644 --- a/pkg/tendermint/tendermint.go +++ b/pkg/tendermint/tendermint.go @@ -152,6 +152,17 @@ func (rpc *RPC) GetStakingPool() (*types.PoolRPCResponse, error) { return &pool, nil } +func (rpc *RPC) GetGovParams(paramsType string) (*types.ParamsResponse, error) { + url := fmt.Sprintf("/cosmos/gov/v1beta1/params/%s", paramsType) + + var pool types.ParamsResponse + if err := rpc.Get(url, &pool); err != nil { + return nil, err + } + + return &pool, nil +} + func (rpc *RPC) Get(url string, target interface{}) error { nodeErrors := make([]error, len(rpc.URLs)) diff --git a/pkg/types/responses.go b/pkg/types/responses.go index db498d5..c992420 100644 --- a/pkg/types/responses.go +++ b/pkg/types/responses.go @@ -1,6 +1,7 @@ package types import ( + "fmt" "strings" "time" @@ -149,3 +150,58 @@ type PoolRPCResponse struct { type Pool struct { BondedTokens math.LegacyDec `json:"bonded_tokens"` } + +type ParamsResponse struct { + VotingParams VotingParams `json:"voting_params"` + DepositParams DepositParams `json:"deposit_params"` + TallyParams TallyParams `json:"tally_params"` +} + +type VotingParams struct { + VotingPeriod string `json:"voting_period"` +} + +type DepositParams struct { + MinDepositAmount []Amount `json:"min_deposit"` + MaxDepositPeriod string `json:"max_deposit_period"` +} + +type Amount struct { + Denom string `json:"denom"` + Amount string `json:"amount"` +} + +type TallyParams struct { + Quorum string `json:"quorum"` + Threshold string `json:"threshold"` + VetoThreshold string `json:"veto_threshold"` +} + +type ChainWithVotingParams struct { + Chain *Chain + VotingPeriod time.Duration `json:"voting_period"` + MinDepositAmount []Amount `json:"amount"` + MaxDepositPeriod time.Duration `json:"max_deposit_period"` + Quorum float64 `json:"quorum"` + Threshold float64 `json:"threshold"` + VetoThreshold float64 `json:"veto_threshold"` +} + +func (c ChainWithVotingParams) FormatQuorum() string { + return fmt.Sprintf("%.2f%%", c.Quorum*100) +} + +func (c ChainWithVotingParams) FormatThreshold() string { + return fmt.Sprintf("%.2f%%", c.Threshold*100) +} + +func (c ChainWithVotingParams) FormatVetoThreshold() string { + return fmt.Sprintf("%.2f%%", c.VetoThreshold*100) +} + +func (c ChainWithVotingParams) FormatMinDepositAmount() string { + amountsAsStrings := utils.Map(c.MinDepositAmount, func(a Amount) string { + return a.Amount + " " + a.Denom + }) + return strings.Join(amountsAsStrings, ",") +} diff --git a/templates/telegram/params.html b/templates/telegram/params.html new file mode 100644 index 0000000..68a7f5f --- /dev/null +++ b/templates/telegram/params.html @@ -0,0 +1,9 @@ +{{- range $chainName, $params := . }} +Params of chain {{ $params.Chain.GetName }}: +Voting period: {{ FormatDuration $params.VotingPeriod }} +Max deposit period: {{ FormatDuration $params.MaxDepositPeriod }} +Min deposit amount: {{ $params.FormatMinDepositAmount }} +Quorum: {{ $params.FormatQuorum }} +Threshold: {{ $params.FormatThreshold }} +Veto threshold: {{ $params.FormatVetoThreshold }} +{{ end }}