diff --git a/platform/cosmos/api.go b/platform/cosmos/api.go index 0c28ef910..b1351eba1 100644 --- a/platform/cosmos/api.go +++ b/platform/cosmos/api.go @@ -49,11 +49,23 @@ func (p *Platform) GetTxsByAddress(address string) (blockatlas.TxPage, error) { } func (p *Platform) GetValidators() (blockatlas.ValidatorPage, error) { - results := blockatlas.ValidatorPage{} - validators, _ := p.client.GetValidators() + results := make(blockatlas.ValidatorPage, 0) + validators, err := p.client.GetValidators() + if err != nil { + return results, nil + } + pool, err := p.client.GetPool() + if err != nil { + return results, nil + } + + inflation, err := p.client.GetInflation() + if err != nil { + return results, nil + } for _, validator := range validators { - results = append(results, normalizeValidator(validator, p.Coin())) + results = append(results, normalizeValidator(validator, pool, inflation, p.Coin())) } return results, nil @@ -88,10 +100,37 @@ func Normalize(srcTx *Tx) (tx blockatlas.Tx) { } } -func normalizeValidator(v CosmosValidator, c coin.Coin) (validator blockatlas.Validator) { +func normalizeValidator(v CosmosValidator, p StakingPool, inflation float64, c coin.Coin) (validator blockatlas.Validator) { + + reward := CalculateAnnualReward(p, inflation, v) + return blockatlas.Validator{ Coin: c, Status: bool(v.Status == 2), ID: v.Operator_Address, + Reward: blockatlas.StakingReward{Annual: reward}, } } + +func CalculateAnnualReward(p StakingPool, inflation float64, validator CosmosValidator) float64 { + + notBondedTokens, err := strconv.ParseFloat(string(p.NotBondedTokens), 32) + + if err != nil { + return 0 + } + + bondedTokens, err := strconv.ParseFloat(string(p.BondedTokens), 32) + if err != nil { + return 0 + } + + commission, err := strconv.ParseFloat(string(validator.Commission.Rate), 32) + if err != nil { + return 0 + } + + result := (notBondedTokens + bondedTokens) / bondedTokens * inflation + + return (result - (result * commission)) * 100 +} diff --git a/platform/cosmos/api_test.go b/platform/cosmos/api_test.go index 8f76f98c4..9e314e183 100644 --- a/platform/cosmos/api_test.go +++ b/platform/cosmos/api_test.go @@ -119,6 +119,12 @@ var basicDst = blockatlas.Tx{ }, } +var stakingPool = StakingPool{"1222", "200"} + +var cosmosValidator = CosmosValidator{Commission: CosmosCommission{Rate: "0.4"}} + +var inflation = 0.7 + func TestNormalize(t *testing.T) { var srcTx Tx err := json.Unmarshal([]byte(basicSrc), &srcTx) @@ -152,9 +158,17 @@ func TestNormalizeValidator(t *testing.T) { expected := blockatlas.Validator{ Status: true, ID: v.Operator_Address, + Reward: blockatlas.StakingReward{Annual: 435.48749999999995}, } - result := normalizeValidator(v, coin) + result := normalizeValidator(v, stakingPool, inflation, coin) assert.Equal(t, result, expected) } + +func TestCalculateAnnualReward(t *testing.T) { + + result := CalculateAnnualReward(StakingPool{"1222", "200"}, inflation, CosmosValidator{Commission: CosmosCommission{Rate: "0.4"}}) + + assert.Equal(t, result, 298.61999703347686) +} diff --git a/platform/cosmos/client.go b/platform/cosmos/client.go index abf96493f..2383b4106 100644 --- a/platform/cosmos/client.go +++ b/platform/cosmos/client.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/trustwallet/blockatlas" + "github.com/trustwallet/blockatlas/client" "net/http" "net/url" "strconv" @@ -73,3 +74,17 @@ func (c *Client) GetValidators() (validators []CosmosValidator, err error) { return validators, err } + +func (c *Client) GetPool() (result StakingPool, err error) { + return result, client.Request(c.HTTPClient, c.BaseURL, "staking/pool", url.Values{}, &result) +} + +func (c *Client) GetInflation() (float64, error) { + var result string + + err := client.Request(c.HTTPClient, c.BaseURL, "minting/inflation", url.Values{}, &result) + + s, err := strconv.ParseFloat(result, 32) + + return s, err +} diff --git a/platform/cosmos/model.go b/platform/cosmos/model.go index d22fec2db..0fd257fc2 100644 --- a/platform/cosmos/model.go +++ b/platform/cosmos/model.go @@ -46,7 +46,17 @@ type Amount struct { // # Staking +type CosmosCommission struct { + Rate string `json:"rate"` +} + type CosmosValidator struct { - Status int `json:"status"` - Operator_Address string `json:"operator_address"` + Status int `json:"status"` + Operator_Address string `json:"operator_address"` + Commission CosmosCommission `json:"commission"` +} + +type StakingPool struct { + NotBondedTokens string `json:"not_bonded_tokens"` + BondedTokens string `json:"bonded_tokens"` } diff --git a/services/assets/client.go b/services/assets/client.go index acdc8323b..204dd2174 100644 --- a/services/assets/client.go +++ b/services/assets/client.go @@ -24,7 +24,7 @@ func GetValidators(coin coin.Coin) ([]AssetValidator, error) { } func NormalizeValidators(validators []blockatlas.Validator, assets []AssetValidator) []blockatlas.StakeValidator { - var results []blockatlas.StakeValidator + results := make([]blockatlas.StakeValidator, 0) for _, v := range validators { for _, v2 := range assets { @@ -47,6 +47,7 @@ func NormalizeValidator(plainValidator blockatlas.Validator, validator AssetVali Image: GetImage(plainValidator.Coin, plainValidator.ID), Website: validator.Website, }, + Reward: plainValidator.Reward, } } diff --git a/staking.go b/staking.go index 6784fd1ab..e5f613826 100644 --- a/staking.go +++ b/staking.go @@ -10,6 +10,17 @@ type DocsResponse struct { const ValidatorsPerPage = 100 +type StakingReward struct { + Annual float64 `json:"annual"` +} + +type Validator struct { + Coin coin.Coin + ID string `json:"id"` + Status bool `json:"status"` + Reward StakingReward `json:"reward"` +} + type StakeValidatorInfo struct { Name string `json:"name"` Description string `json:"description"` @@ -17,14 +28,9 @@ type StakeValidatorInfo struct { Website string `json:"website"` } -type Validator struct { - Coin coin.Coin - ID string `json:"id"` - Status bool `json:"status"` -} - type StakeValidator struct { ID string `json:"id"` Status bool `json:"status"` Info StakeValidatorInfo `json:"info"` + Reward StakingReward `json:"reward"` }