forked from OrchidTechnologies/orchid
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Botanist analyzes Orchid contract data
- Loading branch information
danopato
committed
Jan 3, 2020
1 parent
4dc4b64
commit a14449a
Showing
10 changed files
with
386 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,3 +9,4 @@ Greg Hazel <[email protected]> | |
Pat Niemeyer <[email protected]> | ||
Jake Cannell <[email protected]> | ||
Nathan Handler <[email protected]> | ||
Daniel Montgomery <[email protected]> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*~ | ||
botanist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package ethereum | ||
|
||
type DigitalCurrency struct { | ||
Ticker string | ||
Decimals int | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
|