hive-nectar 0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hive-nectar might be problematic. Click here for more details.
- hive_nectar-0.0.2.dist-info/METADATA +182 -0
- hive_nectar-0.0.2.dist-info/RECORD +86 -0
- hive_nectar-0.0.2.dist-info/WHEEL +4 -0
- hive_nectar-0.0.2.dist-info/entry_points.txt +2 -0
- hive_nectar-0.0.2.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +32 -0
- nectar/account.py +4371 -0
- nectar/amount.py +475 -0
- nectar/asciichart.py +270 -0
- nectar/asset.py +82 -0
- nectar/block.py +446 -0
- nectar/blockchain.py +1178 -0
- nectar/blockchaininstance.py +2284 -0
- nectar/blockchainobject.py +221 -0
- nectar/blurt.py +563 -0
- nectar/cli.py +6285 -0
- nectar/comment.py +1217 -0
- nectar/community.py +513 -0
- nectar/constants.py +111 -0
- nectar/conveyor.py +309 -0
- nectar/discussions.py +1709 -0
- nectar/exceptions.py +149 -0
- nectar/hive.py +546 -0
- nectar/hivesigner.py +420 -0
- nectar/imageuploader.py +72 -0
- nectar/instance.py +129 -0
- nectar/market.py +1013 -0
- nectar/memo.py +449 -0
- nectar/message.py +357 -0
- nectar/nodelist.py +444 -0
- nectar/price.py +557 -0
- nectar/profile.py +65 -0
- nectar/rc.py +308 -0
- nectar/snapshot.py +726 -0
- nectar/steem.py +582 -0
- nectar/storage.py +53 -0
- nectar/transactionbuilder.py +622 -0
- nectar/utils.py +545 -0
- nectar/version.py +2 -0
- nectar/vote.py +557 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +617 -0
- nectarapi/__init__.py +11 -0
- nectarapi/exceptions.py +123 -0
- nectarapi/graphenerpc.py +589 -0
- nectarapi/node.py +178 -0
- nectarapi/noderpc.py +229 -0
- nectarapi/rpcutils.py +97 -0
- nectarapi/version.py +2 -0
- nectarbase/__init__.py +14 -0
- nectarbase/ledgertransactions.py +75 -0
- nectarbase/memo.py +243 -0
- nectarbase/objects.py +429 -0
- nectarbase/objecttypes.py +22 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1297 -0
- nectarbase/signedtransactions.py +48 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +2 -0
- nectargrapheneapi/__init__.py +6 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +846 -0
- nectargraphenebase/aes.py +52 -0
- nectargraphenebase/base58.py +192 -0
- nectargraphenebase/bip32.py +494 -0
- nectargraphenebase/bip38.py +134 -0
- nectargraphenebase/chains.py +149 -0
- nectargraphenebase/dictionary.py +3 -0
- nectargraphenebase/ecdsasig.py +326 -0
- nectargraphenebase/objects.py +123 -0
- nectargraphenebase/objecttypes.py +6 -0
- nectargraphenebase/operationids.py +3 -0
- nectargraphenebase/operations.py +23 -0
- nectargraphenebase/prefix.py +11 -0
- nectargraphenebase/py23.py +38 -0
- nectargraphenebase/signedtransactions.py +201 -0
- nectargraphenebase/types.py +419 -0
- nectargraphenebase/unsignedtransactions.py +283 -0
- nectargraphenebase/version.py +2 -0
- nectarstorage/__init__.py +38 -0
- nectarstorage/base.py +306 -0
- nectarstorage/exceptions.py +16 -0
- nectarstorage/interfaces.py +237 -0
- nectarstorage/masterpassword.py +239 -0
- nectarstorage/ram.py +30 -0
- nectarstorage/sqlite.py +334 -0
nectar/snapshot.py
ADDED
|
@@ -0,0 +1,726 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
import re
|
|
5
|
+
from bisect import bisect_left
|
|
6
|
+
from datetime import date, datetime, time, timedelta, timezone
|
|
7
|
+
|
|
8
|
+
from nectar.account import Account
|
|
9
|
+
from nectar.amount import Amount
|
|
10
|
+
from nectar.constants import STEEM_100_PERCENT, STEEM_VOTE_REGENERATION_SECONDS
|
|
11
|
+
from nectar.instance import shared_blockchain_instance
|
|
12
|
+
from nectar.utils import (
|
|
13
|
+
addTzInfo,
|
|
14
|
+
formatTimeString,
|
|
15
|
+
parse_time,
|
|
16
|
+
reputation_to_score,
|
|
17
|
+
)
|
|
18
|
+
from nectar.vote import Vote
|
|
19
|
+
|
|
20
|
+
log = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AccountSnapshot(list):
|
|
24
|
+
"""This class allows to easily access Account history
|
|
25
|
+
|
|
26
|
+
:param str account_name: Name of the account
|
|
27
|
+
:param Steem blockchain_instance: Steem
|
|
28
|
+
instance
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, account, account_history=[], blockchain_instance=None, **kwargs):
|
|
32
|
+
if blockchain_instance is None:
|
|
33
|
+
if kwargs.get("steem_instance"):
|
|
34
|
+
blockchain_instance = kwargs["steem_instance"]
|
|
35
|
+
elif kwargs.get("hive_instance"):
|
|
36
|
+
blockchain_instance = kwargs["hive_instance"]
|
|
37
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
38
|
+
self.account = Account(account, blockchain_instance=self.blockchain)
|
|
39
|
+
self.reset()
|
|
40
|
+
super(AccountSnapshot, self).__init__(account_history)
|
|
41
|
+
|
|
42
|
+
def reset(self):
|
|
43
|
+
"""Resets the arrays not the stored account history"""
|
|
44
|
+
self.own_vests = [
|
|
45
|
+
Amount(0, self.blockchain.vest_token_symbol, blockchain_instance=self.blockchain)
|
|
46
|
+
]
|
|
47
|
+
self.own_steem = [
|
|
48
|
+
Amount(0, self.blockchain.token_symbol, blockchain_instance=self.blockchain)
|
|
49
|
+
]
|
|
50
|
+
self.own_sbd = [
|
|
51
|
+
Amount(0, self.blockchain.backed_token_symbol, blockchain_instance=self.blockchain)
|
|
52
|
+
]
|
|
53
|
+
self.delegated_vests_in = [{}]
|
|
54
|
+
self.delegated_vests_out = [{}]
|
|
55
|
+
self.timestamps = [addTzInfo(datetime(1970, 1, 1, 0, 0, 0, 0))]
|
|
56
|
+
self.ops_statistics = {}
|
|
57
|
+
for key in self.blockchain.get_operation_names():
|
|
58
|
+
self.ops_statistics[key] = 0
|
|
59
|
+
self.reward_timestamps = []
|
|
60
|
+
self.author_rewards = []
|
|
61
|
+
self.curation_rewards = []
|
|
62
|
+
self.curation_per_1000_SP_timestamp = []
|
|
63
|
+
self.curation_per_1000_SP = []
|
|
64
|
+
self.out_vote_timestamp = []
|
|
65
|
+
self.out_vote_weight = []
|
|
66
|
+
self.in_vote_timestamp = []
|
|
67
|
+
self.in_vote_weight = []
|
|
68
|
+
self.in_vote_rep = []
|
|
69
|
+
self.in_vote_rshares = []
|
|
70
|
+
self.vp = []
|
|
71
|
+
self.vp_timestamp = []
|
|
72
|
+
self.downvote_vp = []
|
|
73
|
+
self.downvote_vp_timestamp = []
|
|
74
|
+
self.rep = []
|
|
75
|
+
self.rep_timestamp = []
|
|
76
|
+
|
|
77
|
+
def search(self, search_str, start=None, stop=None, use_block_num=True):
|
|
78
|
+
"""Returns ops in the given range"""
|
|
79
|
+
ops = []
|
|
80
|
+
if start is not None:
|
|
81
|
+
start = addTzInfo(start)
|
|
82
|
+
if stop is not None:
|
|
83
|
+
stop = addTzInfo(stop)
|
|
84
|
+
for op in self:
|
|
85
|
+
if use_block_num and start is not None and isinstance(start, int):
|
|
86
|
+
if op["block"] < start:
|
|
87
|
+
continue
|
|
88
|
+
elif not use_block_num and start is not None and isinstance(start, int):
|
|
89
|
+
if op["index"] < start:
|
|
90
|
+
continue
|
|
91
|
+
elif start is not None and isinstance(start, (datetime, date, time)):
|
|
92
|
+
if start > formatTimeString(op["timestamp"]):
|
|
93
|
+
continue
|
|
94
|
+
if use_block_num and stop is not None and isinstance(stop, int):
|
|
95
|
+
if op["block"] > stop:
|
|
96
|
+
continue
|
|
97
|
+
elif not use_block_num and stop is not None and isinstance(stop, int):
|
|
98
|
+
if op["index"] > stop:
|
|
99
|
+
continue
|
|
100
|
+
elif stop is not None and isinstance(stop, (datetime, date, time)):
|
|
101
|
+
if stop < formatTimeString(op["timestamp"]):
|
|
102
|
+
continue
|
|
103
|
+
op_string = json.dumps(list(op.values()))
|
|
104
|
+
if re.search(search_str, op_string):
|
|
105
|
+
ops.append(op)
|
|
106
|
+
return ops
|
|
107
|
+
|
|
108
|
+
def get_ops(self, start=None, stop=None, use_block_num=True, only_ops=[], exclude_ops=[]):
|
|
109
|
+
"""Returns ops in the given range"""
|
|
110
|
+
if start is not None:
|
|
111
|
+
start = addTzInfo(start)
|
|
112
|
+
if stop is not None:
|
|
113
|
+
stop = addTzInfo(stop)
|
|
114
|
+
for op in self:
|
|
115
|
+
if use_block_num and start is not None and isinstance(start, int):
|
|
116
|
+
if op["block"] < start:
|
|
117
|
+
continue
|
|
118
|
+
elif not use_block_num and start is not None and isinstance(start, int):
|
|
119
|
+
if op["index"] < start:
|
|
120
|
+
continue
|
|
121
|
+
elif start is not None and isinstance(start, (datetime, date, time)):
|
|
122
|
+
if start > formatTimeString(op["timestamp"]):
|
|
123
|
+
continue
|
|
124
|
+
if use_block_num and stop is not None and isinstance(stop, int):
|
|
125
|
+
if op["block"] > stop:
|
|
126
|
+
continue
|
|
127
|
+
elif not use_block_num and stop is not None and isinstance(stop, int):
|
|
128
|
+
if op["index"] > stop:
|
|
129
|
+
continue
|
|
130
|
+
elif stop is not None and isinstance(stop, (datetime, date, time)):
|
|
131
|
+
if stop < formatTimeString(op["timestamp"]):
|
|
132
|
+
continue
|
|
133
|
+
if exclude_ops and op["type"] in exclude_ops:
|
|
134
|
+
continue
|
|
135
|
+
if not only_ops or op["type"] in only_ops:
|
|
136
|
+
yield op
|
|
137
|
+
|
|
138
|
+
def get_data(self, timestamp=None, index=0):
|
|
139
|
+
"""Returns snapshot for given timestamp"""
|
|
140
|
+
if timestamp is None:
|
|
141
|
+
timestamp = datetime.now(timezone.utc)
|
|
142
|
+
timestamp = addTzInfo(timestamp)
|
|
143
|
+
# Find rightmost value less than x
|
|
144
|
+
i = bisect_left(self.timestamps, timestamp)
|
|
145
|
+
if i:
|
|
146
|
+
index = i - 1
|
|
147
|
+
else:
|
|
148
|
+
return {}
|
|
149
|
+
ts = self.timestamps[index]
|
|
150
|
+
own = self.own_vests[index]
|
|
151
|
+
din = self.delegated_vests_in[index]
|
|
152
|
+
dout = self.delegated_vests_out[index]
|
|
153
|
+
steem = self.own_steem[index]
|
|
154
|
+
sbd = self.own_sbd[index]
|
|
155
|
+
sum_in = sum([din[key].amount for key in din])
|
|
156
|
+
sum_out = sum([dout[key].amount for key in dout])
|
|
157
|
+
from nectar import Steem
|
|
158
|
+
|
|
159
|
+
if isinstance(self.blockchain, Steem):
|
|
160
|
+
sp_in = self.blockchain.vests_to_sp(sum_in, timestamp=ts)
|
|
161
|
+
sp_out = self.blockchain.vests_to_sp(sum_out, timestamp=ts)
|
|
162
|
+
sp_own = self.blockchain.vests_to_sp(own, timestamp=ts)
|
|
163
|
+
else:
|
|
164
|
+
sp_in = self.blockchain.vests_to_hp(sum_in, timestamp=ts)
|
|
165
|
+
sp_out = self.blockchain.vests_to_hp(sum_out, timestamp=ts)
|
|
166
|
+
sp_own = self.blockchain.vests_to_hp(own, timestamp=ts)
|
|
167
|
+
sp_eff = sp_own + sp_in - sp_out
|
|
168
|
+
return {
|
|
169
|
+
"timestamp": ts,
|
|
170
|
+
"vests": own,
|
|
171
|
+
"delegated_vests_in": din,
|
|
172
|
+
"delegated_vests_out": dout,
|
|
173
|
+
"sp_own": sp_own,
|
|
174
|
+
"sp_eff": sp_eff,
|
|
175
|
+
"steem": steem,
|
|
176
|
+
"sbd": sbd,
|
|
177
|
+
"index": index,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
def get_account_history(self, start=None, stop=None, use_block_num=True):
|
|
181
|
+
"""Uses account history to fetch all related ops
|
|
182
|
+
|
|
183
|
+
:param start: start number/date of transactions to
|
|
184
|
+
return (*optional*)
|
|
185
|
+
:type start: int, datetime
|
|
186
|
+
:param stop: stop number/date of transactions to
|
|
187
|
+
return (*optional*)
|
|
188
|
+
:type stop: int, datetime
|
|
189
|
+
:param bool use_block_num: if true, start and stop are block numbers,
|
|
190
|
+
otherwise virtual OP count numbers.
|
|
191
|
+
|
|
192
|
+
"""
|
|
193
|
+
super(AccountSnapshot, self).__init__(
|
|
194
|
+
[h for h in self.account.history(start=start, stop=stop, use_block_num=use_block_num)]
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def update_rewards(self, timestamp, curation_reward, author_vests, author_steem, author_sbd):
|
|
198
|
+
self.reward_timestamps.append(timestamp)
|
|
199
|
+
self.curation_rewards.append(curation_reward)
|
|
200
|
+
self.author_rewards.append(
|
|
201
|
+
{"vests": author_vests, "steem": author_steem, "sbd": author_sbd}
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def update_out_vote(self, timestamp, weight):
|
|
205
|
+
self.out_vote_timestamp.append(timestamp)
|
|
206
|
+
self.out_vote_weight.append(weight)
|
|
207
|
+
|
|
208
|
+
def update_in_vote(self, timestamp, weight, op):
|
|
209
|
+
v = Vote(op)
|
|
210
|
+
try:
|
|
211
|
+
v.refresh()
|
|
212
|
+
self.in_vote_timestamp.append(timestamp)
|
|
213
|
+
self.in_vote_weight.append(weight)
|
|
214
|
+
self.in_vote_rep.append(int(v["reputation"]))
|
|
215
|
+
self.in_vote_rshares.append(int(v["rshares"]))
|
|
216
|
+
except Exception:
|
|
217
|
+
print("Could not find: %s" % v)
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
def update(self, timestamp, own, delegated_in=None, delegated_out=None, steem=0, sbd=0):
|
|
221
|
+
"""Updates the internal state arrays
|
|
222
|
+
|
|
223
|
+
:param datetime timestamp: datetime of the update
|
|
224
|
+
:param own: vests
|
|
225
|
+
:type own: amount.Amount, float
|
|
226
|
+
:param dict delegated_in: Incoming delegation
|
|
227
|
+
:param dict delegated_out: Outgoing delegation
|
|
228
|
+
:param steem: steem
|
|
229
|
+
:type steem: amount.Amount, float
|
|
230
|
+
:param sbd: sbd
|
|
231
|
+
:type sbd: amount.Amount, float
|
|
232
|
+
|
|
233
|
+
"""
|
|
234
|
+
self.timestamps.append(timestamp - timedelta(seconds=1))
|
|
235
|
+
self.own_vests.append(self.own_vests[-1])
|
|
236
|
+
self.own_steem.append(self.own_steem[-1])
|
|
237
|
+
self.own_sbd.append(self.own_sbd[-1])
|
|
238
|
+
self.delegated_vests_in.append(self.delegated_vests_in[-1])
|
|
239
|
+
self.delegated_vests_out.append(self.delegated_vests_out[-1])
|
|
240
|
+
|
|
241
|
+
self.timestamps.append(timestamp)
|
|
242
|
+
self.own_vests.append(self.own_vests[-1] + own)
|
|
243
|
+
self.own_steem.append(self.own_steem[-1] + steem)
|
|
244
|
+
self.own_sbd.append(self.own_sbd[-1] + sbd)
|
|
245
|
+
|
|
246
|
+
new_deleg = dict(self.delegated_vests_in[-1])
|
|
247
|
+
if delegated_in is not None and delegated_in:
|
|
248
|
+
if delegated_in["amount"] == 0:
|
|
249
|
+
del new_deleg[delegated_in["account"]]
|
|
250
|
+
else:
|
|
251
|
+
new_deleg[delegated_in["account"]] = delegated_in["amount"]
|
|
252
|
+
self.delegated_vests_in.append(new_deleg)
|
|
253
|
+
|
|
254
|
+
new_deleg = dict(self.delegated_vests_out[-1])
|
|
255
|
+
if delegated_out is not None and delegated_out:
|
|
256
|
+
if delegated_out["account"] is None:
|
|
257
|
+
# return_vesting_delegation
|
|
258
|
+
for delegatee in new_deleg:
|
|
259
|
+
if new_deleg[delegatee]["amount"] == delegated_out["amount"]:
|
|
260
|
+
del new_deleg[delegatee]
|
|
261
|
+
break
|
|
262
|
+
|
|
263
|
+
elif delegated_out["amount"] != 0:
|
|
264
|
+
# new or updated non-zero delegation
|
|
265
|
+
new_deleg[delegated_out["account"]] = delegated_out["amount"]
|
|
266
|
+
# TODO
|
|
267
|
+
# skip undelegations here, wait for 'return_vesting_delegation'
|
|
268
|
+
# del new_deleg[delegated_out['account']]
|
|
269
|
+
|
|
270
|
+
self.delegated_vests_out.append(new_deleg)
|
|
271
|
+
|
|
272
|
+
def build(
|
|
273
|
+
self,
|
|
274
|
+
only_ops=[],
|
|
275
|
+
exclude_ops=[],
|
|
276
|
+
enable_rewards=False,
|
|
277
|
+
enable_out_votes=False,
|
|
278
|
+
enable_in_votes=False,
|
|
279
|
+
):
|
|
280
|
+
"""Builds the account history based on all account operations
|
|
281
|
+
|
|
282
|
+
:param array only_ops: Limit generator by these
|
|
283
|
+
operations (*optional*)
|
|
284
|
+
:param array exclude_ops: Exclude these operations from
|
|
285
|
+
generator (*optional*)
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
if len(self.timestamps) > 0:
|
|
289
|
+
start_timestamp = self.timestamps[-1]
|
|
290
|
+
else:
|
|
291
|
+
start_timestamp = None
|
|
292
|
+
for op in sorted(self, key=lambda k: k["timestamp"]):
|
|
293
|
+
ts = parse_time(op["timestamp"])
|
|
294
|
+
if start_timestamp is not None and start_timestamp > ts:
|
|
295
|
+
continue
|
|
296
|
+
if op["type"] in exclude_ops:
|
|
297
|
+
continue
|
|
298
|
+
if len(only_ops) > 0 and op["type"] not in only_ops:
|
|
299
|
+
continue
|
|
300
|
+
self.ops_statistics[op["type"]] += 1
|
|
301
|
+
self.parse_op(
|
|
302
|
+
op,
|
|
303
|
+
only_ops=only_ops,
|
|
304
|
+
enable_rewards=enable_rewards,
|
|
305
|
+
enable_out_votes=enable_out_votes,
|
|
306
|
+
enable_in_votes=enable_in_votes,
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def parse_op(
|
|
310
|
+
self, op, only_ops=[], enable_rewards=False, enable_out_votes=False, enable_in_votes=False
|
|
311
|
+
):
|
|
312
|
+
"""Parse account history operation"""
|
|
313
|
+
ts = parse_time(op["timestamp"])
|
|
314
|
+
|
|
315
|
+
if op["type"] == "account_create":
|
|
316
|
+
fee_steem = Amount(op["fee"], blockchain_instance=self.blockchain).amount
|
|
317
|
+
fee_vests = self.blockchain.sp_to_vests(
|
|
318
|
+
Amount(op["fee"], blockchain_instance=self.blockchain).amount, timestamp=ts
|
|
319
|
+
)
|
|
320
|
+
if op["new_account_name"] == self.account["name"]:
|
|
321
|
+
self.update(ts, fee_vests, 0, 0)
|
|
322
|
+
return
|
|
323
|
+
if op["creator"] == self.account["name"]:
|
|
324
|
+
self.update(ts, 0, 0, 0, fee_steem * (-1), 0)
|
|
325
|
+
return
|
|
326
|
+
|
|
327
|
+
elif op["type"] == "account_create_with_delegation":
|
|
328
|
+
fee_steem = Amount(op["fee"], blockchain_instance=self.blockchain).amount
|
|
329
|
+
from nectar import Steem
|
|
330
|
+
|
|
331
|
+
if isinstance(self.blockchain, Steem):
|
|
332
|
+
fee_vests = self.blockchain.sp_to_vests(
|
|
333
|
+
Amount(op["fee"], blockchain_instance=self.blockchain).amount, timestamp=ts
|
|
334
|
+
)
|
|
335
|
+
else:
|
|
336
|
+
fee_vests = self.blockchain.hp_to_vests(
|
|
337
|
+
Amount(op["fee"], blockchain_instance=self.blockchain).amount, timestamp=ts
|
|
338
|
+
)
|
|
339
|
+
if op["new_account_name"] == self.account["name"]:
|
|
340
|
+
if Amount(op["delegation"], blockchain_instance=self.blockchain).amount > 0:
|
|
341
|
+
delegation = {
|
|
342
|
+
"account": op["creator"],
|
|
343
|
+
"amount": Amount(op["delegation"], blockchain_instance=self.blockchain),
|
|
344
|
+
}
|
|
345
|
+
else:
|
|
346
|
+
delegation = None
|
|
347
|
+
self.update(ts, fee_vests, delegation, 0)
|
|
348
|
+
return
|
|
349
|
+
|
|
350
|
+
if op["creator"] == self.account["name"]:
|
|
351
|
+
delegation = {
|
|
352
|
+
"account": op["new_account_name"],
|
|
353
|
+
"amount": Amount(op["delegation"], blockchain_instance=self.blockchain),
|
|
354
|
+
}
|
|
355
|
+
self.update(ts, 0, 0, delegation, fee_steem * (-1), 0)
|
|
356
|
+
return
|
|
357
|
+
|
|
358
|
+
elif op["type"] == "delegate_vesting_shares":
|
|
359
|
+
vests = Amount(op["vesting_shares"], blockchain_instance=self.blockchain)
|
|
360
|
+
if op["delegator"] == self.account["name"]:
|
|
361
|
+
delegation = {"account": op["delegatee"], "amount": vests}
|
|
362
|
+
self.update(ts, 0, 0, delegation)
|
|
363
|
+
return
|
|
364
|
+
if op["delegatee"] == self.account["name"]:
|
|
365
|
+
delegation = {"account": op["delegator"], "amount": vests}
|
|
366
|
+
self.update(ts, 0, delegation, 0)
|
|
367
|
+
return
|
|
368
|
+
|
|
369
|
+
elif op["type"] == "transfer":
|
|
370
|
+
amount = Amount(op["amount"], blockchain_instance=self.blockchain)
|
|
371
|
+
if op["from"] == self.account["name"]:
|
|
372
|
+
if amount.symbol == self.blockchain.blockchain_symbol:
|
|
373
|
+
self.update(ts, 0, 0, 0, amount * (-1), 0)
|
|
374
|
+
elif amount.symbol == self.blockchain.backed_token_symbol:
|
|
375
|
+
self.update(ts, 0, 0, 0, 0, amount * (-1))
|
|
376
|
+
if op["to"] == self.account["name"]:
|
|
377
|
+
if amount.symbol == self.blockchain.blockchain_symbol:
|
|
378
|
+
self.update(ts, 0, 0, 0, amount, 0)
|
|
379
|
+
elif amount.symbol == self.blockchain.backed_token_symbol:
|
|
380
|
+
self.update(ts, 0, 0, 0, 0, amount)
|
|
381
|
+
return
|
|
382
|
+
|
|
383
|
+
elif op["type"] == "fill_order":
|
|
384
|
+
current_pays = Amount(op["current_pays"], blockchain_instance=self.blockchain)
|
|
385
|
+
open_pays = Amount(op["open_pays"], blockchain_instance=self.blockchain)
|
|
386
|
+
if op["current_owner"] == self.account["name"]:
|
|
387
|
+
if current_pays.symbol == self.blockchain.token_symbol:
|
|
388
|
+
self.update(ts, 0, 0, 0, current_pays * (-1), open_pays)
|
|
389
|
+
elif current_pays.symbol == self.blockchain.backed_token_symbol:
|
|
390
|
+
self.update(ts, 0, 0, 0, open_pays, current_pays * (-1))
|
|
391
|
+
if op["open_owner"] == self.account["name"]:
|
|
392
|
+
if current_pays.symbol == self.blockchain.token_symbol:
|
|
393
|
+
self.update(ts, 0, 0, 0, current_pays, open_pays * (-1))
|
|
394
|
+
elif current_pays.symbol == self.blockchain.backed_token_symbol:
|
|
395
|
+
self.update(ts, 0, 0, 0, open_pays * (-1), current_pays)
|
|
396
|
+
return
|
|
397
|
+
|
|
398
|
+
elif op["type"] == "transfer_to_vesting":
|
|
399
|
+
steem = Amount(op["amount"], blockchain_instance=self.blockchain)
|
|
400
|
+
from nectar import Steem
|
|
401
|
+
|
|
402
|
+
if isinstance(self.blockchain, Steem):
|
|
403
|
+
vests = self.blockchain.sp_to_vests(steem.amount, timestamp=ts)
|
|
404
|
+
else:
|
|
405
|
+
vests = self.blockchain.hp_to_vests(steem.amount, timestamp=ts)
|
|
406
|
+
if op["from"] == self.account["name"] and op["to"] == self.account["name"]:
|
|
407
|
+
self.update(ts, vests, 0, 0, steem * (-1), 0) # power up from and to given account
|
|
408
|
+
elif op["from"] != self.account["name"] and op["to"] == self.account["name"]:
|
|
409
|
+
self.update(ts, vests, 0, 0, 0, 0) # power up from another account
|
|
410
|
+
else: # op['from'] == self.account["name"] and op['to'] != self.account["name"]
|
|
411
|
+
self.update(ts, 0, 0, 0, steem * (-1), 0) # power up to another account
|
|
412
|
+
return
|
|
413
|
+
|
|
414
|
+
elif op["type"] == "fill_vesting_withdraw":
|
|
415
|
+
vests = Amount(op["withdrawn"], blockchain_instance=self.blockchain)
|
|
416
|
+
self.update(ts, vests * (-1), 0, 0)
|
|
417
|
+
return
|
|
418
|
+
|
|
419
|
+
elif op["type"] == "return_vesting_delegation":
|
|
420
|
+
delegation = {
|
|
421
|
+
"account": None,
|
|
422
|
+
"amount": Amount(op["vesting_shares"], blockchain_instance=self.blockchain),
|
|
423
|
+
}
|
|
424
|
+
self.update(ts, 0, 0, delegation)
|
|
425
|
+
return
|
|
426
|
+
|
|
427
|
+
elif op["type"] == "claim_reward_balance":
|
|
428
|
+
vests = Amount(op["reward_vests"], blockchain_instance=self.blockchain)
|
|
429
|
+
steem = Amount(op["reward_steem"], blockchain_instance=self.blockchain)
|
|
430
|
+
sbd = Amount(op["reward_sbd"], blockchain_instance=self.blockchain)
|
|
431
|
+
self.update(ts, vests, 0, 0, steem, sbd)
|
|
432
|
+
return
|
|
433
|
+
|
|
434
|
+
elif op["type"] == "curation_reward":
|
|
435
|
+
if "curation_reward" in only_ops or enable_rewards:
|
|
436
|
+
vests = Amount(op["reward"], blockchain_instance=self.blockchain)
|
|
437
|
+
if "curation_reward" in only_ops:
|
|
438
|
+
self.update(ts, vests, 0, 0)
|
|
439
|
+
if enable_rewards:
|
|
440
|
+
self.update_rewards(ts, vests, 0, 0, 0)
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
elif op["type"] == "author_reward":
|
|
444
|
+
if "author_reward" in only_ops or enable_rewards:
|
|
445
|
+
vests = Amount(op["vesting_payout"], blockchain_instance=self.blockchain)
|
|
446
|
+
steem = Amount(op["steem_payout"], blockchain_instance=self.blockchain)
|
|
447
|
+
sbd = Amount(op["sbd_payout"], blockchain_instance=self.blockchain)
|
|
448
|
+
if "author_reward" in only_ops:
|
|
449
|
+
self.update(ts, vests, 0, 0, steem, sbd)
|
|
450
|
+
if enable_rewards:
|
|
451
|
+
self.update_rewards(ts, 0, vests, steem, sbd)
|
|
452
|
+
return
|
|
453
|
+
|
|
454
|
+
elif op["type"] == "producer_reward":
|
|
455
|
+
vests = Amount(op["vesting_shares"], blockchain_instance=self.blockchain)
|
|
456
|
+
self.update(ts, vests, 0, 0)
|
|
457
|
+
return
|
|
458
|
+
|
|
459
|
+
elif op["type"] == "comment_benefactor_reward":
|
|
460
|
+
if op["benefactor"] == self.account["name"]:
|
|
461
|
+
if "reward" in op:
|
|
462
|
+
vests = Amount(op["reward"], blockchain_instance=self.blockchain)
|
|
463
|
+
self.update(ts, vests, 0, 0)
|
|
464
|
+
else:
|
|
465
|
+
vests = Amount(op["vesting_payout"], blockchain_instance=self.blockchain)
|
|
466
|
+
steem = Amount(op["steem_payout"], blockchain_instance=self.blockchain)
|
|
467
|
+
sbd = Amount(op["sbd_payout"], blockchain_instance=self.blockchain)
|
|
468
|
+
self.update(ts, vests, 0, 0, steem, sbd)
|
|
469
|
+
return
|
|
470
|
+
else:
|
|
471
|
+
return
|
|
472
|
+
|
|
473
|
+
elif op["type"] == "fill_convert_request":
|
|
474
|
+
amount_in = Amount(op["amount_in"], blockchain_instance=self.blockchain)
|
|
475
|
+
amount_out = Amount(op["amount_out"], blockchain_instance=self.blockchain)
|
|
476
|
+
if op["owner"] == self.account["name"]:
|
|
477
|
+
self.update(ts, 0, 0, 0, amount_out, amount_in * (-1))
|
|
478
|
+
return
|
|
479
|
+
|
|
480
|
+
elif op["type"] == "interest":
|
|
481
|
+
interest = Amount(op["interest"], blockchain_instance=self.blockchain)
|
|
482
|
+
self.update(ts, 0, 0, 0, 0, interest)
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
elif op["type"] == "vote":
|
|
486
|
+
if "vote" in only_ops or enable_out_votes:
|
|
487
|
+
weight = int(op["weight"])
|
|
488
|
+
if op["voter"] == self.account["name"]:
|
|
489
|
+
self.update_out_vote(ts, weight)
|
|
490
|
+
if "vote" in only_ops or enable_in_votes and op["author"] == self.account["name"]:
|
|
491
|
+
weight = int(op["weight"])
|
|
492
|
+
self.update_in_vote(ts, weight, op)
|
|
493
|
+
return
|
|
494
|
+
|
|
495
|
+
elif op["type"] == "hardfork_hive":
|
|
496
|
+
vests = Amount(op["vests_converted"])
|
|
497
|
+
hbd = Amount(op["steem_transferred"])
|
|
498
|
+
hive = Amount(op["sbd_transferred"])
|
|
499
|
+
self.update(ts, vests * (-1), 0, 0, hive * (-1), hbd * (-1))
|
|
500
|
+
|
|
501
|
+
elif op["type"] in [
|
|
502
|
+
"comment",
|
|
503
|
+
"feed_publish",
|
|
504
|
+
"shutdown_witness",
|
|
505
|
+
"account_witness_vote",
|
|
506
|
+
"witness_update",
|
|
507
|
+
"custom_json",
|
|
508
|
+
"limit_order_create",
|
|
509
|
+
"account_update",
|
|
510
|
+
"account_witness_proxy",
|
|
511
|
+
"limit_order_cancel",
|
|
512
|
+
"comment_options",
|
|
513
|
+
"delete_comment",
|
|
514
|
+
"interest",
|
|
515
|
+
"recover_account",
|
|
516
|
+
"pow",
|
|
517
|
+
"fill_convert_request",
|
|
518
|
+
"convert",
|
|
519
|
+
"request_account_recovery",
|
|
520
|
+
"update_proposal_votes",
|
|
521
|
+
]:
|
|
522
|
+
return
|
|
523
|
+
|
|
524
|
+
def build_sp_arrays(self):
|
|
525
|
+
"""Builds the own_sp and eff_sp array"""
|
|
526
|
+
self.own_sp = []
|
|
527
|
+
self.eff_sp = []
|
|
528
|
+
for ts, own, din, dout in zip(
|
|
529
|
+
self.timestamps, self.own_vests, self.delegated_vests_in, self.delegated_vests_out
|
|
530
|
+
):
|
|
531
|
+
sum_in = sum([din[key].amount for key in din])
|
|
532
|
+
sum_out = sum([dout[key].amount for key in dout])
|
|
533
|
+
from nectar import Steem
|
|
534
|
+
|
|
535
|
+
if isinstance(self.blockchain, Steem):
|
|
536
|
+
sp_in = self.blockchain.vests_to_sp(sum_in, timestamp=ts)
|
|
537
|
+
sp_out = self.blockchain.vests_to_sp(sum_out, timestamp=ts)
|
|
538
|
+
sp_own = self.blockchain.vests_to_sp(own, timestamp=ts)
|
|
539
|
+
else:
|
|
540
|
+
sp_in = self.blockchain.vests_to_hp(sum_in, timestamp=ts)
|
|
541
|
+
sp_out = self.blockchain.vests_to_hp(sum_out, timestamp=ts)
|
|
542
|
+
sp_own = self.blockchain.vests_to_hp(own, timestamp=ts)
|
|
543
|
+
|
|
544
|
+
sp_eff = sp_own + sp_in - sp_out
|
|
545
|
+
self.own_sp.append(sp_own)
|
|
546
|
+
self.eff_sp.append(sp_eff)
|
|
547
|
+
|
|
548
|
+
def build_rep_arrays(self):
|
|
549
|
+
"""Build reputation arrays"""
|
|
550
|
+
self.rep_timestamp = [self.timestamps[1]]
|
|
551
|
+
self.rep = [reputation_to_score(0)]
|
|
552
|
+
current_reputation = 0
|
|
553
|
+
for ts, rshares, rep in zip(self.in_vote_timestamp, self.in_vote_rshares, self.in_vote_rep):
|
|
554
|
+
if rep > 0:
|
|
555
|
+
if rshares > 0 or (rshares < 0 and rep > current_reputation):
|
|
556
|
+
current_reputation += rshares >> 6
|
|
557
|
+
self.rep.append(reputation_to_score(current_reputation))
|
|
558
|
+
self.rep_timestamp.append(ts)
|
|
559
|
+
|
|
560
|
+
def build_vp_arrays(self):
|
|
561
|
+
"""Build vote power arrays"""
|
|
562
|
+
self.vp_timestamp = [self.timestamps[1]]
|
|
563
|
+
self.vp = [STEEM_100_PERCENT]
|
|
564
|
+
HF_21 = datetime(2019, 8, 27, 15, tzinfo=timezone.utc)
|
|
565
|
+
if self.timestamps[1] > HF_21:
|
|
566
|
+
self.downvote_vp_timestamp = [self.timestamps[1]]
|
|
567
|
+
else:
|
|
568
|
+
self.downvote_vp_timestamp = [HF_21]
|
|
569
|
+
self.downvote_vp = [STEEM_100_PERCENT]
|
|
570
|
+
|
|
571
|
+
for ts, weight in zip(self.out_vote_timestamp, self.out_vote_weight):
|
|
572
|
+
regenerated_vp = 0
|
|
573
|
+
if ts > HF_21 and weight < 0:
|
|
574
|
+
self.downvote_vp.append(self.downvote_vp[-1])
|
|
575
|
+
if self.downvote_vp[-1] < STEEM_100_PERCENT:
|
|
576
|
+
regenerated_vp = (
|
|
577
|
+
((ts - self.downvote_vp_timestamp[-1]).total_seconds())
|
|
578
|
+
* STEEM_100_PERCENT
|
|
579
|
+
/ STEEM_VOTE_REGENERATION_SECONDS
|
|
580
|
+
)
|
|
581
|
+
self.downvote_vp[-1] += int(regenerated_vp)
|
|
582
|
+
|
|
583
|
+
if self.downvote_vp[-1] > STEEM_100_PERCENT:
|
|
584
|
+
self.downvote_vp[-1] = STEEM_100_PERCENT
|
|
585
|
+
recharge_time = self.account.get_manabar_recharge_timedelta(
|
|
586
|
+
{"current_mana_pct": self.downvote_vp[-2] / 100}
|
|
587
|
+
)
|
|
588
|
+
# Add full downvote VP once fully charged
|
|
589
|
+
self.downvote_vp_timestamp.append(
|
|
590
|
+
self.downvote_vp_timestamp[-1] + recharge_time
|
|
591
|
+
)
|
|
592
|
+
self.downvote_vp.append(STEEM_100_PERCENT)
|
|
593
|
+
|
|
594
|
+
# Add charged downvote VP just before new Vote
|
|
595
|
+
self.downvote_vp_timestamp.append(ts - timedelta(seconds=1))
|
|
596
|
+
self.downvote_vp.append(
|
|
597
|
+
min([STEEM_100_PERCENT, self.downvote_vp[-1] + regenerated_vp])
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
self.downvote_vp[-1] -= (
|
|
601
|
+
self.blockchain._calc_resulting_vote(STEEM_100_PERCENT, weight) * 4
|
|
602
|
+
)
|
|
603
|
+
# Downvote mana pool is 1/4th of the upvote mana pool, so it gets drained 4 times as quick
|
|
604
|
+
if self.downvote_vp[-1] < 0:
|
|
605
|
+
# There's most likely a better solution to this that what I did here
|
|
606
|
+
self.vp.append(self.vp[-1])
|
|
607
|
+
|
|
608
|
+
if self.vp[-1] < STEEM_100_PERCENT:
|
|
609
|
+
regenerated_vp = (
|
|
610
|
+
((ts - self.vp_timestamp[-1]).total_seconds())
|
|
611
|
+
* STEEM_100_PERCENT
|
|
612
|
+
/ STEEM_VOTE_REGENERATION_SECONDS
|
|
613
|
+
)
|
|
614
|
+
self.vp[-1] += int(regenerated_vp)
|
|
615
|
+
|
|
616
|
+
if self.vp[-1] > STEEM_100_PERCENT:
|
|
617
|
+
self.vp[-1] = STEEM_100_PERCENT
|
|
618
|
+
recharge_time = self.account.get_manabar_recharge_timedelta(
|
|
619
|
+
{"current_mana_pct": self.vp[-2] / 100}
|
|
620
|
+
)
|
|
621
|
+
# Add full VP once fully charged
|
|
622
|
+
self.vp_timestamp.append(self.vp_timestamp[-1] + recharge_time)
|
|
623
|
+
self.vp.append(STEEM_100_PERCENT)
|
|
624
|
+
if self.vp[-1] == STEEM_100_PERCENT and ts - self.vp_timestamp[-1] > timedelta(
|
|
625
|
+
seconds=1
|
|
626
|
+
):
|
|
627
|
+
# Add charged VP just before new Vote
|
|
628
|
+
self.vp_timestamp.append(ts - timedelta(seconds=1))
|
|
629
|
+
self.vp.append(min([STEEM_100_PERCENT, self.vp[-1] + regenerated_vp]))
|
|
630
|
+
self.vp[-1] += self.downvote_vp[-1] / 4
|
|
631
|
+
if self.vp[-1] < 0:
|
|
632
|
+
self.vp[-1] = 0
|
|
633
|
+
|
|
634
|
+
self.vp_timestamp.append(ts)
|
|
635
|
+
self.downvote_vp[-1] = 0
|
|
636
|
+
self.downvote_vp_timestamp.append(ts)
|
|
637
|
+
|
|
638
|
+
else:
|
|
639
|
+
self.vp.append(self.vp[-1])
|
|
640
|
+
|
|
641
|
+
if self.vp[-1] < STEEM_100_PERCENT:
|
|
642
|
+
regenerated_vp = (
|
|
643
|
+
((ts - self.vp_timestamp[-1]).total_seconds())
|
|
644
|
+
* STEEM_100_PERCENT
|
|
645
|
+
/ STEEM_VOTE_REGENERATION_SECONDS
|
|
646
|
+
)
|
|
647
|
+
self.vp[-1] += int(regenerated_vp)
|
|
648
|
+
|
|
649
|
+
if self.vp[-1] > STEEM_100_PERCENT:
|
|
650
|
+
self.vp[-1] = STEEM_100_PERCENT
|
|
651
|
+
recharge_time = self.account.get_manabar_recharge_timedelta(
|
|
652
|
+
{"current_mana_pct": self.vp[-2] / 100}
|
|
653
|
+
)
|
|
654
|
+
# Add full VP once fully charged
|
|
655
|
+
self.vp_timestamp.append(self.vp_timestamp[-1] + recharge_time)
|
|
656
|
+
self.vp.append(STEEM_100_PERCENT)
|
|
657
|
+
if self.vp[-1] == STEEM_100_PERCENT and ts - self.vp_timestamp[-1] > timedelta(
|
|
658
|
+
seconds=1
|
|
659
|
+
):
|
|
660
|
+
# Add charged VP just before new Vote
|
|
661
|
+
self.vp_timestamp.append(ts - timedelta(seconds=1))
|
|
662
|
+
self.vp.append(min([STEEM_100_PERCENT, self.vp[-1] + regenerated_vp]))
|
|
663
|
+
self.vp[-1] -= self.blockchain._calc_resulting_vote(self.vp[-1], weight)
|
|
664
|
+
if self.vp[-1] < 0:
|
|
665
|
+
self.vp[-1] = 0
|
|
666
|
+
|
|
667
|
+
self.vp_timestamp.append(ts)
|
|
668
|
+
|
|
669
|
+
if self.account.get_voting_power() == 100:
|
|
670
|
+
self.vp.append(10000)
|
|
671
|
+
recharge_time = self.account.get_manabar_recharge_timedelta(
|
|
672
|
+
{"current_mana_pct": self.vp[-2] / 100}
|
|
673
|
+
)
|
|
674
|
+
self.vp_timestamp.append(self.vp_timestamp[-1] + recharge_time)
|
|
675
|
+
|
|
676
|
+
if self.account.get_downvoting_power() == 100:
|
|
677
|
+
self.downvote_vp.append(10000)
|
|
678
|
+
recharge_time = self.account.get_manabar_recharge_timedelta(
|
|
679
|
+
{"current_mana_pct": self.downvote_vp[-2] / 100}
|
|
680
|
+
)
|
|
681
|
+
self.downvote_vp_timestamp.append(self.vp_timestamp[-1] + recharge_time)
|
|
682
|
+
|
|
683
|
+
self.vp.append(self.account.get_voting_power() * 100)
|
|
684
|
+
self.downvote_vp.append(self.account.get_downvoting_power() * 100)
|
|
685
|
+
self.downvote_vp_timestamp.append(datetime.now(timezone.utc))
|
|
686
|
+
self.vp_timestamp.append(datetime.now(timezone.utc))
|
|
687
|
+
|
|
688
|
+
def build_curation_arrays(self, end_date=None, sum_days=7):
|
|
689
|
+
"""Build curation arrays"""
|
|
690
|
+
self.curation_per_1000_SP_timestamp = []
|
|
691
|
+
self.curation_per_1000_SP = []
|
|
692
|
+
if sum_days <= 0:
|
|
693
|
+
raise ValueError("sum_days must be greater than 0")
|
|
694
|
+
index = 0
|
|
695
|
+
curation_sum = 0
|
|
696
|
+
days = (self.reward_timestamps[-1] - self.reward_timestamps[0]).days // sum_days * sum_days
|
|
697
|
+
if end_date is None:
|
|
698
|
+
end_date = self.reward_timestamps[-1] - timedelta(days=days)
|
|
699
|
+
for ts, vests in zip(self.reward_timestamps, self.curation_rewards):
|
|
700
|
+
if vests == 0:
|
|
701
|
+
continue
|
|
702
|
+
from nectar import Steem
|
|
703
|
+
|
|
704
|
+
if isinstance(self.blockchain, Steem):
|
|
705
|
+
sp = self.blockchain.vests_to_sp(vests, timestamp=ts)
|
|
706
|
+
else:
|
|
707
|
+
sp = self.blockchain.vests_to_hp(vests, timestamp=ts)
|
|
708
|
+
data = self.get_data(timestamp=ts, index=index)
|
|
709
|
+
index = data["index"]
|
|
710
|
+
if "sp_eff" in data and data["sp_eff"] > 0:
|
|
711
|
+
curation_1k_sp = sp / data["sp_eff"] * 1000 / sum_days * 7
|
|
712
|
+
else:
|
|
713
|
+
curation_1k_sp = 0
|
|
714
|
+
if ts < end_date:
|
|
715
|
+
curation_sum += curation_1k_sp
|
|
716
|
+
else:
|
|
717
|
+
self.curation_per_1000_SP_timestamp.append(end_date)
|
|
718
|
+
self.curation_per_1000_SP.append(curation_sum)
|
|
719
|
+
end_date = end_date + timedelta(days=sum_days)
|
|
720
|
+
curation_sum = 0
|
|
721
|
+
|
|
722
|
+
def __str__(self):
|
|
723
|
+
return self.__repr__()
|
|
724
|
+
|
|
725
|
+
def __repr__(self):
|
|
726
|
+
return "<%s %s>" % (self.__class__.__name__, str(self.account["name"]))
|