· 4 years ago · May 31, 2021, 12:42 PM
1import requests
2import math
3import functools
4import datetime
5import time
6import os
7
8MINA_GRAPHQL_API = "https://graphql.minaexplorer.com/"
9SNARKS_RETURNED_LIMIT = 1000
10SNARKS_REFRESH_INTERVAL = 60
11SNARKS_TIMEDELTA= 3600
12
13
14def percentile(N, percent, key=lambda x: x):
15 """
16 Find the percentile of a list of values.
17
18 @parameter N - is a list of values. Note N MUST BE already sorted.
19 @parameter percent - a float value from 0.0 to 1.0.
20 @parameter key - optional key function to compute value from each element of N.
21
22 @return - the percentile of the values
23 """
24 if not N:
25 return None
26 k = (len(N) - 1) * percent
27 f = math.floor(k)
28 c = math.ceil(k)
29 if f == c:
30 return key(N[int(k)])
31 d0 = key(N[int(f)]) * (c - k)
32 d1 = key(N[int(c)]) * (k - f)
33 return d0 + d1
34
35
36def get_latest_snark_price(interval=3600):
37 """
38 Call the Mina Protocol graphql API to find the latest price for snarks
39
40 @parameter interval - a integer representing seconds of interval
41
42 @return - a list of latest price for snarks during the interval
43 """
44 upper_bound = datetime.datetime.now()
45 lower_bound = upper_bound - datetime.timedelta(seconds=interval)
46 query = """
47 {{
48 snarks(
49 limit: {}
50 sortBy: FEE_DESC
51 query: {{
52 AND: [
53 {{ dateTime_gte: \"{}\" }}
54 {{ dateTime_lte: \"{}\" }}
55 ]
56 }}
57 ) {{
58 prover
59 fee
60 }}
61 }}
62 """
63
64 lower_fmt = lower_bound.isoformat("T") + "Z"
65 upper_fmt = upper_bound.isoformat("T") + "Z"
66
67 query = query.format(SNARKS_RETURNED_LIMIT, lower_fmt, upper_fmt)
68 response = requests.post(MINA_GRAPHQL_API, json={"query": query})
69
70 if response.status_code != 200:
71 Exception("Could not load the latest snarks from api")
72
73 return response.json()
74
75def set_snarking_fee(target_fee):
76 command = f"mina client set-snark-work-fee {target_fee}"
77 print(f"running: {command}")
78 os.system(command)
79
80
81
82p01 = functools.partial(percentile, percent=0.01)
83
84while 1:
85 try:
86 snarks = get_latest_snark_price(interval=SNARKS_TIMEDELTA)['data']['snarks']
87 snarks_fees = list(map(lambda snark: int(snark['fee']), snarks))
88 snarks_target_fee = 0
89
90 if len(snarks_fees) > 0:
91 snarks_fees.sort()
92 snarks_target_fee = p01(snarks_fees)
93
94 print(f"Updating snarking fees with: {snarks_target_fee} (computed using {len(snarks_fees)} previous snarks)")
95 set_snarking_fee(snarks_target_fee)
96 except Exception as e:
97 print(f" {e} error when fetching, retrying in {SNARKS_REFRESH_INTERVAL} seconds")
98 time.sleep(SNARKS_REFRESH_INTERVAL)