mirror of https://github.com/nucypher/nucypher.git
Merge pull request #2191 from vzotova/worklock-restake
WorkLock: `restake` lock while claiming tokenspull/2197/head
commit
6f911ac0a0
|
@ -407,6 +407,7 @@ class ContractAdministrator(NucypherTokenActor):
|
|||
|
||||
def batch_deposits(self,
|
||||
allocation_data_filepath: str,
|
||||
release_period: int,
|
||||
interactive: bool = True,
|
||||
emitter: StdoutEmitter = None,
|
||||
gas_limit: int = None
|
||||
|
@ -431,7 +432,7 @@ class ContractAdministrator(NucypherTokenActor):
|
|||
if interactive and not emitter:
|
||||
raise ValueError("'emitter' is a required keyword argument when interactive is True.")
|
||||
|
||||
allocator = Allocator(allocation_data_filepath, self.registry, self.deployer_address)
|
||||
allocator = Allocator(allocation_data_filepath, self.registry, self.deployer_address, release_period)
|
||||
|
||||
# TODO: Check validity of input address (check TX)
|
||||
|
||||
|
@ -532,7 +533,7 @@ class Allocator:
|
|||
|
||||
OCCUPATION_RATIO = 0.9 # When there's no explicit gas limit, we'll try to use the block limit up to this ratio
|
||||
|
||||
def __init__(self, filepath: str, registry, deployer_address):
|
||||
def __init__(self, filepath: str, registry, deployer_address, release_period):
|
||||
|
||||
self.log = Logger("allocator")
|
||||
self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent,
|
||||
|
@ -541,6 +542,7 @@ class Allocator:
|
|||
self.allocations = dict()
|
||||
self.deposited = set()
|
||||
self.economics = EconomicsFactory.get_economics(registry)
|
||||
self.release_period = release_period
|
||||
|
||||
self.__total_to_allocate = 0
|
||||
self.__process_allocation_data(str(filepath))
|
||||
|
@ -631,6 +633,7 @@ class Allocator:
|
|||
batch_parameters = self.staking_agent.construct_batch_deposit_parameters(deposits=test_batch)
|
||||
try:
|
||||
estimated_gas = self.staking_agent.batch_deposit(*batch_parameters,
|
||||
release_period=self.release_period,
|
||||
sender_address=sender_address,
|
||||
dry_run=True,
|
||||
gas_limit=gas_limit)
|
||||
|
@ -649,6 +652,7 @@ class Allocator:
|
|||
|
||||
batch_parameters = self.staking_agent.construct_batch_deposit_parameters(deposits=last_good_batch)
|
||||
receipt = self.staking_agent.batch_deposit(*batch_parameters,
|
||||
release_period=self.release_period,
|
||||
sender_address=sender_address,
|
||||
gas_limit=gas_limit)
|
||||
|
||||
|
|
|
@ -480,6 +480,7 @@ class StakingEscrowAgent(EthereumContractAgent):
|
|||
number_of_substakes: List[int],
|
||||
amounts: List[NuNits],
|
||||
lock_periods: List[PeriodDelta],
|
||||
release_period: Period,
|
||||
sender_address: ChecksumAddress,
|
||||
dry_run: bool = False,
|
||||
gas_limit: Optional[Wei] = None
|
||||
|
@ -489,7 +490,8 @@ class StakingEscrowAgent(EthereumContractAgent):
|
|||
if gas_limit and gas_limit < min_gas_batch_deposit:
|
||||
raise ValueError(f"{gas_limit} is not enough gas for any batch deposit")
|
||||
|
||||
contract_function: ContractFunction = self.contract.functions.batchDeposit(stakers, number_of_substakes, amounts, lock_periods)
|
||||
contract_function: ContractFunction = self.contract.functions.batchDeposit(
|
||||
stakers, number_of_substakes, amounts, lock_periods, release_period)
|
||||
if dry_run:
|
||||
payload: TxParams = {'from': sender_address}
|
||||
if gas_limit:
|
||||
|
|
|
@ -41,7 +41,7 @@ interface WorkLockInterface {
|
|||
/**
|
||||
* @notice Contract holds and locks stakers tokens.
|
||||
* Each staker that locks their tokens will receive some compensation
|
||||
* @dev |v5.2.2|
|
||||
* @dev |v5.3.1|
|
||||
*/
|
||||
contract StakingEscrow is Issuer, IERC900History {
|
||||
|
||||
|
@ -529,6 +529,52 @@ contract StakingEscrow is Issuer, IERC900History {
|
|||
emit ReStakeLocked(msg.sender, _lockReStakeUntilPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Enable `reStake` and lock this parameter even if parameter is locked
|
||||
* @param _staker Staker address
|
||||
* @param _info Staker structure
|
||||
* @param _lockReStakeUntilPeriod Can't change `reStake` value until this period
|
||||
*/
|
||||
function forceLockReStake(
|
||||
address _staker,
|
||||
StakerInfo storage _info,
|
||||
uint16 _lockReStakeUntilPeriod
|
||||
)
|
||||
internal
|
||||
{
|
||||
// reset bit when `reStake` is already disabled
|
||||
if (_info.flags.bitSet(RE_STAKE_DISABLED_INDEX) == true) {
|
||||
_info.flags = _info.flags.toggleBit(RE_STAKE_DISABLED_INDEX);
|
||||
emit ReStakeSet(_staker, true);
|
||||
}
|
||||
// lock `reStake` parameter if it's not locked or locked for too short duration
|
||||
if (_lockReStakeUntilPeriod > _info.lockReStakeUntilPeriod) {
|
||||
_info.lockReStakeUntilPeriod = _lockReStakeUntilPeriod;
|
||||
emit ReStakeLocked(_staker, _lockReStakeUntilPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Deposit tokens and lock `reStake` parameter from WorkLock contract
|
||||
* @param _staker Staker address
|
||||
* @param _value Amount of tokens to deposit
|
||||
* @param _periods Amount of periods during which tokens will be locked
|
||||
* and number of period after which `reStake` can be changed
|
||||
*/
|
||||
function depositFromWorkLock(
|
||||
address _staker,
|
||||
uint256 _value,
|
||||
uint16 _periods
|
||||
)
|
||||
external
|
||||
{
|
||||
require(msg.sender == address(workLock));
|
||||
deposit(_staker, msg.sender, MAX_SUB_STAKES, _value, _periods);
|
||||
StakerInfo storage info = stakerInfo[_staker];
|
||||
uint16 lockReStakeUntilPeriod = getCurrentPeriod().add16(_periods).add16(1);
|
||||
forceLockReStake(_staker, info, lockReStakeUntilPeriod);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Set `windDown` parameter.
|
||||
* If true then stake's duration will be decreasing in each period with `commitToNextPeriod()`
|
||||
|
@ -615,14 +661,18 @@ contract StakingEscrow is Issuer, IERC900History {
|
|||
* @param _numberOfSubStakes Number of sub-stakes which belong to staker in _values and _periods arrays
|
||||
* @param _values Amount of tokens to deposit for each staker
|
||||
* @param _periods Amount of periods during which tokens will be locked for each staker
|
||||
* @param _lockReStakeUntilPeriod Can't change `reStake` value until this period. Zero value will disable locking
|
||||
*/
|
||||
function batchDeposit(
|
||||
address[] calldata _stakers,
|
||||
uint256[] calldata _numberOfSubStakes,
|
||||
uint256[] calldata _values,
|
||||
uint16[] calldata _periods
|
||||
uint16[] calldata _periods,
|
||||
uint16 _lockReStakeUntilPeriod
|
||||
)
|
||||
external
|
||||
// `onlyOwner` modifier is for prevent malicious using of `forceLockReStake`
|
||||
// remove `onlyOwner` if `forceLockReStake` will be removed
|
||||
external onlyOwner
|
||||
{
|
||||
uint256 subStakesLength = _values.length;
|
||||
require(_stakers.length != 0 &&
|
||||
|
@ -658,6 +708,10 @@ contract StakingEscrow is Issuer, IERC900History {
|
|||
}
|
||||
require(info.value <= maxAllowableLockedTokens);
|
||||
info.history.addSnapshot(info.value);
|
||||
|
||||
if (_lockReStakeUntilPeriod >= nextPeriod) {
|
||||
forceLockReStake(staker, info, _lockReStakeUntilPeriod);
|
||||
}
|
||||
}
|
||||
require(j == subStakesLength);
|
||||
uint256 lastGlobalBalance = uint256(balanceHistory.lastValue());
|
||||
|
|
|
@ -503,7 +503,7 @@ contract WorkLock is Ownable {
|
|||
|
||||
info.claimed = true;
|
||||
token.approve(address(escrow), claimedTokens);
|
||||
escrow.deposit(msg.sender, claimedTokens, stakingPeriods);
|
||||
escrow.depositFromWorkLock(msg.sender, claimedTokens, stakingPeriods);
|
||||
info.completedWork = escrow.setWorkMeasurement(msg.sender, true);
|
||||
emit Claimed(msg.sender, claimedTokens);
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ from nucypher.cli.literature import (
|
|||
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS,
|
||||
SUCCESSFUL_SAVE_MULTISIG_TX_PROPOSAL,
|
||||
SUCCESSFUL_UPGRADE,
|
||||
UNKNOWN_CONTRACT_NAME
|
||||
UNKNOWN_CONTRACT_NAME, PROMPT_FOR_RELEASE_PERIOD
|
||||
)
|
||||
from nucypher.cli.options import (
|
||||
group_options,
|
||||
|
@ -509,14 +509,18 @@ def contracts(general_config, actor_options, mode, activate, gas, ignore_deploye
|
|||
@group_general_config
|
||||
@group_actor_options
|
||||
@click.option('--allocation-infile', help="Input path for token allocation JSON file", type=EXISTING_READABLE_FILE)
|
||||
@click.option('--release-period', help="Period number when re-stake lock will be released", type=click.INT)
|
||||
@option_gas
|
||||
def allocations(general_config, actor_options, allocation_infile, gas):
|
||||
def allocations(general_config, actor_options, allocation_infile, gas, release_period):
|
||||
"""Deposit stake allocations in batches"""
|
||||
emitter = general_config.emitter
|
||||
ADMINISTRATOR, _, deployer_interface, local_registry = actor_options.create_actor(emitter)
|
||||
if not allocation_infile:
|
||||
allocation_infile = click.prompt(PROMPT_FOR_ALLOCATION_DATA_FILEPATH)
|
||||
if release_period is None:
|
||||
release_period = click.prompt(PROMPT_FOR_RELEASE_PERIOD)
|
||||
receipts = ADMINISTRATOR.batch_deposits(allocation_data_filepath=allocation_infile,
|
||||
release_period=release_period,
|
||||
emitter=emitter,
|
||||
gas_limit=gas,
|
||||
interactive=not actor_options.force)
|
||||
|
|
|
@ -395,6 +395,8 @@ DISPLAY_SENDER_TOKEN_BALANCE_BEFORE_TRANSFER = "Deployer NU balance: {token_bala
|
|||
|
||||
PROMPT_FOR_ALLOCATION_DATA_FILEPATH = "Enter allocations data filepath"
|
||||
|
||||
PROMPT_FOR_RELEASE_PERIOD = "Enter period number when re-stake lock should be released"
|
||||
|
||||
SUCCESSFUL_SAVE_BATCH_DEPOSIT_RECEIPTS = "Saved batch deposits receipts to {receipts_filepath}"
|
||||
|
||||
SUCCESSFUL_SAVE_DEPLOY_RECEIPTS = "Saved deployment receipts to {receipts_filepath}"
|
||||
|
|
|
@ -21,6 +21,7 @@ import random
|
|||
import pytest
|
||||
|
||||
from nucypher.blockchain.eth.actors import ContractAdministrator
|
||||
from nucypher.blockchain.eth.agents import StakingEscrowAgent, ContractAgency
|
||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||
from nucypher.characters.control.emitters import StdoutEmitter
|
||||
from nucypher.crypto.powers import TransactingPower
|
||||
|
@ -76,7 +77,9 @@ def test_rapid_deployment(token_economics, test_registry, tmpdir, get_random_che
|
|||
with open(filepath, 'w') as f:
|
||||
json.dump(allocation_data, f)
|
||||
|
||||
administrator.batch_deposits(allocation_data_filepath=str(filepath), interactive=False)
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=test_registry)
|
||||
current_period = staking_agent.get_current_period()
|
||||
administrator.batch_deposits(allocation_data_filepath=str(filepath), interactive=False, release_period=current_period+10)
|
||||
|
||||
minimum, default, maximum = 10, 20, 30
|
||||
administrator.set_fee_rate_range(minimum, default, maximum)
|
||||
|
|
|
@ -447,6 +447,7 @@ def test_batch_deposit(testerchain,
|
|||
|
||||
amount = token_economics.minimum_allowed_locked
|
||||
lock_periods = token_economics.minimum_locked_periods
|
||||
current_period = staking_agent.get_current_period()
|
||||
|
||||
stakers = [get_random_checksum_address() for _ in range(4)]
|
||||
|
||||
|
@ -472,13 +473,19 @@ def test_batch_deposit(testerchain,
|
|||
not_enough_gas = 800_000
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
staking_agent.batch_deposit(*batch_parameters,
|
||||
release_period=current_period + 2,
|
||||
sender_address=testerchain.etherbase_account,
|
||||
dry_run=True,
|
||||
gas_limit=not_enough_gas)
|
||||
|
||||
staking_agent.batch_deposit(*batch_parameters, sender_address=testerchain.etherbase_account, dry_run=True)
|
||||
staking_agent.batch_deposit(*batch_parameters,
|
||||
release_period=current_period + 2,
|
||||
sender_address=testerchain.etherbase_account,
|
||||
dry_run=True)
|
||||
|
||||
staking_agent.batch_deposit(*batch_parameters, sender_address=testerchain.etherbase_account)
|
||||
staking_agent.batch_deposit(*batch_parameters,
|
||||
release_period=current_period + 2,
|
||||
sender_address=testerchain.etherbase_account)
|
||||
|
||||
for staker in stakers:
|
||||
assert staking_agent.owned_tokens(staker_address=staker) == amount * N
|
||||
|
@ -488,3 +495,4 @@ def test_batch_deposit(testerchain,
|
|||
first_period, last_period, locked_value = substake
|
||||
assert last_period == first_period + lock_periods - 1
|
||||
assert locked_value == amount
|
||||
assert staking_agent.is_restaking_locked(staker_address=staker)
|
||||
|
|
|
@ -214,6 +214,8 @@ def test_batch_deposits(click_runner,
|
|||
agency_local_registry,
|
||||
mock_allocation_infile,
|
||||
token_economics):
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=agency_local_registry)
|
||||
current_period = staking_agent.get_current_period()
|
||||
|
||||
#
|
||||
# Main
|
||||
|
@ -222,6 +224,7 @@ def test_batch_deposits(click_runner,
|
|||
deploy_command = ('allocations',
|
||||
'--registry-infile', agency_local_registry.filepath,
|
||||
'--allocation-infile', mock_allocation_infile,
|
||||
'--release-period', current_period + 10,
|
||||
'--provider', TEST_PROVIDER_URI)
|
||||
|
||||
account_index = '0\n'
|
||||
|
|
|
@ -257,13 +257,20 @@ contract Intermediary {
|
|||
*/
|
||||
contract WorkLockForStakingEscrowMock {
|
||||
|
||||
NuCypherToken public immutable token;
|
||||
StakingEscrow public immutable escrow;
|
||||
|
||||
constructor(StakingEscrow _escrow) {
|
||||
constructor(NuCypherToken _token, StakingEscrow _escrow) {
|
||||
token = _token;
|
||||
escrow = _escrow;
|
||||
}
|
||||
|
||||
function setWorkMeasurement(address _staker, bool _measureWork) external returns (uint256) {
|
||||
return escrow.setWorkMeasurement(_staker, _measureWork);
|
||||
}
|
||||
|
||||
function depositFromWorkLock(address _staker, uint256 _value, uint16 _periods) external {
|
||||
token.approve(address(escrow), _value);
|
||||
escrow.depositFromWorkLock(_staker, _value, _periods);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ contract StakingEscrowForWorkLockMock {
|
|||
return stakerInfo[_staker].completedWork;
|
||||
}
|
||||
|
||||
function deposit(address _staker, uint256 _value, uint16 _periods) external {
|
||||
function depositFromWorkLock(address _staker, uint256 _value, uint16 _periods) external {
|
||||
stakerInfo[_staker].value = _value;
|
||||
stakerInfo[_staker].periods = _periods;
|
||||
token.transferFrom(msg.sender, address(this), _value);
|
||||
|
|
|
@ -295,10 +295,10 @@ def preallocation_escrow_2(testerchain, token, staking_interface, staking_interf
|
|||
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('multisig')
|
||||
def test_batch_deposit(testerchain, token_economics, token, escrow):
|
||||
creator = testerchain.w3.eth.accounts[0]
|
||||
staker1, staker2, staker3, staker4 = testerchain.client.accounts[1:5]
|
||||
def test_batch_deposit(testerchain, token_economics, token, escrow, multisig):
|
||||
creator, staker1, staker2, staker3, staker4, _alice1, _alice2, *contracts_owners =\
|
||||
testerchain.client.accounts
|
||||
contracts_owners = sorted(contracts_owners)
|
||||
|
||||
# Travel to the start of the next period to prevent problems with unexpected overflow first period
|
||||
testerchain.time_travel(hours=1)
|
||||
|
@ -317,10 +317,25 @@ def test_batch_deposit(testerchain, token_economics, token, escrow):
|
|||
pytest.staker1_tokens = token_economics.minimum_allowed_locked
|
||||
staker1_tokens = pytest.staker1_tokens
|
||||
duration = token_economics.minimum_locked_periods
|
||||
tx = token.functions.approve(escrow.address, 2 * staker1_tokens).transact({'from': creator})
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
|
||||
# Only owner can use batch deposit
|
||||
tx = token.functions.approve(escrow.address, staker1_tokens).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.batchDeposit([staker1], [1], [staker1_tokens], [duration]).transact({'from': creator})
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit(
|
||||
[staker1], [1], [staker1_tokens], [duration], current_period + 1).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
tx = token.functions.transfer(multisig.address, staker1_tokens).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = token.functions.approve(escrow.address, staker1_tokens) \
|
||||
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
|
||||
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
|
||||
tx = escrow.functions.batchDeposit([staker1], [1], [staker1_tokens], [duration], current_period + 1)\
|
||||
.buildTransaction({'from': multisig.address, 'gasPrice': 0})
|
||||
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
|
||||
|
||||
pytest.escrow_supply = token_economics.minimum_allowed_locked
|
||||
assert token.functions.balanceOf(escrow.address).call() == pytest.escrow_supply
|
||||
assert escrow.functions.getAllTokens(staker1).call() == staker1_tokens
|
||||
|
@ -329,10 +344,13 @@ def test_batch_deposit(testerchain, token_economics, token, escrow):
|
|||
assert escrow.functions.getLockedTokens(staker1, duration).call() == staker1_tokens
|
||||
assert escrow.functions.getLockedTokens(staker1, duration + 1).call() == 0
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker1).call()
|
||||
|
||||
# Can't deposit tokens again for the same staker
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker1], [1], [staker1_tokens], [duration]).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
|
||||
|
||||
tx = token.functions.approve(escrow.address, 0).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
@ -482,6 +500,10 @@ def test_worklock_phases(testerchain,
|
|||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(staker2).call()[2]
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker2).call()
|
||||
|
||||
assert token.functions.balanceOf(staker2).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker2, 0).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker2, 1).call() == staker2_tokens
|
||||
|
@ -502,6 +524,7 @@ def test_worklock_phases(testerchain,
|
|||
wind_down, _re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
|
||||
assert wind_down
|
||||
|
||||
assert not escrow.functions.isReStakeLocked(staker1).call()
|
||||
tx = worklock.functions.claim().transact({'from': staker1, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert worklock.functions.workInfo(staker1).call()[2]
|
||||
|
@ -510,6 +533,10 @@ def test_worklock_phases(testerchain,
|
|||
assert escrow.functions.getLockedTokens(staker1, 1).call() == pytest.staker1_tokens
|
||||
pytest.escrow_supply += staker1_claims
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker1).call()
|
||||
|
||||
# Staker prolongs lock duration
|
||||
tx = escrow.functions.prolongStake(0, 3).transact({'from': staker2, 'gas_price': 0})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
@ -631,8 +658,6 @@ def test_staking(testerchain,
|
|||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.bondWorker(staker1).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.setReStake(False).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.setWindDown(True).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
wind_down, _re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
|
@ -735,14 +760,6 @@ def test_policy(testerchain,
|
|||
tx = escrow.functions.commitToNextPeriod().transact({'from': staker3})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Turn on re-stake for staker1
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert not re_stake
|
||||
tx = escrow.functions.setReStake(True).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
|
||||
testerchain.time_travel(hours=1)
|
||||
tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
@ -856,14 +873,6 @@ def test_policy(testerchain,
|
|||
tx = escrow.functions.commitToNextPeriod().transact({'from': staker3})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Turn off re-stake for staker1
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
tx = escrow.functions.setReStake(False).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert not re_stake
|
||||
|
||||
testerchain.time_travel(hours=1)
|
||||
tx = escrow.functions.commitToNextPeriod().transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
@ -1192,54 +1201,6 @@ def test_upgrading_adjudicator(testerchain,
|
|||
assert adjudicator_v1 == adjudicator.functions.target().call()
|
||||
|
||||
|
||||
def test_another_slashing(testerchain,
|
||||
token_economics,
|
||||
token,
|
||||
escrow,
|
||||
adjudicator,
|
||||
preallocation_escrow_1,
|
||||
mock_ursula_reencrypts,
|
||||
mocker):
|
||||
creator, staker1, staker2, staker3, staker4, alice1, alice2, *contracts_owners =\
|
||||
testerchain.client.accounts
|
||||
ursula1_with_stamp = mock_ursula(testerchain, staker1, mocker=mocker)
|
||||
|
||||
# Slash two sub stakes
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
algorithm_sha256, base_penalty, *coefficients = token_economics.slashing_deployment_parameters
|
||||
penalty_history_coefficient, percentage_penalty_coefficient, reward_coefficient = coefficients
|
||||
total_previous_lock = escrow.functions.lockedPerPeriod(current_period - 1).call()
|
||||
|
||||
tokens_amount = escrow.functions.getAllTokens(staker1).call()
|
||||
unlocked_amount = tokens_amount - escrow.functions.getLockedTokens(staker1, 0).call()
|
||||
tx = escrow.functions.withdraw(unlocked_amount).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
previous_lock = escrow.functions.getLockedTokensInPast(staker1, 1).call()
|
||||
lock = escrow.functions.getLockedTokens(staker1, 0).call()
|
||||
next_lock = escrow.functions.getLockedTokens(staker1, 1).call()
|
||||
total_lock = escrow.functions.lockedPerPeriod(current_period).call()
|
||||
alice2_balance = token.functions.balanceOf(alice2).call()
|
||||
data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula1_with_stamp)
|
||||
assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
|
||||
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice2})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
|
||||
data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula1_with_stamp)
|
||||
assert not adjudicator.functions.evaluatedCFrags(data_hash).call()
|
||||
tx = adjudicator.functions.evaluateCFrag(*slashing_args).transact({'from': alice2})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert adjudicator.functions.evaluatedCFrags(data_hash).call()
|
||||
penalty = (2 * base_penalty + 3 * penalty_history_coefficient)
|
||||
assert lock - penalty == escrow.functions.getAllTokens(staker1).call()
|
||||
assert previous_lock == escrow.functions.getLockedTokensInPast(staker1, 1).call()
|
||||
assert lock - penalty == escrow.functions.getLockedTokens(staker1, 0).call()
|
||||
assert next_lock - (penalty - (lock - next_lock)) == escrow.functions.getLockedTokens(staker1, 1).call()
|
||||
assert total_previous_lock == escrow.functions.lockedPerPeriod(current_period - 1).call()
|
||||
assert total_lock - penalty == escrow.functions.lockedPerPeriod(current_period).call()
|
||||
assert 0 == escrow.functions.lockedPerPeriod(current_period + 1).call()
|
||||
assert alice2_balance + penalty / reward_coefficient == token.functions.balanceOf(alice2).call()
|
||||
|
||||
|
||||
def test_withdraw(testerchain,
|
||||
token_economics,
|
||||
token,
|
||||
|
|
|
@ -23,6 +23,7 @@ from web3.contract import Contract
|
|||
|
||||
MAX_SUB_STAKES = 30
|
||||
MAX_UINT16 = 65535
|
||||
LOCK_RE_STAKE_UNTIL_PERIOD_FIELD = 4
|
||||
|
||||
|
||||
def test_staking(testerchain, token, escrow_contract):
|
||||
|
@ -1266,14 +1267,28 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
creator = testerchain.client.accounts[0]
|
||||
deposit_log = escrow.events.Deposited.createFilter(fromBlock='latest')
|
||||
lock_log = escrow.events.Locked.createFilter(fromBlock='latest')
|
||||
re_stake_log = escrow.events.ReStakeSet.createFilter(fromBlock='latest')
|
||||
re_stake_lock_log = escrow.events.ReStakeLocked.createFilter(fromBlock='latest')
|
||||
|
||||
# Grant access to transfer tokens
|
||||
tx = token.functions.approve(escrow.address, 10000).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Deposit tokens for 1 staker
|
||||
# Can deposit tokens only from owner
|
||||
staker = testerchain.client.accounts[1]
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1000], [10]).transact({'from': creator})
|
||||
tx = token.functions.transfer(staker, 1000).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = token.functions.approve(escrow.address, 1000).transact({'from': staker})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1000], [10], 0)\
|
||||
.transact({'from': staker})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Deposit tokens for 1 staker
|
||||
tx = escrow.functions.setReStake(False).transact({'from': staker})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1000], [10], 0).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
escrow_balance = 1000
|
||||
assert token.functions.balanceOf(escrow.address).call() == escrow_balance
|
||||
|
@ -1288,6 +1303,12 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert escrow.functions.getPastDowntimeLength(staker).call() == 0
|
||||
assert escrow.functions.getLastCommittedPeriod(staker).call() == 0
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker).call()
|
||||
assert not re_stake
|
||||
assert not escrow.functions.isReStakeLocked(staker).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == 0
|
||||
|
||||
deposit_events = deposit_log.get_all_entries()
|
||||
assert len(deposit_events) == 1
|
||||
event_args = deposit_events[-1]['args']
|
||||
|
@ -1303,55 +1324,60 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == 10
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 0
|
||||
|
||||
# Can't deposit tokens again for the same staker twice
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1000], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1000], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Can't deposit tokens with too low or too high value
|
||||
staker = testerchain.client.accounts[2]
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1501], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [1501], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [500], [1])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1], [500], [1], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [2], [1000, 501], [10, 10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [2], [1000, 501], [10, 10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Inconsistent input
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [0], [500], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [0], [500], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [2], [500], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [2], [500], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500, 500], [10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500, 500], [10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500, 500], [10, 10])\
|
||||
tx = escrow.functions.batchDeposit([staker], [1, 1], [500, 500], [10, 10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
stakers = testerchain.client.accounts[2:4]
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.batchDeposit(stakers, [1, 1], [500, 500, 500], [10, 10, 10])\
|
||||
tx = escrow.functions.batchDeposit(stakers, [1, 1], [500, 500, 500], [10, 10, 10], 0)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
@ -1361,18 +1387,23 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
|
||||
# Deposit tokens for multiple stakers
|
||||
stakers = testerchain.client.accounts[2:7]
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
tx = escrow.functions.batchDeposit(
|
||||
stakers, [1, 1, 1, 1, 1], [100, 200, 300, 400, 500], [50, 100, 150, 200, 250]).transact({'from': creator})
|
||||
stakers, [1, 1, 1, 1, 1], [100, 200, 300, 400, 500], [50, 100, 150, 200, 250], current_period + 2
|
||||
).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
escrow_balance += 1500
|
||||
assert token.functions.balanceOf(escrow.address).call() == escrow_balance
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
deposit_events = deposit_log.get_all_entries()
|
||||
lock_events = lock_log.get_all_entries()
|
||||
re_stake_lock_events = re_stake_lock_log.get_all_entries()
|
||||
re_stake_events = re_stake_log.get_all_entries()
|
||||
|
||||
assert len(deposit_events) == 6
|
||||
assert len(lock_events) == 6
|
||||
assert len(re_stake_lock_events) == 5
|
||||
assert len(re_stake_events) == 1
|
||||
|
||||
for index, staker in enumerate(stakers):
|
||||
value = 100 * (index + 1)
|
||||
|
@ -1387,6 +1418,12 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert escrow.functions.getPastDowntimeLength(staker).call() == 0
|
||||
assert escrow.functions.getLastCommittedPeriod(staker).call() == 0
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + 2
|
||||
|
||||
event_args = deposit_events[index + 1]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['value'] == value
|
||||
|
@ -1398,21 +1435,36 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
event_args = re_stake_lock_events[index]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['lockUntilPeriod'] == current_period + 2
|
||||
|
||||
# Deposit tokens for multiple stakers with multiple sub-stakes
|
||||
stakers = testerchain.client.accounts[7:10]
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
tx = escrow.functions.setReStake(False).transact({'from': stakers[0]})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.setReStake(False).transact({'from': stakers[1]})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.lockReStake(current_period + 4).transact({'from': stakers[1]})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
tx = escrow.functions.batchDeposit(
|
||||
stakers, [1, 2, 3], [100, 200, 300, 400, 500, 600], [50, 100, 150, 200, 250, 300])\
|
||||
stakers, [1, 2, 3], [100, 200, 300, 400, 500, 600], [50, 100, 150, 200, 250, 300], current_period + 3)\
|
||||
.transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
escrow_balance += 2100
|
||||
assert token.functions.balanceOf(escrow.address).call() == escrow_balance
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
deposit_events = deposit_log.get_all_entries()
|
||||
lock_events = lock_log.get_all_entries()
|
||||
re_stake_lock_events = re_stake_lock_log.get_all_entries()
|
||||
re_stake_events = re_stake_log.get_all_entries()
|
||||
|
||||
assert len(deposit_events) == 12
|
||||
assert len(lock_events) == 12
|
||||
assert len(re_stake_lock_events) == 8
|
||||
assert len(re_stake_events) == 5
|
||||
|
||||
staker = stakers[0]
|
||||
duration = 50
|
||||
|
@ -1427,6 +1479,12 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert escrow.functions.getLastCommittedPeriod(staker).call() == 0
|
||||
assert escrow.functions.getSubStakesLength(staker).call() == 1
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + 3
|
||||
|
||||
event_args = deposit_events[6]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['value'] == value
|
||||
|
@ -1438,6 +1496,14 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
event_args = re_stake_events[3]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['reStake']
|
||||
|
||||
event_args = re_stake_lock_events[6]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['lockUntilPeriod'] == current_period + 3
|
||||
|
||||
staker = stakers[1]
|
||||
duration1 = 100
|
||||
duration2 = 150
|
||||
|
@ -1456,6 +1522,12 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert escrow.functions.getLastCommittedPeriod(staker).call() == 0
|
||||
assert escrow.functions.getSubStakesLength(staker).call() == 2
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + 4
|
||||
|
||||
event_args = deposit_events[7]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['value'] == value1
|
||||
|
@ -1478,6 +1550,10 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration2
|
||||
|
||||
event_args = re_stake_events[4]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['reStake']
|
||||
|
||||
staker = stakers[2]
|
||||
duration1 = 200
|
||||
duration2 = 250
|
||||
|
@ -1500,6 +1576,12 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert escrow.functions.getLastCommittedPeriod(staker).call() == 0
|
||||
assert escrow.functions.getSubStakesLength(staker).call() == 3
|
||||
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + 3
|
||||
|
||||
event_args = deposit_events[9]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['value'] == value1
|
||||
|
@ -1532,3 +1614,231 @@ def test_batch_deposit(testerchain, token, escrow_contract, deploy_contract):
|
|||
assert event_args['value'] == value3
|
||||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration3
|
||||
|
||||
event_args = re_stake_lock_events[7]['args']
|
||||
assert event_args['staker'] == staker
|
||||
assert event_args['lockUntilPeriod'] == current_period + 3
|
||||
|
||||
|
||||
def test_staking_from_worklock(testerchain, token, escrow_contract, token_economics, deploy_contract):
|
||||
"""
|
||||
Tests for staking method: depositFromWorkLock
|
||||
"""
|
||||
|
||||
maximum_allowed_locked = 1500
|
||||
escrow = escrow_contract(maximum_allowed_locked, disable_reward=True)
|
||||
creator, staker1, staker2, staker3, staker4 = testerchain.client.accounts[0:5]
|
||||
deposit_log = escrow.events.Deposited.createFilter(fromBlock='latest')
|
||||
lock_log = escrow.events.Locked.createFilter(fromBlock='latest')
|
||||
re_stake_log = escrow.events.ReStakeSet.createFilter(fromBlock='latest')
|
||||
re_stake_lock_log = escrow.events.ReStakeLocked.createFilter(fromBlock='latest')
|
||||
|
||||
# Deploy WorkLock mock
|
||||
worklock, _ = deploy_contract('WorkLockForStakingEscrowMock', token.address, escrow.address)
|
||||
tx = escrow.functions.setWorkLock(worklock.address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Give WorkLock and Staker some coins
|
||||
tx = token.functions.transfer(staker1, maximum_allowed_locked).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = token.functions.transfer(worklock.address, maximum_allowed_locked).transact({'from': creator})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Can't use method not from WorkLock
|
||||
value = token_economics.minimum_allowed_locked
|
||||
duration = token_economics.minimum_locked_periods
|
||||
with pytest.raises((TransactionFailed, ValueError)):
|
||||
tx = escrow.functions.depositFromWorkLock(staker1, value, duration).transact({'from': staker1})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert token.functions.balanceOf(escrow.address).call() == 0
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
assert not escrow.functions.isReStakeLocked(staker1).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker1).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == 0
|
||||
|
||||
# Deposit tokens from WorkLock
|
||||
current_period = escrow.functions.getCurrentPeriod().call()
|
||||
tx = worklock.functions.depositFromWorkLock(staker1, value, duration).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert token.functions.balanceOf(escrow.address).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker1, 0).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker1, 1).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker1, duration).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker1, duration + 1).call() == 0
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker1).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker1).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker1).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration + 1
|
||||
|
||||
# Check that all events are emitted
|
||||
events = deposit_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker1
|
||||
assert event_args['value'] == value
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = lock_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker1
|
||||
assert event_args['value'] == value
|
||||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 0
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker1
|
||||
assert event_args['lockUntilPeriod'] == current_period + duration + 1
|
||||
|
||||
# Staker disables `reStake` parameter before depositing tokens
|
||||
duration += 1
|
||||
value += 1
|
||||
tx = escrow.functions.setReStake(False).transact({'from': staker2})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
|
||||
assert not re_stake
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 1
|
||||
|
||||
tx = worklock.functions.depositFromWorkLock(staker2, value, duration).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert escrow.functions.getLockedTokens(staker2, 0).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker2, 1).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker2, duration).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker2, duration + 1).call() == 0
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker2).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker2).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker2).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration + 1
|
||||
|
||||
events = deposit_log.get_all_entries()
|
||||
assert len(events) == 2
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker2
|
||||
assert event_args['value'] == value
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = lock_log.get_all_entries()
|
||||
assert len(events) == 2
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker2
|
||||
assert event_args['value'] == value
|
||||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 2
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker2
|
||||
assert event_args['reStake']
|
||||
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 2
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker2
|
||||
assert event_args['lockUntilPeriod'] == current_period + duration + 1
|
||||
|
||||
# Staker locks `reStake` parameter before depositing tokens for short period
|
||||
duration += 1
|
||||
value += 1
|
||||
tx = escrow.functions.setReStake(False).transact({'from': staker3})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
tx = escrow.functions.lockReStake(current_period + duration).transact({'from': staker3})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
staker_info = escrow.functions.stakerInfo(staker3).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 3
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 3
|
||||
|
||||
tx = worklock.functions.depositFromWorkLock(staker3, value, duration).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert escrow.functions.getLockedTokens(staker3, 0).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker3, 1).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker3, duration).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker3, duration + 1).call() == 0
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker3).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker3).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker3).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration + 1
|
||||
|
||||
events = deposit_log.get_all_entries()
|
||||
assert len(events) == 3
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker3
|
||||
assert event_args['value'] == value
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = lock_log.get_all_entries()
|
||||
assert len(events) == 3
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker3
|
||||
assert event_args['value'] == value
|
||||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 4
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker3
|
||||
assert event_args['reStake']
|
||||
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 4
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker3
|
||||
assert event_args['lockUntilPeriod'] == current_period + duration + 1
|
||||
|
||||
# Staker locks `reStake` parameter before depositing tokens for long duration
|
||||
duration += 1
|
||||
value += 1
|
||||
tx = escrow.functions.lockReStake(current_period + duration + 1).transact({'from': staker4})
|
||||
testerchain.wait_for_receipt(tx)
|
||||
staker_info = escrow.functions.stakerInfo(staker4).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration + 1
|
||||
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 5
|
||||
|
||||
tx = worklock.functions.depositFromWorkLock(staker4, value, duration).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
assert escrow.functions.getLockedTokens(staker4, 0).call() == 0
|
||||
assert escrow.functions.getLockedTokens(staker4, 1).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker4, duration).call() == value
|
||||
assert escrow.functions.getLockedTokens(staker4, duration + 1).call() == 0
|
||||
_wind_down, re_stake, _measure_work, _snapshots = escrow.functions.getFlags(staker4).call()
|
||||
assert re_stake
|
||||
assert escrow.functions.isReStakeLocked(staker4).call()
|
||||
staker_info = escrow.functions.stakerInfo(staker4).call()
|
||||
assert staker_info[LOCK_RE_STAKE_UNTIL_PERIOD_FIELD] == current_period + duration + 1
|
||||
|
||||
events = deposit_log.get_all_entries()
|
||||
assert len(events) == 4
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker4
|
||||
assert event_args['value'] == value
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = lock_log.get_all_entries()
|
||||
assert len(events) == 4
|
||||
event_args = events[-1]['args']
|
||||
assert event_args['staker'] == staker4
|
||||
assert event_args['value'] == value
|
||||
assert event_args['firstPeriod'] == current_period + 1
|
||||
assert event_args['periods'] == duration
|
||||
|
||||
events = re_stake_log.get_all_entries()
|
||||
assert len(events) == 4
|
||||
|
||||
events = re_stake_lock_log.get_all_entries()
|
||||
assert len(events) == 5
|
||||
|
|
|
@ -88,7 +88,7 @@ def test_upgrading(testerchain, token, token_economics, deploy_contract):
|
|||
tx = contract.functions.setPolicyManager(policy_manager.address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
worklock, _ = deploy_contract(
|
||||
'WorkLockForStakingEscrowMock', contract.address
|
||||
'WorkLockForStakingEscrowMock', token.address, contract.address
|
||||
)
|
||||
tx = contract.functions.setWorkLock(worklock.address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
@ -810,7 +810,7 @@ def test_measure_work(testerchain, token, escrow_contract, deploy_contract):
|
|||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
# Deploy WorkLock mock
|
||||
worklock, _ = deploy_contract('WorkLockForStakingEscrowMock', escrow.address)
|
||||
worklock, _ = deploy_contract('WorkLockForStakingEscrowMock', token.address, escrow.address)
|
||||
tx = escrow.functions.setWorkLock(worklock.address).transact()
|
||||
testerchain.wait_for_receipt(tx)
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
|||
print(tabulate.tabulate(rows, headers=headers, tablefmt="simple"), end="\n\n")
|
||||
|
||||
# Accounts
|
||||
origin, staker1, staker2, staker3, alice1, alice2, *everyone_else = testerchain.client.accounts
|
||||
origin, staker1, staker2, staker3, staker4, alice1, alice2, *everyone_else = testerchain.client.accounts
|
||||
|
||||
ursula_with_stamp = mock_ursula(testerchain, staker1)
|
||||
|
||||
|
@ -236,12 +236,14 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
|||
#
|
||||
# Batch deposit tokens
|
||||
#
|
||||
current_period = staking_agent.get_current_period()
|
||||
transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 10), {'from': origin})
|
||||
transact_and_log("Batch deposit tokens for 5 owners x 2 sub-stakes",
|
||||
staker_functions.batchDeposit(everyone_else[0:5],
|
||||
[2] * 5,
|
||||
[MIN_ALLOWED_LOCKED] * 10,
|
||||
[MIN_LOCKED_PERIODS] * 10),
|
||||
[MIN_LOCKED_PERIODS] * 10,
|
||||
current_period + 5),
|
||||
{'from': origin})
|
||||
|
||||
transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 24), {'from': origin})
|
||||
|
@ -249,7 +251,8 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
|||
staker_functions.batchDeposit([everyone_else[6]],
|
||||
[24],
|
||||
[MIN_ALLOWED_LOCKED] * 24,
|
||||
[MIN_LOCKED_PERIODS] * 24),
|
||||
[MIN_LOCKED_PERIODS] * 24,
|
||||
current_period + 5),
|
||||
{'from': origin})
|
||||
|
||||
transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * 24 * 5), {'from': origin})
|
||||
|
@ -257,7 +260,8 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
|||
staker_functions.batchDeposit(everyone_else[7:12],
|
||||
[24]*5,
|
||||
[MIN_ALLOWED_LOCKED] * (24 * 5),
|
||||
[MIN_LOCKED_PERIODS] * (24 * 5)),
|
||||
[MIN_LOCKED_PERIODS] * (24 * 5),
|
||||
current_period + 5),
|
||||
{'from': origin})
|
||||
|
||||
#
|
||||
|
@ -577,6 +581,37 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
|
|||
transact_and_log("Prolong stake", staker_functions.prolongStake(0, 20), {'from': staker1})
|
||||
transact_and_log("Merge sub-stakes", staker_functions.mergeStake(2, 3), {'from': staker1})
|
||||
|
||||
# Large number of sub-stakes
|
||||
number_of_sub_stakes = 24
|
||||
transact(token_functions.approve(staking_agent.contract_address, MIN_ALLOWED_LOCKED * number_of_sub_stakes),
|
||||
{'from': origin})
|
||||
transact(staker_functions.batchDeposit([staker4],
|
||||
[number_of_sub_stakes],
|
||||
[MIN_ALLOWED_LOCKED] * number_of_sub_stakes,
|
||||
[MIN_LOCKED_PERIODS] * number_of_sub_stakes,
|
||||
current_period + 100),
|
||||
{'from': origin})
|
||||
transact(staker_functions.bondWorker(staker4), {'from': staker4})
|
||||
transact(staker_functions.setWindDown(True), {'from': staker4})
|
||||
|
||||
# Used to remove spending for first call in a day for mint and commitToNextPeriod
|
||||
transact(staker_functions.commitToNextPeriod(), {'from': staker1})
|
||||
|
||||
transact_and_log(f"Make a commitment ({number_of_sub_stakes} sub-stakes)",
|
||||
staker_functions.commitToNextPeriod(),
|
||||
{'from': staker4})
|
||||
|
||||
testerchain.time_travel(periods=1)
|
||||
transact(staker_functions.commitToNextPeriod(), {'from': staker4})
|
||||
testerchain.time_travel(periods=1)
|
||||
|
||||
# Used to remove spending for first call in a day for mint and commitToNextPeriod
|
||||
transact(staker_functions.commitToNextPeriod(), {'from': staker1})
|
||||
|
||||
transact_and_log(f"Make a commitment + mint + re-stake ({number_of_sub_stakes} sub-stakes)",
|
||||
staker_functions.commitToNextPeriod(),
|
||||
{'from': staker4})
|
||||
|
||||
print("********* All Done! *********")
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue