diff --git a/README.md b/README.md index 7384d3d..cd03cab 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,10 @@ Golang 2. Also, install [postgresql](https://www.postgresql.org/download/) and make sure it is running in the background 3. After postgres is installed, copy the commands in `db/postgres_script.sql` into the terminal 4. Add following to your `~/.bashrc` or `~/.zshrc`: - 1. `export SOLANA_API_KEY=api_key` - 2. `export DATABASE_URL=postgres://username:password@localhost:5432/postgres` -4. In a separate terminal, run `go run core/main.go`. This will start the core logic of calculating the nakamoto coefficients. -5. If you want to start the server, run `go run server/main.go` in another terminal. + 1. `export SOLANA_API_KEY=api_key` + 2. `export DATABASE_URL=postgres://username:password@localhost:5432/postgres` +5. In a separate terminal, run `go run core/main.go`. This will start the core logic of calculating the nakamoto coefficients. +6. If you want to start the server, run `go run server/main.go` in another terminal. ### Chains currently supported @@ -33,11 +33,13 @@ Golang 8. [Terra](https://www.terra.money/) 9. [Graph Protocol](https://thegraph.com/) 10. [Thorchain](https://www.thorchain.com/) +11. [Near](https://near.org/) +12. [Juno](https://www.junonetwork.io/) ### Notes 1. The actual logic is present inside `/core`. So, ideally a cron job would be run after every `JOB_INTERVAL` which would save/refresh the nakamoto-coefficients `database`. -3. The server code resides inside `/server`. It is a simple server which would only respond to `GET /nakamoto-coefficients`. It basically queries the database and returns the values. +2. The server code resides inside `/server`. It is a simple server which would only respond to `GET /nakamoto-coefficients`. It basically queries the database and returns the values. ### Future Work diff --git a/core/chains/juno.go b/core/chains/juno.go new file mode 100644 index 0000000..fcd96c6 --- /dev/null +++ b/core/chains/juno.go @@ -0,0 +1,95 @@ +package chains + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "strings" + + "math/big" + "net/http" + "sort" + + utils "github.com/xenowits/nakamoto-coefficient-calculator/core/utils" +) + +type JunoResponse struct { + Data struct { + Validators []struct { + ValidatorVotingPowers []struct { + VotingPower uint64 `json:"votingPower"` + } `json:"validatorVotingPowers"` + } `json:"validator"` + } `json:"data"` +} + +type JunoErrorResponse struct { + Id int `json:"id"` + Jsonrpc string `json:"jsonrpc"` + Error string `json:"error"` +} + +func Juno() (int, error) { + votingPowers := make([]big.Int, 0, 1000) + + url := fmt.Sprintf("https://hasura.junoscan.com/v1/graphql") + jsonReqData := "{\"query\":\"query { validator { validatorVotingPowers: validator_voting_powers(order_by: { voting_power: desc }) { votingPower: voting_power } }}\",\"variables\":{}}" + + // Create a new request using http + req, err := http.NewRequest("POST", url, strings.NewReader(jsonReqData)) + if err != nil { + return -1, err + } + req.Header.Add("Content-Type", "application/json") + + // Send req using http Client + client := &http.Client{} + resp, err := client.Do(req) + + if err != nil { + errBody, _ := ioutil.ReadAll(resp.Body) + var errResp JunoErrorResponse + json.Unmarshal(errBody, &errResp) + log.Println(errResp.Error) + return -1, err + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return -1, err + } + + var response JunoResponse + err = json.Unmarshal(body, &response) + if err != nil { + return -1, err + } + + // loop through the validators voting powers + for _, ele := range response.Data.Validators { + if len(ele.ValidatorVotingPowers) > 0 { + n := new(big.Int).SetUint64(ele.ValidatorVotingPowers[0].VotingPower) + votingPowers = append(votingPowers, *n) + } + } + + // need to sort the powers in descending order since they are in random order + sort.Slice(votingPowers, func(i, j int) bool { + res := (&votingPowers[i]).Cmp(&votingPowers[j]) + if res == 1 { + return true + } + return false + }) + + totalVotingPower := utils.CalculateTotalVotingPowerBigNums(votingPowers) + fmt.Println("Total voting power:", totalVotingPower) + + // now we're ready to calculate the nakomoto coefficient + nakamotoCoefficient := utils.CalcNakamotoCoefficientBigNums(totalVotingPower, votingPowers) + fmt.Println("The Nakamoto coefficient for juno is", nakamotoCoefficient) + + return nakamotoCoefficient, nil +} diff --git a/core/chains/mina.go b/core/chains/mina.go index 9997e9d..d01d1c2 100644 --- a/core/chains/mina.go +++ b/core/chains/mina.go @@ -36,19 +36,17 @@ func reverse(numbers []float64) { } func Mina() (int, error) { - votingPowers := make([]float64, 0, 200) + votingPowers := make([]float64, 0, 1000) pageNo, entriesPerPage := 0, 50 url := "" for true { // Check the most active url in the network logs here: https://mina.staketab.com/validators/stake // Sometimes it changes, like once it changed from mina.staketab.com to t-mina.staketab.com - url = fmt.Sprintf("https://t-mina.staketab.com:8181/api/validator/all/?page=%d&size=%d&sortBy=canonical_block&findStr=&orderBy=DESC", pageNo, entriesPerPage) + // Once, it was https://mina.staketab.com:8181/api/validator/all/ + url = fmt.Sprintf("https://mina.staketab.com/mainnet/api/api/validators/?page=%d&size=%d&sortBy=canonical_block&findStr=&orderBy=DESC", pageNo, entriesPerPage) resp, err := http.Get(url) if err != nil { - errBody, _ := ioutil.ReadAll(resp.Body) - var errResp MinaErrorResponse - json.Unmarshal(errBody, &errResp) - log.Println(errResp.Error) + log.Println(err) return -1, err } defer resp.Body.Close() diff --git a/core/main.go b/core/main.go index e2f1e8f..2778973 100644 --- a/core/main.go +++ b/core/main.go @@ -26,7 +26,7 @@ func main() { } }() - networks := []string{"BNB", "ATOM", "OSMO", "MATIC", "MINA", "SOL", "AVAX", "LUNA", "GRT", "RUNE", "NEAR"} + networks := []string{"BNB", "ATOM", "OSMO", "MATIC", "MINA", "SOL", "AVAX", "LUNA", "GRT", "RUNE", "NEAR", "JUNO"} for _, n := range networks { UpdateChainInfo(n) } @@ -58,6 +58,8 @@ func UpdateChainInfo(chainToken string) { currVal, err = chains.Thorchain() case "NEAR": currVal, err = chains.Near() + case "JUNO": + currVal, err = chains.Juno() } if err != nil { diff --git a/db/postgres_script.sql b/db/postgres_script.sql index fa374a3..14485ce 100644 --- a/db/postgres_script.sql +++ b/db/postgres_script.sql @@ -48,6 +48,8 @@ INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_c INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_co_curr_val) VALUES ('Near', 'NEAR', -1, 6); +INSERT INTO naka_coefficients (chain_name, chain_token, naka_co_prev_val, naka_co_curr_val) VALUES ('Juno', 'JUNO', -1, 2); + select * from naka_coefficients; \q