mirror of https://github.com/nucypher/nucypher.git
Merge pull request #2441 from cygnusv/floater
Allow arbitrary decimal precision as input to nucypher CLIpull/2452/head
commit
5873dc4fb1
|
@ -0,0 +1 @@
|
|||
Allow arbitrary decimal precision when entering NU amounts to nucypher CLI.
|
|
@ -48,21 +48,24 @@ class NU:
|
|||
An amount of NuCypher tokens that doesn't hurt your eyes.
|
||||
Wraps the eth_utils currency conversion methods.
|
||||
|
||||
The easiest way to use NU, is to pass an int, float, or str, and denomination string:
|
||||
The easiest way to use NU, is to pass an int, Decimal, or str, and denomination string:
|
||||
|
||||
Int: nu = NU(100, 'NU')
|
||||
Int: nu_wei = NU(15000000000000000000000, 'NuNit')
|
||||
Int: nu = NU(15000000000000000000000, 'NuNit')
|
||||
|
||||
Float: nu = NU(15042.445, 'NU')
|
||||
Decimal: nu = NU(Decimal('15042.445'), 'NU')
|
||||
String: nu = NU('10002.302', 'NU')
|
||||
|
||||
...or alternately...
|
||||
|
||||
Float: nu = NU.from_tokens(100.50)
|
||||
Int: nu_wei = NU.from_nu_wei(15000000000000000000000)
|
||||
Decimal: nu = NU.from_tokens(Decimal('100.50'))
|
||||
Int: nu = NU.from_nunits(15000000000000000000000)
|
||||
|
||||
Token quantity is stored internally as an int in the smallest denomination,
|
||||
and all arithmetic operations use this value.
|
||||
|
||||
Using float inputs to this class to represent amounts of NU is supported but not recommended,
|
||||
as floats don't have enough precision to represent some quantities.
|
||||
"""
|
||||
|
||||
__symbol = 'NU'
|
||||
|
@ -74,7 +77,7 @@ class NU:
|
|||
class InvalidDenomination(ValueError):
|
||||
"""Raised when an unknown denomination string is passed into __init__"""
|
||||
|
||||
def __init__(self, value: Union[int, float, str], denomination: str):
|
||||
def __init__(self, value: Union[int, Decimal, str], denomination: str):
|
||||
|
||||
# Lookup Conversion
|
||||
try:
|
||||
|
@ -97,7 +100,7 @@ class NU:
|
|||
return cls(value, denomination='NuNit')
|
||||
|
||||
@classmethod
|
||||
def from_tokens(cls, value: Union[int, float, str]) -> 'NU':
|
||||
def from_tokens(cls, value: Union[int, Decimal, str]) -> 'NU':
|
||||
return cls(value, denomination='NU')
|
||||
|
||||
def to_tokens(self) -> Decimal:
|
||||
|
|
|
@ -107,7 +107,7 @@ from nucypher.cli.utils import setup_emitter
|
|||
from nucypher.config.characters import StakeHolderConfiguration
|
||||
from nucypher.utilities.gas_strategies import construct_fixed_price_gas_strategy
|
||||
|
||||
option_value = click.option('--value', help="Token value of stake", type=click.INT)
|
||||
option_value = click.option('--value', help="Token value of stake", type=DecimalRange(min=0))
|
||||
option_lock_periods = click.option('--lock-periods', help="Duration of stake in periods.", type=click.INT)
|
||||
option_worker_address = click.option('--worker-address', help="Address to bond as an Ursula-Worker", type=EIP55_CHECKSUM_ADDRESS)
|
||||
option_index = click.option('--index', help="The staker-specific stake index to edit", type=click.INT)
|
||||
|
@ -498,7 +498,7 @@ def create(general_config: GroupGeneralConfig,
|
|||
|
||||
# Dynamic click types (Economics)
|
||||
min_locked = economics.minimum_allowed_locked
|
||||
stake_value_range = click.FloatRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
|
||||
stake_value_range = DecimalRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
|
||||
stake_duration_range = click.IntRange(min=economics.minimum_locked_periods, clamp=False)
|
||||
|
||||
#
|
||||
|
@ -625,7 +625,7 @@ def increase(general_config: GroupGeneralConfig,
|
|||
emitter.echo(MAXIMUM_STAKE_REACHED, color='red')
|
||||
raise click.Abort
|
||||
|
||||
stake_value_range = click.FloatRange(min=0, max=upper_limit.to_tokens(), clamp=False)
|
||||
stake_value_range = DecimalRange(min=0, max=upper_limit.to_tokens(), clamp=False)
|
||||
value = click.prompt(PROMPT_STAKE_INCREASE_VALUE.format(upper_limit=upper_limit),
|
||||
type=stake_value_range)
|
||||
value = NU.from_tokens(value)
|
||||
|
@ -872,7 +872,7 @@ def divide(general_config: GroupGeneralConfig,
|
|||
|
||||
# Dynamic click types (Economics)
|
||||
min_locked = economics.minimum_allowed_locked
|
||||
stake_value_range = click.FloatRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
|
||||
stake_value_range = DecimalRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
|
||||
|
||||
if index is not None: # 0 is valid.
|
||||
current_stake = STAKEHOLDER.stakes[index]
|
||||
|
|
|
@ -341,9 +341,9 @@ def test_nothing_to_mint(click_runner, surrogate_stakers, mock_staking_agent, mo
|
|||
mock_staking_agent.get_next_committed_period.return_value = 0
|
||||
|
||||
mint_command = ('mint',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
|
||||
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
||||
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
||||
|
@ -367,9 +367,9 @@ def test_mint_with_warning(click_runner, surrogate_stakers, mock_staking_agent,
|
|||
mock_staking_agent.non_withdrawable_stake.return_value = NU(1, 'NU').to_nunits()
|
||||
|
||||
mint_command = ('mint',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
|
||||
user_input = '\n'.join((INSECURE_DEVELOPMENT_PASSWORD, YES))
|
||||
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
||||
|
@ -394,9 +394,9 @@ def test_mint_without_warning(click_runner, surrogate_stakers, mock_staking_agen
|
|||
mock_staking_agent.non_withdrawable_stake.return_value = 0
|
||||
|
||||
mint_command = ('mint',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0])
|
||||
|
||||
user_input = '\n'.join((INSECURE_DEVELOPMENT_PASSWORD, YES))
|
||||
result = click_runner.invoke(stake, mint_command, input=user_input, catch_exceptions=False)
|
||||
|
@ -428,8 +428,8 @@ def test_prolong_interactive(click_runner,
|
|||
final_period = surrogate_stakes[selected_index][sub_stake_index][1]
|
||||
|
||||
command = ('prolong',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN)
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN)
|
||||
|
||||
user_input = '\n'.join((str(selected_index),
|
||||
str(sub_stake_index),
|
||||
|
@ -470,12 +470,12 @@ def test_prolong_non_interactive(click_runner,
|
|||
final_period = surrogate_stakes[selected_index][sub_stake_index][1]
|
||||
|
||||
command = ('prolong',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0],
|
||||
'--index', sub_stake_index,
|
||||
'--lock-periods', lock_periods,
|
||||
'--force')
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
'--network', TEMPORARY_DOMAIN,
|
||||
'--staking-address', surrogate_stakers[0],
|
||||
'--index', sub_stake_index,
|
||||
'--lock-periods', lock_periods,
|
||||
'--force')
|
||||
|
||||
user_input = INSECURE_DEVELOPMENT_PASSWORD
|
||||
result = click_runner.invoke(stake, command, input=user_input, catch_exceptions=False)
|
||||
|
@ -510,7 +510,7 @@ def test_divide_interactive(click_runner,
|
|||
sub_stake_index = 1
|
||||
lock_periods = 10
|
||||
min_allowed_locked = token_economics.minimum_allowed_locked
|
||||
target_value = min_allowed_locked
|
||||
target_value = min_allowed_locked + 1 # Let's add some spare change to force dealing with decimal NU
|
||||
|
||||
mock_staking_agent.get_worker_from_staker.return_value = NULL_ADDRESS
|
||||
|
||||
|
@ -557,7 +557,7 @@ def test_divide_non_interactive(click_runner,
|
|||
sub_stake_index = 1
|
||||
lock_periods = 10
|
||||
min_allowed_locked = token_economics.minimum_allowed_locked
|
||||
target_value = min_allowed_locked
|
||||
target_value = min_allowed_locked + 1 # Let's add some spare change to force dealing with decimal NU
|
||||
|
||||
mock_staking_agent.get_worker_from_staker.return_value = surrogate_stakers[0]
|
||||
|
||||
|
@ -604,7 +604,7 @@ def test_increase_interactive(click_runner,
|
|||
|
||||
selected_index = 0
|
||||
sub_stake_index = 1
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10 + 12345)
|
||||
|
||||
mock_token_agent.get_balance.return_value = 0
|
||||
|
||||
|
@ -671,7 +671,7 @@ def test_increase_non_interactive(click_runner,
|
|||
mock_refresh_stakes = mocker.spy(Staker, 'refresh_stakes')
|
||||
|
||||
sub_stake_index = 1
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10 + 12345)
|
||||
|
||||
locked_tokens = token_economics.minimum_allowed_locked * 5
|
||||
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
||||
|
@ -722,7 +722,7 @@ def test_increase_lock_interactive(click_runner,
|
|||
|
||||
selected_index = 0
|
||||
sub_stake_index = len(surrogate_stakes) - 1
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10 + 12345)
|
||||
|
||||
mock_staking_agent.calculate_staking_reward.return_value = 0
|
||||
|
||||
|
@ -789,7 +789,7 @@ def test_increase_lock_non_interactive(click_runner,
|
|||
|
||||
selected_index = 0
|
||||
sub_stake_index = len(surrogate_stakes) - 1
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10)
|
||||
additional_value = NU.from_nunits(token_economics.minimum_allowed_locked // 10 + 12345)
|
||||
|
||||
mock_staking_agent.get_locked_tokens.return_value = token_economics.minimum_allowed_locked * 2
|
||||
unlocked_tokens = token_economics.minimum_allowed_locked * 5
|
||||
|
@ -838,7 +838,7 @@ def test_create_interactive(click_runner,
|
|||
|
||||
selected_index = 0
|
||||
lock_periods = 366
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11)
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11 + 12345)
|
||||
|
||||
command = ('create',
|
||||
'--provider', MOCK_PROVIDER_URI,
|
||||
|
@ -943,7 +943,7 @@ def test_create_non_interactive(click_runner,
|
|||
selected_index = 0
|
||||
|
||||
lock_periods = token_economics.minimum_locked_periods
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2)
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2 + 12345)
|
||||
|
||||
locked_tokens = token_economics.minimum_allowed_locked * 5
|
||||
mock_staking_agent.get_locked_tokens.return_value = locked_tokens
|
||||
|
@ -1000,7 +1000,7 @@ def test_create_lock_interactive(click_runner,
|
|||
|
||||
selected_index = 0
|
||||
lock_periods = 366
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2)
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 2 + 12345)
|
||||
|
||||
mock_staking_agent.calculate_staking_reward.return_value = token_economics.minimum_allowed_locked - 1
|
||||
|
||||
|
@ -1077,7 +1077,7 @@ def test_create_lock_non_interactive(click_runner,
|
|||
selected_index = 0
|
||||
|
||||
lock_periods = token_economics.minimum_locked_periods
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11)
|
||||
value = NU.from_nunits(token_economics.minimum_allowed_locked * 11 + 12345)
|
||||
|
||||
mock_staking_agent.get_locked_tokens.return_value = token_economics.minimum_allowed_locked * 5
|
||||
unlocked_tokens = token_economics.maximum_allowed_locked // 2
|
||||
|
|
Loading…
Reference in New Issue