Skip to content

Commit

Permalink
Botanist analyzes Orchid contract data
Browse files Browse the repository at this point in the history
  • Loading branch information
danopato committed Jan 3, 2020
1 parent 4dc4b64 commit a14449a
Show file tree
Hide file tree
Showing 10 changed files with 386 additions and 0 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ Greg Hazel <[email protected]>
Pat Niemeyer <[email protected]>
Jake Cannell <[email protected]>
Nathan Handler <[email protected]>
Daniel Montgomery <[email protected]>
2 changes: 2 additions & 0 deletions tst-ethereum/botanist/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*~
botanist
9 changes: 9 additions & 0 deletions tst-ethereum/botanist/botanist.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package main

import (
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/cmd"
)

func main() {
cmd.Execute()
}
33 changes: 33 additions & 0 deletions tst-ethereum/botanist/cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cmd

import (
"fmt"
"os"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "botanist",
Short: "Botanist tobulates metrics for monitoring Orchid",
Long: ``,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
},
TraverseChildren: true,
}

func init() {
rootCmd.PersistentFlags().StringVarP(&ApiKey, "apikey", "k", "doesntseemtomatter", "Etherscan API key")
rootCmd.PersistentFlags().StringVarP(&LottoAddr, "lotto", "l", "0xb02396f06cc894834b7934ecf8c8e5ab5c1d12f1", "Lottery contract address")
rootCmd.PersistentFlags().StringVarP(&StartBlock, "start", "s", "0", "Starting block number")
rootCmd.PersistentFlags().StringVarP(&EndBlock, "end", "e", "999999999", "Ending block number")
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

var StartBlock, EndBlock, ApiKey, LottoAddr string

33 changes: 33 additions & 0 deletions tst-ethereum/botanist/cmd/tallies.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package cmd

import (
"fmt"
"github.com/spf13/cobra"
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/orchid"
"math"
"strconv"
)



var talliesCmd = &cobra.Command{
Use: "tallies",
Short: "Print tallies of useful metrics",
Long: ``,
Run: func(cmd *cobra.Command, args []string) {
grabsize, _ := strconv.Atoi(MinFaceValue)
err, lot := orchid.NewLotteryFromEtherscan(ApiKey, LottoAddr, StartBlock, EndBlock)
if err != nil {
fmt.Println(err)
return
}
lot.Tallies(int64(math.Pow10(grabsize)))
},
}

func init() {
rootCmd.AddCommand(talliesCmd)
talliesCmd.PersistentFlags().StringVarP(&MinFaceValue, "minface", "c", "16", "Only print payments of face value > 10^x keiki")
}

var MinFaceValue string
7 changes: 7 additions & 0 deletions tst-ethereum/botanist/ethereum/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ethereum

type Contract struct {
Name string
Address string
Functions map[string]string
}
6 changes: 6 additions & 0 deletions tst-ethereum/botanist/ethereum/currency.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package ethereum

type DigitalCurrency struct {
Ticker string
Decimals int
}
152 changes: 152 additions & 0 deletions tst-ethereum/botanist/etherscan/etherscan.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
package etherscan

import (
"fmt"
"strings"
"io/ioutil"
"encoding/json"
"net/http"
"strconv"
"math/big"
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/ethereum"
log "github.com/sirupsen/logrus"
)

const apiBaseUrl = "https://api.etherscan.io/api"

func apiCallBase(key string, module string, action string, argstr string) (error, []byte) {
qstr := fmt.Sprintf("%s?module=%s&action=%s&sort-asc&%s&apikey=%s", apiBaseUrl, module, action, argstr, key)
resp, err := http.Get(qstr)
log.Debug("Etherscan query:" + qstr)

if err != nil {
log.Error(err)
return err, nil
}

defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)

if err != nil {
log.Error(err)
return err, nil
}
return nil, body
}

func apiCallPlural(key string, module string, action string, argstr string) (error, []map[string]string) {
err, body := apiCallBase(key, module, action, argstr)
if err != nil {
return err, nil
}

type Response struct {
Status string
Message string
Result []map[string]string
}

var r Response
err = json.Unmarshal(body, &r)

if err != nil {
log.Error(err)
return err, nil
}
return nil, r.Result
}

func apiCallSingular(key string, module string, action string, argstr string) (error, map[string]string) {
err, body := apiCallBase(key, module, action, argstr)
if err != nil {
return err, nil
}

type Response struct {
Status string
Message string
Result map[string]string
}

var r Response
err = json.Unmarshal(body, &r)

if err != nil {
log.Error(err)
return err, nil
}
return nil, r.Result
}

type EtherscanTxn struct {
Hash string
From string
To string
Amount *big.Int
Currency ethereum.DigitalCurrency
Function string
}

