hive-nectar 0.2.9__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.
Files changed (87) hide show
  1. hive_nectar-0.2.9.dist-info/METADATA +194 -0
  2. hive_nectar-0.2.9.dist-info/RECORD +87 -0
  3. hive_nectar-0.2.9.dist-info/WHEEL +4 -0
  4. hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
  5. hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
  6. nectar/__init__.py +37 -0
  7. nectar/account.py +5076 -0
  8. nectar/amount.py +553 -0
  9. nectar/asciichart.py +303 -0
  10. nectar/asset.py +122 -0
  11. nectar/block.py +574 -0
  12. nectar/blockchain.py +1242 -0
  13. nectar/blockchaininstance.py +2590 -0
  14. nectar/blockchainobject.py +263 -0
  15. nectar/cli.py +5937 -0
  16. nectar/comment.py +1552 -0
  17. nectar/community.py +854 -0
  18. nectar/constants.py +95 -0
  19. nectar/discussions.py +1437 -0
  20. nectar/exceptions.py +152 -0
  21. nectar/haf.py +381 -0
  22. nectar/hive.py +630 -0
  23. nectar/imageuploader.py +114 -0
  24. nectar/instance.py +113 -0
  25. nectar/market.py +876 -0
  26. nectar/memo.py +542 -0
  27. nectar/message.py +379 -0
  28. nectar/nodelist.py +309 -0
  29. nectar/price.py +603 -0
  30. nectar/profile.py +74 -0
  31. nectar/py.typed +0 -0
  32. nectar/rc.py +333 -0
  33. nectar/snapshot.py +1024 -0
  34. nectar/storage.py +62 -0
  35. nectar/transactionbuilder.py +659 -0
  36. nectar/utils.py +630 -0
  37. nectar/version.py +3 -0
  38. nectar/vote.py +722 -0
  39. nectar/wallet.py +472 -0
  40. nectar/witness.py +728 -0
  41. nectarapi/__init__.py +12 -0
  42. nectarapi/exceptions.py +126 -0
  43. nectarapi/graphenerpc.py +596 -0
  44. nectarapi/node.py +194 -0
  45. nectarapi/noderpc.py +79 -0
  46. nectarapi/openapi.py +107 -0
  47. nectarapi/py.typed +0 -0
  48. nectarapi/rpcutils.py +98 -0
  49. nectarapi/version.py +3 -0
  50. nectarbase/__init__.py +15 -0
  51. nectarbase/ledgertransactions.py +106 -0
  52. nectarbase/memo.py +242 -0
  53. nectarbase/objects.py +521 -0
  54. nectarbase/objecttypes.py +21 -0
  55. nectarbase/operationids.py +102 -0
  56. nectarbase/operations.py +1357 -0
  57. nectarbase/py.typed +0 -0
  58. nectarbase/signedtransactions.py +89 -0
  59. nectarbase/transactions.py +11 -0
  60. nectarbase/version.py +3 -0
  61. nectargraphenebase/__init__.py +27 -0
  62. nectargraphenebase/account.py +1121 -0
  63. nectargraphenebase/aes.py +49 -0
  64. nectargraphenebase/base58.py +197 -0
  65. nectargraphenebase/bip32.py +575 -0
  66. nectargraphenebase/bip38.py +110 -0
  67. nectargraphenebase/chains.py +15 -0
  68. nectargraphenebase/dictionary.py +2 -0
  69. nectargraphenebase/ecdsasig.py +309 -0
  70. nectargraphenebase/objects.py +130 -0
  71. nectargraphenebase/objecttypes.py +8 -0
  72. nectargraphenebase/operationids.py +5 -0
  73. nectargraphenebase/operations.py +25 -0
  74. nectargraphenebase/prefix.py +13 -0
  75. nectargraphenebase/py.typed +0 -0
  76. nectargraphenebase/signedtransactions.py +221 -0
  77. nectargraphenebase/types.py +557 -0
  78. nectargraphenebase/unsignedtransactions.py +288 -0
  79. nectargraphenebase/version.py +3 -0
  80. nectarstorage/__init__.py +57 -0
  81. nectarstorage/base.py +317 -0
  82. nectarstorage/exceptions.py +15 -0
  83. nectarstorage/interfaces.py +244 -0
  84. nectarstorage/masterpassword.py +237 -0
  85. nectarstorage/py.typed +0 -0
  86. nectarstorage/ram.py +27 -0
  87. nectarstorage/sqlite.py +343 -0
