From 776a5cc26879fc7cdcf56665cbdb547d605adf00 Mon Sep 17 00:00:00 2001 From: Techno Freak Date: Thu, 21 Dec 2023 02:50:57 +0300 Subject: [PATCH 1/4] chore: refactor tally querying and types --- pkg/data/manager.go | 108 +------------------ pkg/fetchers/cosmos/fetcher.go | 129 +---------------------- pkg/fetchers/cosmos/proposals_v1.go | 49 +++++++++ pkg/fetchers/cosmos/proposals_v1beta1.go | 49 +++++++++ pkg/fetchers/cosmos/responses/tally.go | 29 +++++ pkg/fetchers/cosmos/tally.go | 121 +++++++++++++++++++++ pkg/fetchers/cosmos/vote.go | 47 +++++++++ pkg/types/responses.go | 13 --- pkg/types/tally.go | 63 +++++++++++ pkg/types/types.go | 66 ------------ templates/telegram/tally.html | 9 +- 11 files changed, 371 insertions(+), 312 deletions(-) create mode 100644 pkg/fetchers/cosmos/proposals_v1.go create mode 100644 pkg/fetchers/cosmos/proposals_v1beta1.go create mode 100644 pkg/fetchers/cosmos/responses/tally.go create mode 100644 pkg/fetchers/cosmos/tally.go create mode 100644 pkg/fetchers/cosmos/vote.go create mode 100644 pkg/types/tally.go diff --git a/pkg/data/manager.go b/pkg/data/manager.go index 3e1510a..c74d667 100644 --- a/pkg/data/manager.go +++ b/pkg/data/manager.go @@ -28,10 +28,7 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { var mutex sync.Mutex errors := make([]error, 0) - - pools := make(map[string]types.Pool, 0) - proposals := make(map[string][]types.Proposal, 0) - tallies := make(map[string]map[string]types.Tally, 0) + tallies := make(map[string]types.ChainTallyInfos, 0) for _, chain := range m.Chains { rpc := cosmos.NewRPC(chain, m.Logger) @@ -40,81 +37,18 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { go func(c *types.Chain, rpc *cosmos.RPC) { defer wg.Done() - pool, err := rpc.GetStakingPool() + talliesForChain, err := rpc.GetTallies() mutex.Lock() if err != nil { m.Logger.Error().Err(err).Str("chain", c.Name).Msg("Error fetching staking pool") errors = append(errors, err) - } else if pool.Pool == nil { - m.Logger.Error().Err(err).Str("chain", c.Name).Msg("Staking pool is empty!") - errors = append(errors, fmt.Errorf("staking pool is empty")) } else { - pools[c.Name] = *pool.Pool + tallies[c.Name] = talliesForChain } mutex.Unlock() }(chain, rpc) - - wg.Add(1) - go func(c *types.Chain, rpc *cosmos.RPC) { - defer wg.Done() - - chainProposals, err := rpc.GetAllProposals() - - mutex.Lock() - - if err != nil { - m.Logger.Error().Err(err).Str("chain", c.Name).Msg("Error fetching chain proposals") - errors = append(errors, err) - - mutex.Unlock() - return - } else { - proposals[c.Name] = chainProposals - } - - mutex.Unlock() - - var internalWg sync.WaitGroup - - for _, proposal := range chainProposals { - internalWg.Add(1) - - go func(c *types.Chain, p types.Proposal) { - defer internalWg.Done() - - tally, err := rpc.GetTally(p.ID) - - mutex.Lock() - defer mutex.Unlock() - - if err != nil { - m.Logger.Error(). - Err(err). - Str("chain", c.Name). - Str("proposal_id", p.ID). - Msg("Error fetching tally for proposal") - errors = append(errors, err) - } else if tally.Tally == nil { - m.Logger.Error(). - Err(err). - Str("chain", c.Name). - Str("proposal_id", p.ID). - Msg("Tally is empty") - errors = append(errors, fmt.Errorf("tally is empty")) - } else { - if _, ok := tallies[c.Name]; !ok { - tallies[c.Name] = make(map[string]types.Tally, 0) - } - - tallies[c.Name][p.ID] = *tally.Tally - } - }(c, proposal) - } - - internalWg.Wait() - }(chain, rpc) } wg.Wait() @@ -124,41 +58,7 @@ func (m *Manager) GetTallies() (map[string]types.ChainTallyInfos, error) { return map[string]types.ChainTallyInfos{}, fmt.Errorf("could not get tallies info: got %d errors", len(errors)) } - tallyInfos := make(map[string]types.ChainTallyInfos, 0) - - for chainName, chainProposals := range proposals { - chain := m.Chains.FindByName(chainName) - if chain == nil { - return map[string]types.ChainTallyInfos{}, fmt.Errorf("could not chain with name %s", chainName) - } - - if _, ok := tallyInfos[chainName]; !ok { - tallyInfos[chainName] = types.ChainTallyInfos{ - Chain: chain, - TallyInfos: make([]types.TallyInfo, len(chainProposals)), - } - } - - for index, proposal := range chainProposals { - tally, ok := tallies[chainName][proposal.ID] - if !ok { - return map[string]types.ChainTallyInfos{}, fmt.Errorf("could not get tallies info") - } - - pool, ok := pools[chainName] - if !ok { - return map[string]types.ChainTallyInfos{}, fmt.Errorf("could not get tallies info") - } - - tallyInfos[chainName].TallyInfos[index] = types.TallyInfo{ - Proposal: proposal, - Tally: tally, - Pool: pool, - } - } - } - - return tallyInfos, nil + return tallies, nil } func (m *Manager) GetChainParams(chain *types.Chain) (*types.ChainWithVotingParams, []error) { diff --git a/pkg/fetchers/cosmos/fetcher.go b/pkg/fetchers/cosmos/fetcher.go index 2366135..476ea38 100644 --- a/pkg/fetchers/cosmos/fetcher.go +++ b/pkg/fetchers/cosmos/fetcher.go @@ -2,12 +2,9 @@ package cosmos import ( "encoding/json" - "errors" "fmt" "main/pkg/fetchers/cosmos/responses" - "main/pkg/utils" "net/http" - "strings" "time" "main/pkg/types" @@ -18,6 +15,7 @@ import ( const PaginationLimit = 1000 type RPC struct { + ChainConfig *types.Chain URLs []string ProposalsType string Logger zerolog.Logger @@ -25,6 +23,7 @@ type RPC struct { func NewRPC(chainConfig *types.Chain, logger zerolog.Logger) *RPC { return &RPC{ + ChainConfig: chainConfig, URLs: chainConfig.LCDEndpoints, ProposalsType: chainConfig.ProposalsType, Logger: logger.With().Str("component", "rpc").Logger(), @@ -39,131 +38,13 @@ func (rpc *RPC) GetAllProposals() ([]types.Proposal, *types.QueryError) { return rpc.GetAllV1beta1Proposals() } -func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, *types.QueryError) { - proposals := []types.Proposal{} - offset := 0 - - for { - url := fmt.Sprintf( - // 2 is for PROPOSAL_STATUS_VOTING_PERIOD - "/cosmos/gov/v1beta1/proposals?pagination.limit=%d&pagination.offset=%d&proposal_status=2", - PaginationLimit, - offset, - ) - - var batchProposals responses.V1Beta1ProposalsRPCResponse - if errs := rpc.Get(url, &batchProposals); len(errs) > 0 { - return nil, &types.QueryError{ - QueryError: nil, - NodeErrors: errs, - } - } - - if batchProposals.Message != "" { - return nil, &types.QueryError{ - QueryError: errors.New(batchProposals.Message), - } - } - - parsedProposals := utils.Map(batchProposals.Proposals, func(p responses.V1beta1Proposal) types.Proposal { - return p.ToProposal() - }) - proposals = append(proposals, parsedProposals...) - if len(batchProposals.Proposals) < PaginationLimit { - break - } - - offset += PaginationLimit - } - - return proposals, nil -} - -func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, *types.QueryError) { - proposals := []types.Proposal{} - offset := 0 - - for { - url := fmt.Sprintf( - // 2 is for PROPOSAL_STATUS_VOTING_PERIOD - "/cosmos/gov/v1/proposals?pagination.limit=%d&pagination.offset=%d&proposal_status=2", - PaginationLimit, - offset, - ) - - var batchProposals responses.V1ProposalsRPCResponse - if errs := rpc.Get(url, &batchProposals); len(errs) > 0 { - return nil, &types.QueryError{ - QueryError: nil, - NodeErrors: errs, - } - } - - if batchProposals.Message != "" { - return nil, &types.QueryError{ - QueryError: errors.New(batchProposals.Message), - } - } - - parsedProposals := utils.Map(batchProposals.Proposals, func(p responses.V1Proposal) types.Proposal { - return p.ToProposal() - }) - proposals = append(proposals, parsedProposals...) - if len(batchProposals.Proposals) < PaginationLimit { - break - } - - offset += PaginationLimit - } - - return proposals, nil -} - -func (rpc *RPC) GetVote(proposal, voter string) (*types.Vote, *types.QueryError) { - url := fmt.Sprintf( - "/cosmos/gov/v1beta1/proposals/%s/votes/%s", - proposal, - voter, - ) - - var vote responses.VoteRPCResponse - if errs := rpc.Get(url, &vote); len(errs) > 0 { - return nil, &types.QueryError{ - QueryError: nil, - NodeErrors: errs, - } - } - - if vote.IsError() { - // not voted - if strings.Contains(vote.Message, "not found") { - return nil, nil - } - - // some other errors - return nil, &types.QueryError{ - QueryError: errors.New(vote.Message), - } - } - - voteParsed, err := vote.ToVote() - if err != nil { - return nil, &types.QueryError{ - QueryError: err, - NodeErrors: nil, - } - } - - return voteParsed, nil -} - -func (rpc *RPC) GetTally(proposal string) (*types.TallyRPCResponse, *types.QueryError) { +func (rpc *RPC) GetTally(proposal string) (*types.Tally, *types.QueryError) { url := fmt.Sprintf( "/cosmos/gov/v1beta1/proposals/%s/tally", proposal, ) - var tally types.TallyRPCResponse + var tally responses.TallyRPCResponse if errs := rpc.Get(url, &tally); len(errs) > 0 { return nil, &types.QueryError{ QueryError: nil, @@ -171,7 +52,7 @@ func (rpc *RPC) GetTally(proposal string) (*types.TallyRPCResponse, *types.Query } } - return &tally, nil + return tally.Tally.ToTally(), nil } func (rpc *RPC) GetStakingPool() (*types.PoolRPCResponse, *types.QueryError) { diff --git a/pkg/fetchers/cosmos/proposals_v1.go b/pkg/fetchers/cosmos/proposals_v1.go new file mode 100644 index 0000000..dfe8278 --- /dev/null +++ b/pkg/fetchers/cosmos/proposals_v1.go @@ -0,0 +1,49 @@ +package cosmos + +import ( + "errors" + "fmt" + "main/pkg/fetchers/cosmos/responses" + "main/pkg/types" + "main/pkg/utils" +) + +func (rpc *RPC) GetAllV1Proposals() ([]types.Proposal, *types.QueryError) { + proposals := []types.Proposal{} + offset := 0 + + for { + url := fmt.Sprintf( + // 2 is for PROPOSAL_STATUS_VOTING_PERIOD + "/cosmos/gov/v1/proposals?pagination.limit=%d&pagination.offset=%d&proposal_status=2", + PaginationLimit, + offset, + ) + + var batchProposals responses.V1ProposalsRPCResponse + if errs := rpc.Get(url, &batchProposals); len(errs) > 0 { + return nil, &types.QueryError{ + QueryError: nil, + NodeErrors: errs, + } + } + + if batchProposals.Message != "" { + return nil, &types.QueryError{ + QueryError: errors.New(batchProposals.Message), + } + } + + parsedProposals := utils.Map(batchProposals.Proposals, func(p responses.V1Proposal) types.Proposal { + return p.ToProposal() + }) + proposals = append(proposals, parsedProposals...) + if len(batchProposals.Proposals) < PaginationLimit { + break + } + + offset += PaginationLimit + } + + return proposals, nil +} diff --git a/pkg/fetchers/cosmos/proposals_v1beta1.go b/pkg/fetchers/cosmos/proposals_v1beta1.go new file mode 100644 index 0000000..7dfbbf9 --- /dev/null +++ b/pkg/fetchers/cosmos/proposals_v1beta1.go @@ -0,0 +1,49 @@ +package cosmos + +import ( + "errors" + "fmt" + "main/pkg/fetchers/cosmos/responses" + "main/pkg/types" + "main/pkg/utils" +) + +func (rpc *RPC) GetAllV1beta1Proposals() ([]types.Proposal, *types.QueryError) { + proposals := []types.Proposal{} + offset := 0 + + for { + url := fmt.Sprintf( + // 2 is for PROPOSAL_STATUS_VOTING_PERIOD + "/cosmos/gov/v1beta1/proposals?pagination.limit=%d&pagination.offset=%d&proposal_status=2", + PaginationLimit, + offset, + ) + + var batchProposals responses.V1Beta1ProposalsRPCResponse + if errs := rpc.Get(url, &batchProposals); len(errs) > 0 { + return nil, &types.QueryError{ + QueryError: nil, + NodeErrors: errs, + } + } + + if batchProposals.Message != "" { + return nil, &types.QueryError{ + QueryError: errors.New(batchProposals.Message), + } + } + + parsedProposals := utils.Map(batchProposals.Proposals, func(p responses.V1beta1Proposal) types.Proposal { + return p.ToProposal() + }) + proposals = append(proposals, parsedProposals...) + if len(batchProposals.Proposals) < PaginationLimit { + break + } + + offset += PaginationLimit + } + + return proposals, nil +} diff --git a/pkg/fetchers/cosmos/responses/tally.go b/pkg/fetchers/cosmos/responses/tally.go new file mode 100644 index 0000000..10bdb7a --- /dev/null +++ b/pkg/fetchers/cosmos/responses/tally.go @@ -0,0 +1,29 @@ +package responses + +import ( + "main/pkg/types" + + "cosmossdk.io/math" +) + +type TallyRPCResponse struct { + Code int64 `json:"code"` + Message string `json:"message"` + Tally *Tally `json:"tally"` +} + +type Tally struct { + Yes math.LegacyDec `json:"yes"` + No math.LegacyDec `json:"no"` + NoWithVeto math.LegacyDec `json:"no_with_veto"` + Abstain math.LegacyDec `json:"abstain"` +} + +func (t Tally) ToTally() *types.Tally { + return &types.Tally{ + {Option: "Yes", Voted: t.Yes}, + {Option: "No", Voted: t.No}, + {Option: "Abstain", Voted: t.Abstain}, + {Option: "No with veto", Voted: t.NoWithVeto}, + } +} diff --git a/pkg/fetchers/cosmos/tally.go b/pkg/fetchers/cosmos/tally.go new file mode 100644 index 0000000..8f28fe6 --- /dev/null +++ b/pkg/fetchers/cosmos/tally.go @@ -0,0 +1,121 @@ +package cosmos + +import ( + "fmt" + "main/pkg/types" + "sync" + + "cosmossdk.io/math" +) + +func (rpc *RPC) GetTallies() (types.ChainTallyInfos, error) { + var wg sync.WaitGroup + var mutex sync.Mutex + + errors := make([]error, 0) + + var pool math.LegacyDec + var proposals []types.Proposal + tallies := make(map[string]types.Tally, 0) + + wg.Add(1) + go func() { + defer wg.Done() + + poolResponse, err := rpc.GetStakingPool() + + mutex.Lock() + + if err != nil { + rpc.Logger.Error().Err(err).Msg("Error fetching staking pool") + errors = append(errors, err) + } else if poolResponse.Pool == nil { + rpc.Logger.Error().Err(err).Msg("Staking pool is empty!") + errors = append(errors, fmt.Errorf("staking pool is empty")) + } else { + pool = poolResponse.Pool.BondedTokens + } + mutex.Unlock() + }() + + wg.Add(1) + go func() { + defer wg.Done() + + chainProposals, err := rpc.GetAllProposals() + + mutex.Lock() + + if err != nil { + rpc.Logger.Error().Err(err).Msg("Error fetching chain proposals") + errors = append(errors, err) + + mutex.Unlock() + return + } else { + proposals = chainProposals + } + + mutex.Unlock() + + var internalWg sync.WaitGroup + + for _, proposal := range chainProposals { + internalWg.Add(1) + + go func(p types.Proposal) { + defer internalWg.Done() + + tally, err := rpc.GetTally(p.ID) + + mutex.Lock() + defer mutex.Unlock() + + if err != nil { + rpc.Logger.Error(). + Err(err). + Str("proposal_id", p.ID). + Msg("Error fetching tally for proposal") + errors = append(errors, err) + } else if tally == nil { + rpc.Logger.Error(). + Err(err). + Str("proposal_id", p.ID). + Msg("Tally is empty") + errors = append(errors, fmt.Errorf("tally is empty")) + } else { + tallies[p.ID] = *tally + } + }(proposal) + } + + internalWg.Wait() + }() + + wg.Wait() + + if len(errors) > 0 { + rpc.Logger.Error().Msg("Errors getting tallies info, not processing") + return types.ChainTallyInfos{}, fmt.Errorf("could not get tallies info: got %d errors", len(errors)) + } + + tallyInfos := types.ChainTallyInfos{ + Chain: rpc.ChainConfig, + TallyInfos: make([]types.TallyInfo, len(proposals)), + } + + for index, proposal := range proposals { + tally, ok := tallies[proposal.ID] + if !ok { + return types.ChainTallyInfos{}, fmt.Errorf("could not get tallies info") + } + + tallyInfos.TallyInfos[index] = types.TallyInfo{ + Proposal: proposal, + Tally: tally, + TotalVotingPower: pool, + } + } + + return tallyInfos, nil +} diff --git a/pkg/fetchers/cosmos/vote.go b/pkg/fetchers/cosmos/vote.go new file mode 100644 index 0000000..aa34dce --- /dev/null +++ b/pkg/fetchers/cosmos/vote.go @@ -0,0 +1,47 @@ +package cosmos + +import ( + "errors" + "fmt" + "main/pkg/fetchers/cosmos/responses" + "main/pkg/types" + "strings" +) + +func (rpc *RPC) GetVote(proposal, voter string) (*types.Vote, *types.QueryError) { + url := fmt.Sprintf( + "/cosmos/gov/v1beta1/proposals/%s/votes/%s", + proposal, + voter, + ) + + var vote responses.VoteRPCResponse + if errs := rpc.Get(url, &vote); len(errs) > 0 { + return nil, &types.QueryError{ + QueryError: nil, + NodeErrors: errs, + } + } + + if vote.IsError() { + // not voted + if strings.Contains(vote.Message, "not found") { + return nil, nil + } + + // some other errors + return nil, &types.QueryError{ + QueryError: errors.New(vote.Message), + } + } + + voteParsed, err := vote.ToVote() + if err != nil { + return nil, &types.QueryError{ + QueryError: err, + NodeErrors: nil, + } + } + + return voteParsed, nil +} diff --git a/pkg/types/responses.go b/pkg/types/responses.go index 73ffadb..45e0733 100644 --- a/pkg/types/responses.go +++ b/pkg/types/responses.go @@ -10,19 +10,6 @@ import ( "cosmossdk.io/math" ) -type TallyRPCResponse struct { - Code int64 `json:"code"` - Message string `json:"message"` - Tally *Tally `json:"tally"` -} - -type Tally struct { - Yes math.LegacyDec `json:"yes"` - No math.LegacyDec `json:"no"` - NoWithVeto math.LegacyDec `json:"no_with_veto"` - Abstain math.LegacyDec `json:"abstain"` -} - type PoolRPCResponse struct { Code int64 `json:"code"` Message string `json:"message"` diff --git a/pkg/types/tally.go b/pkg/types/tally.go new file mode 100644 index 0000000..cdb8e6a --- /dev/null +++ b/pkg/types/tally.go @@ -0,0 +1,63 @@ +package types + +import ( + "fmt" + + "cosmossdk.io/math" +) + +type TallyOption struct { + Option string + Voted math.LegacyDec +} + +type Tally []TallyOption + +func (t Tally) GetTotalVoted() math.LegacyDec { + sum := math.LegacyNewDec(0) + + for _, option := range t { + sum = sum.Add(option.Voted) + } + + return sum +} + +func (t Tally) GetVoted(option TallyOption) string { + votedPercent := option.Voted. + Quo(t.GetTotalVoted()). + Mul(math.LegacyNewDec(100)). + MustFloat64() + + return fmt.Sprintf( + "%.2f%%", + votedPercent, + ) +} + +type TallyInfo struct { + Proposal Proposal + Tally Tally + TotalVotingPower math.LegacyDec +} + +func (t TallyInfo) GetQuorum() string { + return fmt.Sprintf( + "%.2f%%", + t.Tally.GetTotalVoted(). + Quo(t.TotalVotingPower). + Mul(math.LegacyNewDec(100)). + MustFloat64(), + ) +} + +func (t TallyInfo) GetNotVoted() string { + return fmt.Sprintf( + "%.2f%%", + math.LegacyNewDec(100). + Sub(t.Tally.GetTotalVoted(). + Quo(t.TotalVotingPower). + Mul(math.LegacyNewDec(100)), + ).MustFloat64(), + ) +} diff --git a/pkg/types/types.go b/pkg/types/types.go index c086419..a2fa4e5 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -2,8 +2,6 @@ package types import ( "fmt" - - "cosmossdk.io/math" ) type Link struct { @@ -19,71 +17,7 @@ func (l Link) Serialize() string { return fmt.Sprintf("%s", l.Href, l.Name) } -type TallyInfo struct { - Proposal Proposal - Tally Tally - Pool Pool -} - type ChainTallyInfos struct { Chain *Chain TallyInfos []TallyInfo } - -func (t *TallyInfo) GetNotAbstained() math.LegacyDec { - return t.Tally.Yes.Add(t.Tally.No).Add(t.Tally.NoWithVeto) -} - -func (t *TallyInfo) GetTotalVoted() math.LegacyDec { - return t.GetNotAbstained().Add(t.Tally.Abstain) -} - -func (t *TallyInfo) GetQuorum() string { - return fmt.Sprintf( - "%.2f%%", - t.GetTotalVoted().Quo(t.Pool.BondedTokens).Mul(math.LegacyNewDec(100)).MustFloat64(), - ) -} - -func (t *TallyInfo) GetNotVoted() string { - return fmt.Sprintf( - "%.2f%%", - math.LegacyNewDec(100).Sub(t.GetTotalVoted().Quo(t.Pool.BondedTokens).Mul(math.LegacyNewDec(100))).MustFloat64(), - ) -} - -func (t *TallyInfo) GetAbstained() string { - abstainedPercent := t.Tally.Abstain.Quo(t.GetTotalVoted()).Mul(math.LegacyNewDec(100)).MustFloat64() - - return fmt.Sprintf( - "%.2f%%", - abstainedPercent, - ) -} - -func (t *TallyInfo) GetYesVotes() string { - percent := t.Tally.Yes.Quo(t.GetTotalVoted()).Mul(math.LegacyNewDec(100)).MustFloat64() - - return fmt.Sprintf( - "%.2f%%", - percent, - ) -} - -func (t *TallyInfo) GetNoVotes() string { - percent := t.Tally.No.Quo(t.GetTotalVoted()).Mul(math.LegacyNewDec(100)).MustFloat64() - - return fmt.Sprintf( - "%.2f%%", - percent, - ) -} - -func (t *TallyInfo) GetNoWithVetoVotes() string { - percent := t.Tally.NoWithVeto.Quo(t.GetTotalVoted()).Mul(math.LegacyNewDec(100)).MustFloat64() - - return fmt.Sprintf( - "%.2f%%", - percent, - ) -} diff --git a/templates/telegram/tally.html b/templates/telegram/tally.html index c908c10..ea65406 100644 --- a/templates/telegram/tally.html +++ b/templates/telegram/tally.html @@ -4,16 +4,15 @@ {{- range $chainName, $tallyInfos := . }} {{- if $tallyInfos.TallyInfos }} Proposals on chain {{ $tallyInfos.Chain.GetName }}: -{{ range $tallyInfos.TallyInfos }} +{{ range $chainIndex, $tallyInfo := $tallyInfos.TallyInfos }} {{- $proposalLink := $tallyInfos.Chain.GetProposalLink .Proposal }} Proposal #{{ .Proposal.ID }}: {{ SerializeLink $proposalLink }} Ends in: {{ .Proposal.GetTimeLeft }} - Not voted: {{ .GetNotVoted }} - Voted: {{ .GetQuorum }} -- Voted "Abstain": {{ .GetAbstained }} -- Voted "Yes": {{ .GetYesVotes }} -- Voted "No": {{ .GetNoVotes }} -- Voted "NoWithVeto": {{ .GetNoWithVetoVotes }} +{{- range $tallyOptionIndex, $tallyOption := .Tally }} +- Voted "{{ $tallyOption.Option }}": {{ $tallyInfo.Tally.GetVoted $tallyOption }} +{{- end }} {{ end }} {{- end }} {{ end }} From 0a7f4ac0d4431986628df6d1bc53b0e0acaf0b66 Mon Sep 17 00:00:00 2001 From: Techno Freak Date: Thu, 21 Dec 2023 02:59:05 +0300 Subject: [PATCH 2/4] chore: moved types --- pkg/fetchers/cosmos/fetcher.go | 4 ++-- pkg/fetchers/cosmos/responses/pool.go | 13 +++++++++++++ pkg/fetchers/cosmos/tally.go | 2 +- pkg/types/responses.go | 12 ------------ pkg/types/tally.go | 5 +++++ pkg/types/types.go | 5 ----- 6 files changed, 21 insertions(+), 20 deletions(-) create mode 100644 pkg/fetchers/cosmos/responses/pool.go diff --git a/pkg/fetchers/cosmos/fetcher.go b/pkg/fetchers/cosmos/fetcher.go index 476ea38..1dba776 100644 --- a/pkg/fetchers/cosmos/fetcher.go +++ b/pkg/fetchers/cosmos/fetcher.go @@ -55,10 +55,10 @@ func (rpc *RPC) GetTally(proposal string) (*types.Tally, *types.QueryError) { return tally.Tally.ToTally(), nil } -func (rpc *RPC) GetStakingPool() (*types.PoolRPCResponse, *types.QueryError) { +func (rpc *RPC) GetStakingPool() (*responses.PoolRPCResponse, *types.QueryError) { url := "/cosmos/staking/v1beta1/pool" - var pool types.PoolRPCResponse + var pool responses.PoolRPCResponse if errs := rpc.Get(url, &pool); len(errs) > 0 { return nil, &types.QueryError{ QueryError: nil, diff --git a/pkg/fetchers/cosmos/responses/pool.go b/pkg/fetchers/cosmos/responses/pool.go new file mode 100644 index 0000000..9a27f7b --- /dev/null +++ b/pkg/fetchers/cosmos/responses/pool.go @@ -0,0 +1,13 @@ +package responses + +import "cosmossdk.io/math" + +type PoolRPCResponse struct { + Code int64 `json:"code"` + Message string `json:"message"` + Pool *Pool `json:"pool"` +} + +type Pool struct { + BondedTokens math.LegacyDec `json:"bonded_tokens"` +} diff --git a/pkg/fetchers/cosmos/tally.go b/pkg/fetchers/cosmos/tally.go index 8f28fe6..bcd485f 100644 --- a/pkg/fetchers/cosmos/tally.go +++ b/pkg/fetchers/cosmos/tally.go @@ -16,7 +16,7 @@ func (rpc *RPC) GetTallies() (types.ChainTallyInfos, error) { var pool math.LegacyDec var proposals []types.Proposal - tallies := make(map[string]types.Tally, 0) + tallies := make(map[string]types.Tally) wg.Add(1) go func() { diff --git a/pkg/types/responses.go b/pkg/types/responses.go index 45e0733..d6ba5f5 100644 --- a/pkg/types/responses.go +++ b/pkg/types/responses.go @@ -6,20 +6,8 @@ import ( "time" "main/pkg/utils" - - "cosmossdk.io/math" ) -type PoolRPCResponse struct { - Code int64 `json:"code"` - Message string `json:"message"` - Pool *Pool `json:"pool"` -} - -type Pool struct { - BondedTokens math.LegacyDec `json:"bonded_tokens"` -} - type ParamsResponse struct { VotingParams VotingParams `json:"voting_params"` DepositParams DepositParams `json:"deposit_params"` diff --git a/pkg/types/tally.go b/pkg/types/tally.go index cdb8e6a..572b819 100644 --- a/pkg/types/tally.go +++ b/pkg/types/tally.go @@ -61,3 +61,8 @@ func (t TallyInfo) GetNotVoted() string { ).MustFloat64(), ) } + +type ChainTallyInfos struct { + Chain *Chain + TallyInfos []TallyInfo +} diff --git a/pkg/types/types.go b/pkg/types/types.go index a2fa4e5..9627c33 100644 --- a/pkg/types/types.go +++ b/pkg/types/types.go @@ -16,8 +16,3 @@ func (l Link) Serialize() string { return fmt.Sprintf("%s", l.Href, l.Name) } - -type ChainTallyInfos struct { - Chain *Chain - TallyInfos []TallyInfo -} From 9c3d48b536ad01893033219da23d032fb0569fd1 Mon Sep 17 00:00:00 2001 From: Techno Freak Date: Thu, 21 Dec 2023 03:10:45 +0300 Subject: [PATCH 3/4] chore: add tally test --- pkg/types/tally_test.go | 45 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 pkg/types/tally_test.go diff --git a/pkg/types/tally_test.go b/pkg/types/tally_test.go new file mode 100644 index 0000000..dc22c85 --- /dev/null +++ b/pkg/types/tally_test.go @@ -0,0 +1,45 @@ +package types + +import ( + "cosmossdk.io/math" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestTallyGetVoted(t *testing.T) { + t.Parallel() + + tally := Tally{ + {Option: "Yes", Voted: math.LegacyNewDec(2)}, + {Option: "No", Voted: math.LegacyNewDec(3)}, + {Option: "Abstain", Voted: math.LegacyNewDec(5)}, + } + + assert.Equal(t, "20.00%", tally.GetVoted(tally[0]), "Wrong value!") +} + +func TestTallyGetQuorum(t *testing.T) { + t.Parallel() + + tallyInfo := TallyInfo{ + Tally: Tally{ + {Option: "idk", Voted: math.LegacyNewDec(3)}, + }, + TotalVotingPower: math.LegacyNewDec(10), + } + + assert.Equal(t, "30.00%", tallyInfo.GetQuorum(), "Wrong value!") +} + +func TestTallyGetNotVoted(t *testing.T) { + t.Parallel() + + tallyInfo := TallyInfo{ + Tally: Tally{ + {Option: "idk", Voted: math.LegacyNewDec(3)}, + }, + TotalVotingPower: math.LegacyNewDec(10), + } + + assert.Equal(t, "70.00%", tallyInfo.GetNotVoted(), "Wrong value!") +} From 2ae51d636821dc6a3092a6f8705f4c359c30b373 Mon Sep 17 00:00:00 2001 From: Techno Freak Date: Thu, 21 Dec 2023 03:12:38 +0300 Subject: [PATCH 4/4] chore: fix linting --- pkg/types/tally_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/types/tally_test.go b/pkg/types/tally_test.go index dc22c85..f38420c 100644 --- a/pkg/types/tally_test.go +++ b/pkg/types/tally_test.go @@ -1,9 +1,10 @@ package types import ( + "testing" + "cosmossdk.io/math" "github.com/stretchr/testify/assert" - "testing" ) func TestTallyGetVoted(t *testing.T) {