mirror of https://github.com/nucypher/nucypher.git
commit
314ab277b1
|
@ -43,7 +43,8 @@ All staking-related operations done by StakeHolder are performed through the ``n
|
|||
+----------------------+-------------------------------------------------------------------------------+
|
||||
| ``divide`` | Create a new stake from part of an existing one |
|
||||
+----------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
| ``restake`` | Manage automatic reward re-staking |
|
||||
+----------------------+-------------------------------------------------------------------------------+
|
||||
|
||||
**Stake Command Options**
|
||||
|
||||
|
@ -59,6 +60,18 @@ All staking-related operations done by StakeHolder are performed through the ``n
|
|||
| ``--hw-wallet`` | Use a hardware wallet |
|
||||
+-----------------+--------------------------------------------+
|
||||
|
||||
**ReStake Command Options**
|
||||
|
||||
+-------------------------+---------------------------------------------+
|
||||
| Option | Description |
|
||||
+=========================+=============================================+
|
||||
| ``--enable`` | Enable re-staking |
|
||||
+-------------------------+---------------------------------------------+
|
||||
| ``--disable`` | Disable re-staking |
|
||||
+-------------------------+---------------------------------------------+
|
||||
| ``--lock-until`` | Enable re-staking lock until release period |
|
||||
+-------------------------+---------------------------------------------+
|
||||
|
||||
|
||||
Staking Overview
|
||||
-----------------
|
||||
|
@ -72,7 +85,8 @@ Most stakers on the Goerli testnet will complete the following steps:
|
|||
4) Stake tokens (See Below)
|
||||
5) Install another Ethereum node at the Worker instance
|
||||
6) Initialize a Worker node [:ref:`ursula-config-guide`] and bond it to your Staker (``set-worker``)
|
||||
7) Configure and run the Worker, and keep it online [:ref:`ursula-config-guide`]!
|
||||
7) Optionally, enable re-staking
|
||||
8) Configure and run the Worker, and keep it online [:ref:`ursula-config-guide`]!
|
||||
|
||||
Interactive Method
|
||||
------------------
|
||||
|
@ -237,6 +251,35 @@ the address to checksum format in geth console:
|
|||
After this step, you're finished with the Staker, and you can proceed to :ref:`ursula-config-guide`.
|
||||
|
||||
|
||||
Manage automatic reward re-staking
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As your Ursula performs work, you can optionally enable the automatic addition of
|
||||
all rewards to your existing stake to optimize earnings. By default this feature is disabled,
|
||||
to enable it run:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(nucypher)$ nucypher stake restake --enable
|
||||
|
||||
To disable restaking:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(nucypher)$ nucypher stake restake --disable
|
||||
|
||||
|
||||
Additionally, you can enable **restake locking**, an on-chain commitment to continue restaking
|
||||
until a future period (`release_period`). Once enabled, the `StakingEscrow` contract will not
|
||||
allow **restaking** to be disabled until the release period begins, even if you are the stake owner.
|
||||
|
||||
.. code:: bash
|
||||
|
||||
(nucypher)$ nucypher stake restake --lock-until 12345
|
||||
|
||||
No action is needed to release the restaking lock once the release period begins.
|
||||
|
||||
|
||||
Collect rewards earned by the staker
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -512,7 +512,7 @@ class Staker(NucypherTokenActor):
|
|||
|
||||
# Calculate stake duration in periods
|
||||
if expiration:
|
||||
additional_periods = datetime_to_period(datetime=expiration) - current_stake.final_locked_period
|
||||
additional_periods = datetime_to_period(datetime=expiration, seconds_per_period=self.economics.seconds_per_period) - current_stake.final_locked_period
|
||||
if additional_periods <= 0:
|
||||
raise Stake.StakingError(f"New expiration {expiration} must be at least 1 period from the "
|
||||
f"current stake's end period ({current_stake.final_locked_period}).")
|
||||
|
@ -539,7 +539,8 @@ class Staker(NucypherTokenActor):
|
|||
if lock_periods and expiration:
|
||||
raise ValueError("Pass the number of lock periods or an expiration MayaDT; not both.")
|
||||
if expiration:
|
||||
lock_periods = calculate_period_duration(future_time=expiration)
|
||||
lock_periods = calculate_period_duration(future_time=expiration,
|
||||
seconds_per_period=self.economics.seconds_per_period)
|
||||
|
||||
# Value
|
||||
if entire_balance and amount:
|
||||
|
@ -566,6 +567,36 @@ class Staker(NucypherTokenActor):
|
|||
|
||||
return new_stake
|
||||
|
||||
@property
|
||||
def is_restaking(self) -> bool:
|
||||
restaking = self.staking_agent.is_restaking(staker_address=self.checksum_address)
|
||||
return restaking
|
||||
|
||||
@only_me
|
||||
@save_receipt
|
||||
def enable_restaking(self) -> dict:
|
||||
receipt = self.staking_agent.set_restaking(staker_address=self.checksum_address, value=True)
|
||||
return receipt
|
||||
|
||||
@only_me
|
||||
@save_receipt
|
||||
def enable_restaking_lock(self, release_period: int):
|
||||
current_period = self.staking_agent.get_current_period()
|
||||
if release_period < current_period:
|
||||
raise ValueError(f"Terminal restaking period must be in the future. "
|
||||
f"Current period is {current_period}, got '{release_period}'.")
|
||||
receipt = self.staking_agent.lock_restaking(staker_address=self.checksum_address,
|
||||
release_period=release_period)
|
||||
return receipt
|
||||
|
||||
@property
|
||||
def restaking_lock_enabled(self) -> bool:
|
||||
status = self.staking_agent.is_restaking_locked(staker_address=self.checksum_address)
|
||||
return status
|
||||
|
||||
def disable_restaking(self) -> dict:
|
||||
receipt = self.staking_agent.set_restaking(staker_address=self.checksum_address, value=False)
|
||||
return receipt
|
||||
#
|
||||
# Reward and Collection
|
||||
#
|
||||
|
|
|
@ -363,6 +363,42 @@ class StakingEscrowAgent(EthereumContractAgent):
|
|||
sender_address=staker_address)
|
||||
return receipt
|
||||
|
||||
@validate_checksum_address
|
||||
def is_restaking(self, staker_address: str) -> bool:
|
||||
staker_info = self.get_staker_info(staker_address)
|
||||
restake_flag = bool(staker_info[3]) # TODO: #1348 Use constant or enum
|
||||
return restake_flag
|
||||
|
||||
@validate_checksum_address
|
||||
def is_restaking_locked(self, staker_address: str) -> bool:
|
||||
return self.contract.functions.isReStakeLocked(staker_address).call()
|
||||
|
||||
@validate_checksum_address
|
||||
def set_restaking(self, staker_address: str, value: bool) -> dict:
|
||||
"""
|
||||
Enable automatic restaking for a fixed duration of lock periods.
|
||||
If set to True, then all staking rewards will be automatically added to locked stake.
|
||||
"""
|
||||
contract_function = self.contract.functions.setReStake(value)
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=staker_address)
|
||||
# TODO: Handle ReStakeSet event (see #1193)
|
||||
return receipt
|
||||
|
||||
@validate_checksum_address
|
||||
def lock_restaking(self, staker_address: str, release_period: int) -> dict:
|
||||
contract_function = self.contract.functions.lockReStake(release_period)
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=staker_address)
|
||||
# TODO: Handle ReStakeLocked event (see #1193)
|
||||
return receipt
|
||||
|
||||
@validate_checksum_address
|
||||
def get_restake_unlock_period(self, staker_address: str) -> int:
|
||||
staker_info = self.get_staker_info(staker_address)
|
||||
restake_unlock_period = int(staker_info[4]) # TODO: #1348 Use constant or enum
|
||||
return restake_unlock_period
|
||||
|
||||
def staking_parameters(self) -> Tuple:
|
||||
parameter_signatures = (
|
||||
# Period
|
||||
|
|
|
@ -78,7 +78,7 @@ class BaseContractRegistry(ABC):
|
|||
return bool(self.id == other.id)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
r = f"{self.__class__.__name__}"
|
||||
r = f"{self.__class__.__name__}(id={self.id[:6]})"
|
||||
return r
|
||||
|
||||
@property
|
||||
|
|
|
@ -369,3 +369,21 @@ def confirm_deployment(emitter, deployer_interface) -> bool:
|
|||
raise click.Abort()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def confirm_enable_restaking_lock(emitter, staking_address: str, release_period: int) -> bool:
|
||||
restaking_lock_agreement = f"""
|
||||
By enabling the re-staking lock for {staking_address}, you are committing to automatically
|
||||
re-stake all rewards until period a future period. You will not be able to disable re-staking until {release_period}.
|
||||
"""
|
||||
emitter.message(restaking_lock_agreement)
|
||||
click.confirm(f"Confirm enable re-staking lock for staker {staking_address} until {release_period}?", abort=True)
|
||||
return True
|
||||
|
||||
|
||||
def confirm_enable_restaking(emitter, staking_address: str) -> bool:
|
||||
restaking_lock_agreement = f"By enabling the re-staking for {staking_address}, " \
|
||||
f"All staking rewards will be automatically added to your existing stake."
|
||||
emitter.message(restaking_lock_agreement)
|
||||
click.confirm(f"Confirm enable automatic re-staking for staker {staking_address}?", abort=True)
|
||||
return True
|
||||
|
|
|
@ -19,12 +19,19 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
import click
|
||||
from web3 import Web3
|
||||
|
||||
from nucypher.characters.lawful import StakeHolder
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.token import NU
|
||||
from nucypher.blockchain.eth.utils import datetime_at_period
|
||||
from nucypher.characters.lawful import StakeHolder
|
||||
from nucypher.cli import painting, actions
|
||||
from nucypher.cli.actions import confirm_staged_stake, get_client_password, select_stake, select_client_account
|
||||
from nucypher.cli.actions import (
|
||||
confirm_staged_stake,
|
||||
get_client_password,
|
||||
select_stake,
|
||||
select_client_account,
|
||||
confirm_enable_restaking_lock,
|
||||
confirm_enable_restaking
|
||||
)
|
||||
from nucypher.cli.config import nucypher_click_config
|
||||
from nucypher.cli.painting import paint_receipt_summary
|
||||
from nucypher.cli.types import (
|
||||
|
@ -53,6 +60,8 @@ from nucypher.config.characters import StakeHolderConfiguration
|
|||
@click.option('--value', help="Token value of stake", type=click.INT)
|
||||
@click.option('--lock-periods', help="Duration of stake in periods.", type=click.INT)
|
||||
@click.option('--index', help="A specific stake index to resume", type=click.INT)
|
||||
@click.option('--enable/--disable', help="Used to enable and disable re-staking", is_flag=True, default=True)
|
||||
@click.option('--lock-until', help="Period to release re-staking lock", type=click.IntRange(min=0))
|
||||
@nucypher_click_config
|
||||
def stake(click_config,
|
||||
action,
|
||||
|
@ -80,6 +89,8 @@ def stake(click_config,
|
|||
index,
|
||||
policy_reward,
|
||||
staking_reward,
|
||||
enable,
|
||||
lock_until,
|
||||
|
||||
) -> None:
|
||||
"""
|
||||
|
@ -88,15 +99,16 @@ def stake(click_config,
|
|||
\b
|
||||
Actions
|
||||
-------------------------------------------------
|
||||
init-stakeholder Create a new stakeholder configuration
|
||||
list List active stakes for current stakeholder
|
||||
accounts Show ETH and NU balances for stakeholder's accounts
|
||||
sync Synchronize stake data with on-chain information
|
||||
set-worker Bond a worker to a staker
|
||||
detach-worker Detach worker currently bonded to a staker
|
||||
init Create a new stake
|
||||
divide Create a new stake from part of an existing one
|
||||
collect-reward Withdraw staking reward
|
||||
init-stakeholder Create a new stakeholder configuration
|
||||
list List active stakes for current stakeholder
|
||||
accounts Show ETH and NU balances for stakeholder's accounts
|
||||
sync Synchronize stake data with on-chain information
|
||||
set-worker Bond a worker to a staker
|
||||
detach-worker Detach worker currently bonded to a staker
|
||||
init Create a new stake
|
||||
restake Manage re-staking with --enable or --disable
|
||||
divide Create a new stake from part of an existing one
|
||||
collect-reward Withdraw staking reward
|
||||
|
||||
"""
|
||||
|
||||
|
@ -290,10 +302,41 @@ def stake(click_config,
|
|||
transactions=new_stake.transactions)
|
||||
return # Exit
|
||||
|
||||
elif action == "restake":
|
||||
|
||||
# Authenticate
|
||||
if not staking_address:
|
||||
staking_address = select_stake(stakeholder=STAKEHOLDER, emitter=emitter).staker_address
|
||||
password = None
|
||||
if not hw_wallet and not blockchain.client.is_local:
|
||||
password = get_client_password(checksum_address=staking_address)
|
||||
STAKEHOLDER.assimilate(checksum_address=staking_address, password=password)
|
||||
|
||||
# Inner Exclusive Switch
|
||||
if lock_until:
|
||||
if not force:
|
||||
confirm_enable_restaking_lock(emitter, staking_address=staking_address, release_period=lock_until)
|
||||
receipt = STAKEHOLDER.enable_restaking_lock(release_period=lock_until)
|
||||
emitter.echo(f'Successfully enabled re-staking lock for {staking_address} until {lock_until}',
|
||||
color='green', verbosity=1)
|
||||
elif enable:
|
||||
if not force:
|
||||
confirm_enable_restaking(emitter, staking_address=staking_address)
|
||||
receipt = STAKEHOLDER.enable_restaking()
|
||||
emitter.echo(f'Successfully enabled re-staking for {staking_address}', color='green', verbosity=1)
|
||||
else:
|
||||
if not force:
|
||||
click.confirm(f"Confirm disable re-staking for staker {staking_address}?", abort=True)
|
||||
receipt = STAKEHOLDER.disable_restaking()
|
||||
emitter.echo(f'Successfully disabled re-staking for {staking_address}', color='green', verbosity=1)
|
||||
|
||||
paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=blockchain.client.chain_name)
|
||||
return # Exit
|
||||
|
||||
elif action == 'divide':
|
||||
"""Divide an existing stake by specifying the new target value and end period"""
|
||||
|
||||
if staking_address and index is not None:
|
||||
if staking_address and index is not None: # 0 is valid.
|
||||
current_stake = STAKEHOLDER.stakes[index]
|
||||
else:
|
||||
current_stake = select_stake(stakeholder=STAKEHOLDER, emitter=emitter)
|
||||
|
|
|
@ -517,12 +517,21 @@ def paint_stakers(emitter, stakers: List[str], agent) -> None:
|
|||
stake = agent.owned_tokens(staker)
|
||||
last_confirmed_period = agent.get_last_active_period(staker)
|
||||
worker = agent.get_worker_from_staker(staker)
|
||||
is_restaking = agent.is_restaking(staker)
|
||||
|
||||
missing_confirmations = current_period - last_confirmed_period
|
||||
stake_in_nu = round(NU.from_nunits(stake), 2)
|
||||
locked_tokens = round(NU.from_nunits(agent.get_locked_tokens(staker)), 2)
|
||||
|
||||
emitter.echo(f"{tab} {'Stake:':10} {stake_in_nu} (Locked: {locked_tokens})")
|
||||
if is_restaking:
|
||||
if agent.is_restaking_locked(staker):
|
||||
unlock_period = agent.get_restake_unlock_period(staker)
|
||||
emitter.echo(f"{tab} {'Re-staking:':10} Yes (Locked until period: {unlock_period})")
|
||||
else:
|
||||
emitter.echo(f"{tab} {'Re-staking:':10} Yes (Unlocked)")
|
||||
else:
|
||||
emitter.echo(f"{tab} {'Re-staking:':10} No")
|
||||
emitter.echo(f"{tab} {'Activity:':10} ", nl=False)
|
||||
if missing_confirmations == -1:
|
||||
emitter.echo(f"Next period confirmed (#{last_confirmed_period})", color='green')
|
||||
|
|
|
@ -17,8 +17,10 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
|
||||
|
||||
import pytest
|
||||
from eth_tester.exceptions import TransactionFailed
|
||||
|
||||
from nucypher.blockchain.eth.actors import Staker
|
||||
from nucypher.blockchain.eth.agents import ContractAgency, StakingEscrowAgent
|
||||
from nucypher.blockchain.eth.token import NU, Stake
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
from nucypher.utilities.sandbox.blockchain import token_airdrop
|
||||
|
@ -90,6 +92,32 @@ def test_staker_divides_stake(staker, token_economics):
|
|||
assert expected_yet_another_stake == staker.stakes[stake_index + 3], 'Third stake values are invalid'
|
||||
|
||||
|
||||
def test_staker_manages_restaking(testerchain, test_registry, staker):
|
||||
|
||||
# Enable Restaking
|
||||
receipt = staker.enable_restaking()
|
||||
assert receipt['status'] == 1
|
||||
|
||||
# Enable Restaking Lock
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
current_period = staking_agent.get_current_period()
|
||||
terminal_period = current_period + 2
|
||||
|
||||
assert not staker.restaking_lock_enabled
|
||||
receipt = staker.enable_restaking_lock(release_period=terminal_period)
|
||||
assert receipt['status'] == 1
|
||||
assert staker.restaking_lock_enabled
|
||||
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
staker.disable_restaking()
|
||||
|
||||
# Wait until terminal period
|
||||
testerchain.time_travel(periods=2)
|
||||
receipt = staker.disable_restaking()
|
||||
assert receipt['status'] == 1
|
||||
assert not staker.restaking_lock_enabled
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
def test_staker_collects_staking_reward(testerchain,
|
||||
test_registry,
|
||||
|
|
|
@ -224,6 +224,45 @@ def test_divide_stake(agency, token_economics):
|
|||
assert len(stakes) == 3
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
def test_enable_restaking(agency, testerchain, test_registry):
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
staker_account, worker_account, *other = testerchain.unassigned_accounts
|
||||
|
||||
assert not staking_agent.is_restaking(staker_account)
|
||||
receipt = staking_agent.set_restaking(staker_account, value=True)
|
||||
assert receipt['status'] == 1
|
||||
assert staking_agent.is_restaking(staker_account)
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
def test_lock_restaking(agency, testerchain, test_registry):
|
||||
staker_account, worker_account, *other = testerchain.unassigned_accounts
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
current_period = staking_agent.get_current_period()
|
||||
terminal_period = current_period + 2
|
||||
|
||||
assert staking_agent.is_restaking(staker_account)
|
||||
assert not staking_agent.is_restaking_locked(staker_account)
|
||||
receipt = staking_agent.lock_restaking(staker_account, release_period=terminal_period)
|
||||
assert receipt['status'] == 1, "Transaction Rejected"
|
||||
assert staking_agent.is_restaking_locked(staker_account)
|
||||
|
||||
testerchain.time_travel(periods=2) # Wait for re-staking lock to be released.
|
||||
assert not staking_agent.is_restaking_locked(staker_account)
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
def test_disable_restaking(agency, testerchain, test_registry):
|
||||
staker_account, worker_account, *other = testerchain.unassigned_accounts
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
|
||||
assert staking_agent.is_restaking(staker_account)
|
||||
receipt = staking_agent.set_restaking(staker_account, value=False)
|
||||
assert receipt['status'] == 1, "Transaction Rejected"
|
||||
assert not staking_agent.is_restaking(staker_account)
|
||||
|
||||
|
||||
@pytest.mark.slow()
|
||||
def test_collect_staking_reward(agency, testerchain):
|
||||
token_agent, staking_agent, _policy_agent = agency
|
||||
|
|
|
@ -7,7 +7,7 @@ from nucypher.blockchain.eth.agents import (
|
|||
ContractAgency
|
||||
)
|
||||
from nucypher.blockchain.eth.constants import STAKING_ESCROW_CONTRACT_NAME
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
|
||||
from nucypher.blockchain.eth.registry import InMemoryContractRegistry, LocalContractRegistry
|
||||
from nucypher.cli.deploy import deploy
|
||||
from nucypher.utilities.sandbox.constants import TEST_PROVIDER_URI, MOCK_REGISTRY_FILEPATH
|
||||
|
||||
|
@ -32,11 +32,12 @@ def test_nucypher_deploy_inspect_no_deployments(click_runner, testerchain):
|
|||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_nucypher_deploy_inspect_fully_deployed(click_runner, testerchain, test_registry, agency):
|
||||
def test_nucypher_deploy_inspect_fully_deployed(click_runner, testerchain, agency):
|
||||
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=test_registry)
|
||||
adjudicator_agent = ContractAgency.get_agent(AdjudicatorAgent, registry=test_registry)
|
||||
local_registry = LocalContractRegistry(filepath=registry_filepath)
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=local_registry)
|
||||
policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=local_registry)
|
||||
adjudicator_agent = ContractAgency.get_agent(AdjudicatorAgent, registry=local_registry)
|
||||
|
||||
status_command = ('inspect',
|
||||
'--registry-infile', MOCK_REGISTRY_FILEPATH,
|
||||
|
@ -52,11 +53,12 @@ def test_nucypher_deploy_inspect_fully_deployed(click_runner, testerchain, test_
|
|||
assert adjudicator_agent.owner in result.output
|
||||
|
||||
|
||||
def test_transfer_ownership(click_runner, testerchain, test_registry, agency):
|
||||
def test_transfer_ownership(click_runner, testerchain, agency):
|
||||
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=test_registry)
|
||||
adjudicator_agent = ContractAgency.get_agent(AdjudicatorAgent, registry=test_registry)
|
||||
local_registry = LocalContractRegistry(filepath=registry_filepath)
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=local_registry)
|
||||
policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=local_registry)
|
||||
adjudicator_agent = ContractAgency.get_agent(AdjudicatorAgent, registry=local_registry)
|
||||
|
||||
assert staking_agent.owner == testerchain.etherbase_account
|
||||
assert policy_agent.owner == testerchain.etherbase_account
|
||||
|
@ -65,7 +67,7 @@ def test_transfer_ownership(click_runner, testerchain, test_registry, agency):
|
|||
maclane = testerchain.unassigned_accounts[0]
|
||||
|
||||
ownership_command = ('transfer-ownership',
|
||||
'--registry-infile', MOCK_REGISTRY_FILEPATH,
|
||||
'--registry-infile', registry_filepath,
|
||||
'--provider', TEST_PROVIDER_URI,
|
||||
'--target-address', maclane,
|
||||
'--poa')
|
||||
|
|
|
@ -275,6 +275,76 @@ def test_ursula_run(click_runner,
|
|||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_stake_restake(click_runner,
|
||||
manual_staker,
|
||||
custom_filepath,
|
||||
testerchain,
|
||||
test_registry,
|
||||
stakeholder_configuration_file_location):
|
||||
|
||||
staker = Staker(is_me=True, checksum_address=manual_staker, registry=test_registry)
|
||||
assert not staker.is_restaking
|
||||
|
||||
restake_args = ('stake', 'restake',
|
||||
'--enable',
|
||||
'--config-file', stakeholder_configuration_file_location,
|
||||
'--staking-address', manual_staker,
|
||||
'--force',
|
||||
'--debug')
|
||||
|
||||
result = click_runner.invoke(nucypher_cli,
|
||||
restake_args,
|
||||
input=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
catch_exceptions=False)
|
||||
assert result.exit_code == 0
|
||||
assert staker.is_restaking
|
||||
assert "Successfully enabled" in result.output
|
||||
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
current_period = staking_agent.get_current_period()
|
||||
release_period = current_period + 1
|
||||
lock_args = ('stake', 'restake',
|
||||
'--lock-until', release_period,
|
||||
'--config-file', stakeholder_configuration_file_location,
|
||||
'--staking-address', manual_staker,
|
||||
'--force',
|
||||
'--debug')
|
||||
|
||||
result = click_runner.invoke(nucypher_cli,
|
||||
lock_args,
|
||||
input=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
catch_exceptions=False)
|
||||
assert result.exit_code == 0
|
||||
|
||||
# Still staking and the lock is enabled
|
||||
assert staker.is_restaking
|
||||
assert staker.restaking_lock_enabled
|
||||
|
||||
# CLI Output includes success message
|
||||
assert "Successfully enabled" in result.output
|
||||
assert str(release_period) in result.output
|
||||
|
||||
# Wait until release period
|
||||
testerchain.time_travel(periods=1)
|
||||
assert not staker.restaking_lock_enabled
|
||||
assert staker.is_restaking
|
||||
|
||||
disable_args = ('stake', 'restake',
|
||||
'--disable',
|
||||
'--config-file', stakeholder_configuration_file_location,
|
||||
'--staking-address', manual_staker,
|
||||
'--force',
|
||||
'--debug')
|
||||
|
||||
result = click_runner.invoke(nucypher_cli,
|
||||
disable_args,
|
||||
input=INSECURE_DEVELOPMENT_PASSWORD,
|
||||
catch_exceptions=False)
|
||||
assert result.exit_code == 0
|
||||
assert not staker.is_restaking
|
||||
assert "Successfully disabled" in result.output
|
||||
|
||||
|
||||
def test_collect_rewards_integration(click_runner,
|
||||
testerchain,
|
||||
test_registry,
|
||||
|
@ -397,8 +467,8 @@ def test_collect_rewards_integration(click_runner,
|
|||
current_period += 1
|
||||
logger.debug(f">>>>>>>>>>> TEST PERIOD {current_period} <<<<<<<<<<<<<<<<")
|
||||
|
||||
# Half of the tokens are unlocked.
|
||||
assert staker.locked_tokens() == token_economics.minimum_allowed_locked
|
||||
# At least half of the tokens are unlocked (restaking was enabled for some prior periods)
|
||||
assert staker.locked_tokens() >= token_economics.minimum_allowed_locked
|
||||
|
||||
# Since we are mocking the blockchain connection, manually consume the transacting power of the Staker.
|
||||
testerchain.transacting_power = TransactingPower(account=staker_address,
|
||||
|
|
Loading…
Reference in New Issue