nectar/hive.py ADDED
@@ -0,0 +1,630 @@
1
+ import logging
2
+ import math
3
+ from datetime import date, datetime, timezone
4
+ from typing import Any, Dict, Optional, Union
5
+
6
+ from nectar.blockchaininstance import BlockChainInstance
7
+ from nectar.constants import HIVE_100_PERCENT
8
+ from nectar.price import Price
9
+ from nectargraphenebase.chains import known_chains
10
+
11
+ from .amount import Amount
12
+ from .utils import formatToTimeStamp
13
+
14
+ log = logging.getLogger(__name__)
15
+
16
+
17
+ class Hive(BlockChainInstance):
18
+ """Connect to the Hive network.
19
+
20
+ :param str node: Node to connect to *(optional)*
21
+ :param str rpcuser: RPC user *(optional)*
22
+ :param str rpcpassword: RPC password *(optional)*
23
+ :param bool nobroadcast: Do **not** broadcast a transaction!
24
+ *(optional)*
25
+ :param bool unsigned: Do **not** sign a transaction! *(optional)*
26
+ :param bool debug: Enable Debugging *(optional)*
27
+ :param keys: Predefine the wif keys to shortcut the
28
+ wallet database *(optional)*
29
+ :type keys: array, dict, string
30
+ :param wif: Predefine the wif keys to shortcut the
31
+ wallet database *(optional)*
32
+ :type wif: array, dict, string
33
+ :param bool offline: Boolean to prevent connecting to network (defaults
34
+ to ``False``) *(optional)*
35
+ :param int expiration: Delay in seconds until transactions are supposed
36
+ to expire *(optional)* (default is 300)
37
+ :param str blocking: Wait for broadcasted transactions to be included
38
+ in a block and return full transaction (can be "head" or
39
+ "irreversible")
40
+ :param bool bundle: Do not broadcast transactions right away, but allow
41
+ to bundle operations. It is not possible to send out more than one
42
+ vote operation and more than one comment operation in a single broadcast *(optional)*
43
+ :param dict custom_chains: custom chain which should be added to the known chains
44
+
45
+ Three wallet operation modes are possible:
46
+
47
+ * **Wallet Database**: Here, the nectarlibs load the keys from the
48
+ locally stored wallet SQLite database (see ``storage.py``).
49
+ To use this mode, simply call ``Hive()`` without the
50
+ ``keys`` parameter
51
+ * **Providing Keys**: Here, you can provide the keys for
52
+ your accounts manually. All you need to do is add the wif
53
+ keys for the accounts you want to use as a simple array
54
+ using the ``keys`` parameter to ``Hive()``.
55
+ * **Force keys**: This more is for advanced users and
56
+ requires that you know what you are doing. Here, the
57
+ ``keys`` parameter is a dictionary that overwrite the
58
+ ``active``, ``owner``, ``posting`` or ``memo`` keys for
59
+ any account. This mode is only used for *foreign*
60
+ signatures!
61
+
62
+ If no node is provided, it will connect to default nodes from
63
+ nectar.NodeList. Default settings can be changed with:
64
+
65
+ .. code-block:: python
66
+
67
+ hive = Hive(<host>)
68
+
69
+ where ``<host>`` starts with ``https://``, ``ws://`` or ``wss://``.
70
+
71
+ The purpose of this class it to simplify interaction with
72
+ Hive.
73
+
74
+ The idea is to have a class that allows to do this:
75
+
76
+ .. code-block:: python
77
+
78
+ >>> from nectar import Hive
79
+ >>> hive = Hive()
80
+ >>> print(hive.get_blockchain_version()) # doctest: +SKIP
81
+
82
+ This class also deals with edits, votes and reading content.
83
+
84
+ Example for adding a custom chain:
85
+
86
+ .. code-block:: python
87
+
88
+ from nectar import Hive
89
+ hv = Hive(node=["https://mytstnet.com"], custom_chains={"MYTESTNET":
90
+ {'chain_assets': [{'asset': 'HBD', 'id': 0, 'precision': 3, 'symbol': 'HBD'},
91
+ {'asset': 'HIVE', 'id': 1, 'precision': 3, 'symbol': 'HIVE'},
92
+ {'asset': 'VESTS', 'id': 2, 'precision': 6, 'symbol': 'VESTS'}],
93
+ 'chain_id': '79276aea5d4877d9a25892eaa01b0adf019d3e5cb12a97478df3298ccdd01674',
94
+ 'min_version': '0.0.0',
95
+ 'prefix': 'MTN'}
96
+ }
97
+ )
98
+
99
+ """
100
+
101
+ def get_network(
102
+ self, use_stored_data: bool = True, config: Optional[Dict[str, Any]] = None
103
+ ) -> Optional[Dict[str, Any]]:
104
+ """Identify the network
105
+
106
+ :param bool use_stored_data: if True, stored data will be returned. If stored data are
107
+ empty or old, refresh_data() is used.
108
+
109
+ :returns: Network parameters
110
+ :rtype: dictionary
111
+ """
112
+ if use_stored_data:
113
+ self.refresh_data("config")
114
+ return self.data["network"]
115
+
116
+ if self.rpc is None:
117
+ return known_chains["HIVE"]
118
+ try:
119
+ return self.rpc.get_network(props=config)
120
+ except Exception:
121
+ return known_chains["HIVE"]
122
+
123
+ def rshares_to_token_backed_dollar(
124
+ self,
125
+ rshares: Union[int, float],
126
+ not_broadcasted_vote: bool = False,
127
+ use_stored_data: bool = True,
128
+ ) -> float:
129
+ return self.rshares_to_hbd(
130
+ rshares, not_broadcasted_vote=not_broadcasted_vote, use_stored_data=use_stored_data
131
+ )
132
+
133
+ def rshares_to_hbd(
134
+ self,
135
+ rshares: Union[int, float],
136
+ not_broadcasted_vote: bool = False,
137
+ use_stored_data: bool = True,
138
+ ) -> float:
139
+ """Calculates the current HBD value of a vote"""
140
+ payout = float(rshares) * self.get_hbd_per_rshares(
141
+ use_stored_data=use_stored_data,
142
+ not_broadcasted_vote_rshares=rshares if not_broadcasted_vote else 0,
143
+ )
144
+ return payout
145
+
146
+ def get_hbd_per_rshares(
147
+ self, not_broadcasted_vote_rshares: Union[int, float] = 0, use_stored_data: bool = True
148
+ ) -> float:
149
+ """Returns the current rshares to HBD ratio"""
150
+ reward_fund = self.get_reward_funds(use_stored_data=use_stored_data)
151
+ if not reward_fund or not isinstance(reward_fund, dict):
152
+ return 0
153
+ reward_balance = float(Amount(reward_fund["reward_balance"], blockchain_instance=self))
154
+ recent_claims = float(reward_fund["recent_claims"]) + not_broadcasted_vote_rshares
155
+ if recent_claims == 0:
156
+ return 0
157
+ fund_per_share = reward_balance / (recent_claims)
158
+ median_price = self.get_median_price(use_stored_data=use_stored_data)
159
+ # Check if median_price is a valid Price object (not None or raw dict)
160
+ from nectar.price import Price
161
+
162
+ if median_price is None or (
163
+ isinstance(median_price, dict) and not isinstance(median_price, Price)
164
+ ):
165
+ return 0
166
+ HBD_price = float(median_price * Amount(1, self.hive_symbol, blockchain_instance=self))
167
+ return fund_per_share * HBD_price
168
+
169
+ def get_token_per_mvest(
170
+ self, time_stamp: Optional[Union[datetime, int]] = None, use_stored_data: bool = True
171
+ ) -> float:
172
+ return self.get_hive_per_mvest(time_stamp=time_stamp, use_stored_data=use_stored_data)
173
+
174
+ def get_hive_per_mvest(
175
+ self, time_stamp: Optional[Union[datetime, int]] = None, use_stored_data: bool = True
176
+ ) -> float:
177
+ """Returns the MVEST to HIVE ratio
178
+
179
+ :param int time_stamp: (optional) if set, return an estimated
180
+ HIVE per MVEST ratio for the given time stamp. If unset the
181
+ current ratio is returned (default). (can also be a datetime object)
182
+ """
183
+ if self.offline and time_stamp is None:
184
+ time_stamp = datetime.now(timezone.utc)
185
+
186
+ if time_stamp is not None:
187
+ if isinstance(time_stamp, (datetime, date)):
188
+ time_stamp = formatToTimeStamp(time_stamp)
189
+ a = 2.1325476281078992e-05
190
+ b = -31099.685481490847
191
+ a2 = 2.9019227739473682e-07
192
+ b2 = 48.41432402074669
193
+
194
+ if time_stamp < (b2 - b) / (a - a2):
195
+ return a * time_stamp + b
196
+ else:
197
+ return a2 * time_stamp + b2
198
+ global_properties = self.get_dynamic_global_properties(use_stored_data=use_stored_data)
199
+ if not global_properties or not isinstance(global_properties, dict):
200
+ return 0.0
201
+ return float(
202
+ Amount(global_properties["total_vesting_fund_hive"], blockchain_instance=self)
203
+ ) / (
204
+ float(Amount(global_properties["total_vesting_shares"], blockchain_instance=self)) / 1e6
205
+ )
206
+
207
+ def vests_to_hp(
208
+ self,
209
+ vests: Union[Amount, float],
210
+ timestamp: Optional[Union[datetime, int]] = None,
211
+ use_stored_data: bool = True,
212
+ ) -> float:
213
+ """Converts vests to HP
214
+
215
+ :param amount.Amount vests/float vests: Vests to convert
216
+ :param int timestamp: (Optional) Can be used to calculate
217
+ the conversion rate from the past
218
+
219
+ """
220
+ if isinstance(vests, Amount):
221
+ vests = float(vests)
222
+ return float(
223
+ float(vests)
224
+ / 1e6
225
+ * float(self.get_hive_per_mvest(timestamp, use_stored_data=use_stored_data))
226
+ )
227
+
228
+ def vests_to_token_power(
229
+ self,
230
+ vests: Union[Amount, float],
231
+ timestamp: Optional[Union[datetime, int]] = None,
232
+ use_stored_data: bool = True,
233
+ ) -> float:
234
+ return self.vests_to_hp(vests, timestamp=timestamp, use_stored_data=use_stored_data)
235
+
236
+ def hp_to_vests(
237
+ self,
238
+ hp: Union[Amount, float],
239
+ timestamp: Optional[Union[datetime, int]] = None,
240
+ use_stored_data: bool = True,
241
+ ) -> float:
242
+ """Converts HP to vests
243
+
244
+ :param float hp: Hive power to convert
245
+ :param datetime timestamp: (Optional) Can be used to calculate
246
+ the conversion rate from the past
247
+ """
248
+ return (
249
+ float(hp)
250
+ * 1e6
251
+ / float(self.get_hive_per_mvest(timestamp, use_stored_data=use_stored_data))
252
+ )
253
+
254
+ def token_power_to_vests(
255
+ self,
256
+ token_power: Union[Amount, float],
257
+ timestamp: Optional[Union[datetime, int]] = None,
258
+ use_stored_data: bool = True,
259
+ ) -> float:
260
+ return self.hp_to_vests(token_power, timestamp=timestamp, use_stored_data=use_stored_data)
261
+
262
+ def token_power_to_token_backed_dollar(
263
+ self,
264
+ token_power: Union[Amount, float],
265
+ post_rshares: int = 0,
266
+ voting_power: int = HIVE_100_PERCENT,
267
+ vote_pct: int = HIVE_100_PERCENT,
268
+ not_broadcasted_vote: bool = True,
269
+ use_stored_data: bool = True,
270
+ ) -> float:
271
+ """
272
+ Convert token power (Hive Power) to its token-backed dollar equivalent (HBD).
273
+
274
+ Parameters:
275
+ token_power: Hive Power amount (numeric or Amount-like) to convert.
276
+ post_rshares (int): Optional existing rshares on the post to include when estimating payout.
277
+ voting_power (int): Voter's current voting power (use HIVE_100_PERCENT for full power).
278
+ vote_pct (int): Vote weight to apply (use HIVE_100_PERCENT for 100%).
279
+ not_broadcasted_vote (bool): If True, include the vote as not-yet-broadcasted when computing reward pool effects.
280
+ use_stored_data (bool): If True, prefer cached chain data when available.
281
+
282
+ Returns:
283
+ The estimated HBD value (token-backed dollar) corresponding to the provided token power.
284
+ """
285
+ return self.hp_to_hbd(
286
+ token_power,
287
+ post_rshares=post_rshares,
288
+ voting_power=voting_power,
289
+ vote_pct=vote_pct,
290
+ not_broadcasted_vote=not_broadcasted_vote,
291
+ use_stored_data=use_stored_data,
292
+ )
293
+
294
+ def hp_to_hbd(
295
+ self,
296
+ hp: Union[Amount, float],
297
+ post_rshares: int = 0,
298
+ voting_power: int = HIVE_100_PERCENT,
299
+ vote_pct: int = HIVE_100_PERCENT,
300
+ not_broadcasted_vote: bool = True,
301
+ use_stored_data: bool = True,
302
+ ) -> float:
303
+ """
304
+ Convert Hive Power (HP) to the estimated HBD payout this vote would produce.
305
+
306
+ Parameters:
307
+ hp (number): Amount of Hive Power to convert.
308
+ post_rshares (int): Current post rshares to include when computing marginal effect of this vote.
309
+ voting_power (int): Voter's current voting power (100% = 10000).
310
+ vote_pct (int): Vote percentage to apply (100% = 10000).
311
+ not_broadcasted_vote (bool): If True, treat the vote as not yet broadcast — the function will account for the vote reducing the available reward pool when applicable.
312
+ use_stored_data (bool): If True, use cached chain/state data when available.
313
+
314
+ Returns:
315
+ HBD value corresponding to the provided HP under the current reward pool and price conditions (same type/format as the library's Amount/HBD results).
316
+ """
317
+ vesting_shares = int(self.hp_to_vests(hp, use_stored_data=use_stored_data))
318
+ return self.vests_to_hbd(
319
+ vesting_shares,
320
+ post_rshares=post_rshares,
321
+ voting_power=voting_power,
322
+ vote_pct=vote_pct,
323
+ not_broadcasted_vote=not_broadcasted_vote,
324
+ use_stored_data=use_stored_data,
325
+ )
326
+
327
+ def vests_to_hbd(
328
+ self,
329
+ vests: Union[Amount, float],
330
+ post_rshares: int = 0,
331
+ voting_power: int = HIVE_100_PERCENT,
332
+ vote_pct: int = HIVE_100_PERCENT,
333
+ not_broadcasted_vote: bool = True,
334
+ use_stored_data: bool = True,
335
+ ) -> float:
336
+ """
337
+ Convert vesting shares to their equivalent HBD payout for a single vote.
338
+
339
+ Given vesting shares, computes the vote's r-shares (taking into account post r-shares,
340
+ voter power and percentage) and converts that r-shares value to an HBD payout.
341
+
342
+ Parameters:
343
+ vests: Vesting shares to use for the vote (number or Amount-like).
344
+ post_rshares (int): Existing r-shares of the post being voted on; affects the r-shares calculation.
345
+ voting_power (int): Voter's current voting power (units where 100% == HIVE_100_PERCENT).
346
+ vote_pct (int): Vote percentage to apply (units where 100% == HIVE_100_PERCENT).
347
+ not_broadcasted_vote (bool): If True, treat this as a not-yet-broadcast vote (it reduces the effective reward pool);
348
+ if False, treat as already-applied (affects conversion math for very large votes).
349
+ use_stored_data (bool): Whether to use cached chain parameters/reward state or query fresh data.
350
+
351
+ Returns:
352
+ The estimated HBD value (same type returned by rshares_to_hbd) for the vote.
353
+ """
354
+ vote_rshares = self.vests_to_rshares(
355
+ vests, post_rshares=post_rshares, voting_power=voting_power, vote_pct=vote_pct
356
+ )
357
+ return self.rshares_to_hbd(
358
+ vote_rshares, not_broadcasted_vote=not_broadcasted_vote, use_stored_data=use_stored_data
359
+ )
360
+
361
+ def hp_to_rshares(
362
+ self,
363
+ hive_power: Union[Amount, float],
364
+ post_rshares: int = 0,
365
+ voting_power: int = HIVE_100_PERCENT,
366
+ vote_pct: int = HIVE_100_PERCENT,
367
+ use_stored_data: bool = True,
368
+ ) -> float:
369
+ """
370
+ Convert Hive Power (HP) to r-shares used for voting.
371
+
372
+ Given a Hive Power amount, computes the equivalent vesting shares and then the r-shares that a vote with the specified voting_power and vote_pct would produce against a post that currently has post_rshares. `voting_power` and `vote_pct` use the chain-normalized scale (100% == HIVE_100_PERCENT).
373
+
374
+ Parameters:
375
+ hive_power (number): Hive Power (HP) value to convert.
376
+ post_rshares (int, optional): Current r-shares of the post being voted on; used to adjust the resulting r-shares. Defaults to 0.
377
+ voting_power (int, optional): Voter's current voting power on the HIVE_100_PERCENT scale. Defaults to HIVE_100_PERCENT.
378
+ vote_pct (int, optional): Vote percentage to apply on the HIVE_100_PERCENT scale. Defaults to HIVE_100_PERCENT.
379
+ use_stored_data (bool, optional): Whether to use cached chain data when performing conversions. Defaults to True.
380
+
381
+ Returns:
382
+ int: The computed r-shares produced by the specified vote.
383
+ """
384
+ # calculate our account voting shares (from vests)
385
+ vesting_shares = int(self.hp_to_vests(hive_power, use_stored_data=use_stored_data))
386
+ rshares = self.vests_to_rshares(
387
+ vesting_shares,
388
+ post_rshares=post_rshares,
389
+ voting_power=voting_power,
390
+ vote_pct=vote_pct,
391
+ use_stored_data=use_stored_data,
392
+ )
393
+ return rshares
394
+
395
+ def hbd_to_rshares(
396
+ self,
397
+ hbd: Union[str, int, Amount],
398
+ not_broadcasted_vote: bool = False,
399
+ use_stored_data: bool = True,
400
+ ) -> float:
401
+ """Obtain the r-shares from HBD
402
+
403
+ :param hbd: HBD
404
+ :type hbd: str, int, amount.Amount
405
+ :param bool not_broadcasted_vote: not_broadcasted or already broadcasted vote (True = not_broadcasted vote).
406
+ Only impactful for very high amounts of HBD. Slight modification to the value calculation, as the not_broadcasted
407
+ vote rshares decreases the reward pool.
408
+
409
+ """
410
+ if isinstance(hbd, Amount):
411
+ hbd = Amount(hbd, blockchain_instance=self)
412
+ elif isinstance(hbd, str):
413
+ hbd = Amount(hbd, blockchain_instance=self)
414
+ else:
415
+ hbd = Amount(hbd, self.hbd_symbol, blockchain_instance=self)
416
+ if hbd["symbol"] != self.hbd_symbol:
417
+ raise AssertionError("Should input HBD, not any other asset!")
418
+
419
+ # If the vote was already broadcasted we can assume the blockchain values to be true
420
+ if not not_broadcasted_vote:
421
+ return int(float(hbd) / self.get_hbd_per_rshares(use_stored_data=use_stored_data))
422
+
423
+ # If the vote wasn't broadcasted (yet), we have to calculate the rshares while considering
424
+ # the change our vote is causing to the recent_claims. This is more important for really
425
+ # big votes which have a significant impact on the recent_claims.
426
+ reward_fund = self.get_reward_funds(use_stored_data=use_stored_data)
427
+ median_price = self.get_median_price(use_stored_data=use_stored_data)
428
+ if (
429
+ not reward_fund
430
+ or not isinstance(reward_fund, dict)
431
+ or not median_price
432
+ or (isinstance(median_price, dict) and not isinstance(median_price, Price))
433
+ ):
434
+ return int(float(hbd) / self.get_hbd_per_rshares(use_stored_data=use_stored_data))
435
+ recent_claims = int(reward_fund["recent_claims"])
436
+ reward_balance = Amount(reward_fund["reward_balance"], blockchain_instance=self)
437
+ reward_pool_hbd = median_price * reward_balance
438
+ if hbd > reward_pool_hbd:
439
+ raise ValueError("Provided more HBD than available in the reward pool.")
440
+
441
+ # This is the formula we can use to determine the "true" rshares.
442
+ # We get this formula by some math magic using the previous used formulas
443
+ # FundsPerShare = (balance / (claims + newShares)) * Price
444
+ # newShares = amount / FundsPerShare
445
+ # We can now resolve both formulas for FundsPerShare and set the formulas to be equal
446
+ # (balance / (claims + newShares)) * price = amount / newShares
447
+ # Now we resolve for newShares resulting in:
448
+ # newShares = claims * amount / (balance * price - amount)
449
+ rshares = (
450
+ recent_claims
451
+ * float(hbd)
452
+ / ((float(reward_balance) * float(median_price)) - float(hbd))
453
+ )
454
+ return int(rshares)
455
+
456
+ def rshares_to_vote_pct(
457
+ self,
458
+ rshares: Union[int, float],
459
+ post_rshares: int = 0,
460
+ hive_power: Optional[Union[Amount, float]] = None,
461
+ vests: Optional[Union[Amount, float]] = None,
462
+ voting_power: int = HIVE_100_PERCENT,
463
+ use_stored_data: bool = True,
464
+ ) -> float:
465
+ """
466
+ Compute the voting percentage required to achieve a target r-shares value.
467
+
468
+ Given a desired r-shares (positive for upvotes, negative for downvotes) and either
469
+ hive_power or vests (exactly one must be provided), return the voting percentage
470
+ where 100% = 10000. The calculation accounts for post-vote r-shares adjustments,
471
+ current dust-threshold behavior (post-hardfork), and the configured voting power.
472
+
473
+ Parameters:
474
+ rshares (int | float): Target r-shares value (signed).
475
+ post_rshares (int, optional): R-shares already present on the post (positive
476
+ for existing upvotes, negative for downvotes). Defaults to 0.
477
+ hive_power (float, optional): Hive Power to use for the calculation. Provide
478
+ this or `vests`, not both. If given, it is converted to vesting shares.
479
+ vests (int, optional): Vesting shares (in micro-vests, i.e., vest*1e6). Provide
480
+ this or `hive_power`, not both.
481
+ voting_power (int, optional): Voter's current voting power where 100% = 10000.
482
+ Defaults to HIVE_100_PERCENT.
483
+ use_stored_data (bool, optional): Whether to use cached chain properties when
484
+ available. Defaults to True.
485
+
486
+ Returns:
487
+ int: Signed voting percentage required (100% = 10000). The sign matches the
488
+ sign of `rshares`.
489
+
490
+ Raises:
491
+ ValueError: If neither or both of `hive_power` and `vests` are provided.
492
+ """
493
+ if hive_power is None and vests is None:
494
+ raise ValueError("Either hive_power or vests has to be set!")
495
+ if hive_power is not None and vests is not None:
496
+ raise ValueError("Either hive_power or vests has to be set. Not both!")
497
+ if hive_power is not None:
498
+ vests_value = self.hp_to_vests(hive_power, use_stored_data=use_stored_data)
499
+ vests = int(vests_value) if vests_value is not None else 0
500
+
501
+ # Parse version as tuple for reliable comparison
502
+ version_parts = self.hardfork.split(".")
503
+ try:
504
+ major, minor = int(version_parts[0]), int(version_parts[1]) if len(version_parts) > 1 else 0
505
+ except (ValueError, IndexError):
506
+ major, minor = 1, 20 # Default to current behavior
507
+ if (major, minor) >= (1, 20):
508
+ rshares += math.copysign(
509
+ self.get_dust_threshold(use_stored_data=use_stored_data), rshares
510
+ )
511
+
512
+ if post_rshares >= 0 and rshares > 0:
513
+ rshares = math.copysign(
514
+ self._calc_revert_vote_claim(abs(rshares), post_rshares), rshares
515
+ )
516
+ elif post_rshares < 0 and rshares < 0:
517
+ rshares = math.copysign(
518
+ self._calc_revert_vote_claim(abs(rshares), abs(post_rshares)), rshares
519
+ )
520
+ elif post_rshares < 0 and rshares > 0:
521
+ rshares = math.copysign(self._calc_revert_vote_claim(abs(rshares), 0), rshares)
522
+ elif post_rshares > 0 and rshares < 0:
523
+ rshares = math.copysign(
524
+ self._calc_revert_vote_claim(abs(rshares), post_rshares), rshares
525
+ )
526
+
527
+ # Invert the chain-accurate power model used above:
528
+ # rshares ≈ sign * (vests*1e6 * used_power / HIVE_100_PERCENT)
529
+ # used_power ≈ abs(rshares) * HIVE_100_PERCENT / (vests*1e6)
530
+ # and used_power ≈ ceil((voting_power * abs(vote_pct) / HIVE_100_PERCENT * 86400) / max_vote_denom)
531
+ if vests == 0 or voting_power == 0:
532
+ return 0
533
+ max_vote_denom = self._max_vote_denom(use_stored_data=use_stored_data)
534
+ vests_value = vests if vests is not None else 0
535
+ used_power_est = (abs(rshares) * HIVE_100_PERCENT) / (vests_value * 1e6)
536
+ # Invert the linear relation (ignoring ceil):
537
+ vote_pct_abs = used_power_est * max_vote_denom * HIVE_100_PERCENT / (86400 * voting_power)
538
+ return round(math.copysign(vote_pct_abs, rshares))
539
+
540
+ def hbd_to_vote_pct(
541
+ self,
542
+ hbd: Union[str, int, Amount],
543
+ post_rshares: int = 0,
544
+ hive_power: Optional[Union[Amount, float]] = None,
545
+ vests: Optional[Union[Amount, float]] = None,
546
+ voting_power: int = HIVE_100_PERCENT,
547
+ not_broadcasted_vote: bool = True,
548
+ use_stored_data: bool = True,
549
+ ) -> float:
550
+ """
551
+ Calculate the voting percentage required to achieve a target HBD payout for a given voting power and stake.
552
+
553
+ Given a desired HBD amount, this returns the vote percentage (100% == 10000) that, when applied from the provided Hive Power or vesting shares, would produce approximately that payout. Exactly one of `hive_power` or `vests` must be provided.
554
+
555
+ Parameters:
556
+ hbd (str|int|Amount): Desired HBD payout. Accepts an Amount, numeric value, or asset string; will be converted to an Amount in the chain's HBD symbol.
557
+ hive_power (number, optional): Voter's Hive Power. Mutually exclusive with `vests`.
558
+ vests (number, optional): Voter's vesting shares. Mutually exclusive with `hive_power`.
559
+ voting_power (int, optional): Current voting power normalization constant (default HIVE_100_PERCENT).
560
+ not_broadcasted_vote (bool, optional): If True, treat the vote as not yet broadcast; this slightly changes calculations for very large HBD amounts because an unbroadcasted vote reduces the available reward pool.
561
+ post_rshares (int, optional): rshares already present on the post (used when calculating required vote to reach a target).
562
+ use_stored_data (bool, optional): Use cached chain properties when available.
563
+
564
+ Returns:
565
+ int: Required vote percentage where 100% == 10000. Values >10000 or < -10000 indicate the requested HBD is too large for a single vote.
566
+
567
+ Raises:
568
+ AssertionError: If the provided `hbd` cannot be interpreted as the chain's HBD asset.
569
+ """
570
+ if isinstance(hbd, Amount):
571
+ hbd = Amount(hbd, blockchain_instance=self)
572
+ elif isinstance(hbd, str):
573
+ hbd = Amount(hbd, blockchain_instance=self)
574
+ else:
575
+ hbd = Amount(hbd, self.hbd_symbol, blockchain_instance=self)
576
+ if hbd["symbol"] != self.hbd_symbol:
577
+ raise AssertionError()
578
+ rshares = self.hbd_to_rshares(
579
+ hbd, not_broadcasted_vote=not_broadcasted_vote, use_stored_data=use_stored_data
580
+ )
581
+ return self.rshares_to_vote_pct(
582
+ rshares,
583
+ post_rshares=post_rshares,
584
+ hive_power=hive_power,
585
+ vests=vests,
586
+ voting_power=voting_power,
587
+ use_stored_data=use_stored_data,
588
+ )
589
+
590
+ @property
591
+ def chain_params(self) -> Optional[Dict[str, Any]]:
592
+ if self.offline or self.rpc is None:
593
+ return known_chains["HIVE"]
594
+ else:
595
+ return self.get_network()
596
+
597
+ @property
598
+ def hardfork(self) -> str:
599
+ if self.offline or self.rpc is None:
600
+ versions = known_chains["HIVE"]["min_version"]
601
+ else:
602
+ hf_prop = self.get_hardfork_properties()
603
+ if hf_prop and isinstance(hf_prop, dict) and "current_hardfork_version" in hf_prop:
604
+ versions = hf_prop["current_hardfork_version"]
605
+ else:
606
+ versions = known_chains["HIVE"]["min_version"]
607
+ return versions
608
+
609
+ @property
610
+ def is_hive(self) -> bool:
611
+ return True
612
+
613
+ @property
614
+ def hbd_symbol(self) -> str:
615
+ params = self.chain_params
616
+ if params and isinstance(params, dict):
617
+ return params.get("backed_token_symbol", "HBD")
618
+ return "HBD"
619
+
620
+ @property
621
+ def hive_symbol(self) -> str:
622
+ params = self.chain_params
623
+ if params and isinstance(params, dict):
624
+ return params.get("token_symbol", "HIVE")
625
+ return "HIVE"
626
+
627
+ @property
628
+ def vests_symbol(self) -> str:
629
+ """get the current chains symbol for VESTS"""
630
+ return self.vest_token_symbol