func AccountTransactions(key string, addr string, start string, end string, contract ethereum.Contract) (error, []EtherscanTxn) {
log.Trace("Etherscan key" + key)

type Transaction struct {
BlockNumber string
Timestamp string
Hash string
Nonce string
BlockHash string
From string
ContractAddress string
To string
Value string
TokenName string
TokenSymbol string
TokenDecimal string
TransactionIndex string
Gas string
GasPrice string
GasUsed string
CumulativeGasUsed string
Input string
Confirmations string
}

addrStr := fmt.Sprintf("address=%s", addr)
startStr := fmt.Sprintf("startblock=%s", start)
endStr := fmt.Sprintf("endblock=%s", end)
argstr := strings.Join([]string{addrStr, startStr, endStr}, "&")

err, result := apiCallPlural(key, "account", "tokentx", argstr)
if err != nil {
log.Error(err)
return err, nil
}

out := make([]EtherscanTxn, 0)
for _, t := range result {
amt := new(big.Int)
amt.SetString(t["value"], 10)

d, err := strconv.Atoi(t["tokenDecimal"])
if err != nil {
log.Warn(err)
continue
}

cur := ethereum.DigitalCurrency{t["tokenSymbol"], d}

err, txres := apiCallSingular(key, "proxy", "eth_getTransactionByHash", fmt.Sprintf("txhash=%s", t["hash"]))
fstr := "00000000"
if err == nil {
fstr = txres["input"][2:10]
if contract.Functions[fstr] != "" {
fstr = contract.Functions[fstr]
}
}

out = append(out, EtherscanTxn{t["hash"], t["from"], t["to"], amt, cur, fstr})
}
return nil, out
}

106 changes: 106 additions & 0 deletions tst-ethereum/botanist/orchid/lottery.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package orchid

import (
"strings"
"fmt"
"math"
"math/big"
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/ethereum"
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/etherscan"
"github.com/OrchidTechnologies/orchid/tst-ethereum/botanist/util"
)

type Lottery struct {
Accounts []LotteryAccount
Currency ethereum.DigitalCurrency
Address string
Transactions []LotteryTransaction
LotteryContract ethereum.Contract
}


func NewLotteryFromEtherscan(key string, addr string, start string, end string) (error, *Lottery) {
out := Lottery{}
out.Address = addr
out.LotteryContract = ethereum.Contract{"Lottery", addr,
map[string]string{"66458bbd": "grab", "73fb4644": "push", "5f51b34e": "yank", "a6cbd6e3": "pull"}}

err, txns := etherscan.AccountTransactions(key, addr, start, end, out.LotteryContract)
if err != nil {
fmt.Println(err)
return err, nil
}

ltxns := make([]LotteryTransaction, 0)
for _, t := range txns {
ltxns = append(ltxns, LotteryTransaction{out, t.Hash, t.From, t.To, t.Amount, t.Function})
out.Currency = t.Currency
}
out.Transactions = ltxns
return nil, &out
}

func (lotto *Lottery) Tallies(grabsize int64) string {
intot := new(big.Int)
outtot := new(big.Int)
var funders []string
var c,d int
dec := int64(math.Pow10(lotto.Currency.Decimals))
div := new(big.Int).SetInt64(dec)
headstr, _ := util.Columnize(util.ColumnList{"Txn Hash": 66, "Recipient": 42, "Face Value": 15})
fmt.Println(headstr)
for _, txn := range lotto.Transactions {
if txn.TxnType == "grab" {
if txn.Amount.Cmp(new(big.Int).SetInt64(grabsize)) != -1 {
face := new(big.Float).Quo(new(big.Float).SetInt(txn.Amount), new(big.Float).SetInt(div))
fmt.Println(txn.Hash, txn.To, face)
c++
}
d++
}
if strings.EqualFold(txn.To, lotto.Address) {
funders = util.AppendIfUnique(funders, txn.From)
intot.Add(intot, txn.Amount)
}
if strings.EqualFold(txn.From, lotto.Address) {
outtot.Add(outtot, txn.Amount)
}
// fmt.Println(res.Timestamp, "\t", res.From, "\t", amount)
// fmt.Println(res)
}
threshold, _ := new(big.Float).Quo(new(big.Float).SetInt64(grabsize), new(big.Float).SetInt(div)).Float64()
fmt.Printf("%d grab() transactions of face value > %f %s out of %d total.\n", c, threshold, lotto.Currency.Ticker, d)

in := new(big.Int).Div(intot, div)
out := new(big.Int).Div(outtot, div)
net := new(big.Int).Sub(in, out)
fmt.Println("Total in: ", in , lotto.Currency.Ticker)
fmt.Println("Total out: ", out, "OXT")
fmt.Println("Net held in contract: ", net, lotto.Currency.Ticker)
fmt.Println("Accounts: ", len(funders))
return ""
}

type LotteryAccount struct {
Funder string
Signer string
Balance int
Deposit int
Transactions []LotteryTransaction
}

type LotteryFunction int

const (
In LotteryFunction = iota
Out
)

type LotteryTransaction struct {
Contract Lottery
Hash string
From string
To string
Amount *big.Int
TxnType string
}
37 changes: 37 additions & 0 deletions tst-ethereum/botanist/util/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package util

import (
"strings"
"errors"
"fmt"
)

func AppendIfUnique(s []string, v string) []string {
unique := true
for _, x := range s {
if strings.EqualFold(x, v) {
unique = false
}
}
if unique == true {
return append(s, v)
}
return s
}

type ColumnList map[string]int

func Columnize(list ColumnList) (string, error) {
out := make([]string, 0)
for s, l := range list {
if len(s) > l {
return "", errors.New(fmt.Sprintf("String \"%s\" is longer than length %d", s, l))
}
pad := strings.Repeat(" ", (l - len(s)) / 2)
outstr := pad + s + pad
outstr += strings.Repeat(" ", l - len(outstr))
out = append(out, outstr)
}
return strings.Join(out, " "), nil
}

0 comments on commit a14449a

Please sign in to comment.