mirror of https://github.com/nucypher/nucypher.git
527 lines
24 KiB
Python
527 lines
24 KiB
Python
"""
|
|
This file is part of nucypher.
|
|
|
|
nucypher is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
nucypher is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|
"""
|
|
|
|
|
|
import pytest
|
|
from eth_tester.exceptions import TransactionFailed
|
|
|
|
from nucypher.blockchain.eth.token import NU
|
|
|
|
|
|
VESTING_RELEASE_TIMESTAMP_SLOT = 9
|
|
VESTING_RELEASE_RATE_SLOT = 10
|
|
STAKING_PROVIDER_SLOT = 11
|
|
ONE_HOUR = 60 * 60
|
|
|
|
|
|
def test_staking_from_worklock(testerchain, token, worklock, escrow):
|
|
"""
|
|
Tests for staking method: depositFromWorkLock
|
|
"""
|
|
|
|
creator, staker1, staker2, staker3 = testerchain.client.accounts[0:4]
|
|
deposit_log = escrow.events.Deposited.createFilter(fromBlock='latest')
|
|
|
|
# Give WorkLock and Staker some coins
|
|
value = NU(15_000, 'NU').to_units()
|
|
tx = token.functions.transfer(worklock.address, 10 * value).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Can't use method not from WorkLock
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.depositFromWorkLock(staker1, value, 0).transact({'from': staker1})
|
|
testerchain.wait_for_receipt(tx)
|
|
# Can't deposit 0 tokens
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.depositFromWorkLock(staker1, 0, 0).transact({'from': staker1})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert token.functions.balanceOf(escrow.address).call() == 0
|
|
|
|
# First deposit
|
|
tx = worklock.functions.depositFromWorkLock(staker1, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert token.functions.balanceOf(escrow.address).call() == value
|
|
assert escrow.functions.getAllTokens(staker1).call() == value
|
|
assert escrow.functions.getStakersLength().call() == 1
|
|
assert escrow.functions.stakers(0).call() == staker1
|
|
|
|
# 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
|
|
|
|
# Deposit directly and then through WorkLock
|
|
tx = escrow.functions.setStaker(staker2, value, 0).transact({'from': staker2})
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker2, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert token.functions.balanceOf(escrow.address).call() == 2 * value
|
|
assert escrow.functions.getAllTokens(staker2).call() == 2 * value
|
|
assert escrow.functions.getStakersLength().call() == 2
|
|
assert escrow.functions.stakers(1).call() == staker2
|
|
|
|
# Check that all events are emitted
|
|
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
|
|
|
|
# Emulate case when staker withdraws everything and then deposits from WorkLock
|
|
tx = escrow.functions.setStaker(staker3, 0, 1).transact({'from': staker3})
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker3, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert token.functions.balanceOf(escrow.address).call() == 3 * value
|
|
assert escrow.functions.getAllTokens(staker3).call() == value
|
|
assert escrow.functions.getStakersLength().call() == 3
|
|
assert escrow.functions.stakers(2).call() == staker3
|
|
|
|
# Check that all events are emitted
|
|
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
|
|
|
|
|
|
def test_slashing(testerchain, token, worklock, threshold_staking, escrow):
|
|
creator = testerchain.client.accounts[0]
|
|
staker = testerchain.client.accounts[1]
|
|
investigator = testerchain.client.accounts[2]
|
|
|
|
slashing_log = escrow.events.Slashed.createFilter(fromBlock='latest')
|
|
|
|
# Staker deposits some tokens
|
|
stake = NU(15_000, 'NU').to_units()
|
|
tx = token.functions.transfer(worklock.address, 10 * stake).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker, stake, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
assert stake == escrow.functions.getAllTokens(staker).call()
|
|
|
|
reward = stake // 100
|
|
# Can't slash directly using the escrow contract
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.slashStaker(staker, stake, investigator, reward).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
# Penalty must be greater than zero
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = threshold_staking.functions.slashStaker(staker, 0, investigator, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Slash the whole stake
|
|
tx = threshold_staking.functions.slashStaker(staker, 2 * stake, investigator, reward).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
# Staker has no more stake
|
|
assert escrow.functions.getAllTokens(staker).call() == 0
|
|
assert token.functions.balanceOf(investigator).call() == reward
|
|
|
|
events = slashing_log.get_all_entries()
|
|
assert 1 == len(events)
|
|
event_args = events[0]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['penalty'] == stake
|
|
assert event_args['investigator'] == investigator
|
|
assert event_args['reward'] == reward
|
|
|
|
# Slash small part
|
|
tx = worklock.functions.depositFromWorkLock(staker, stake, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
amount_to_slash = stake // 10
|
|
tx = threshold_staking.functions.slashStaker(staker, amount_to_slash, investigator, 2 * amount_to_slash).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
# Staker has no more stake
|
|
assert escrow.functions.getAllTokens(staker).call() == stake - amount_to_slash
|
|
assert token.functions.balanceOf(investigator).call() == reward + amount_to_slash
|
|
|
|
events = slashing_log.get_all_entries()
|
|
assert 2 == len(events)
|
|
event_args = events[1]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['penalty'] == amount_to_slash
|
|
assert event_args['investigator'] == investigator
|
|
assert event_args['reward'] == amount_to_slash
|
|
|
|
# Slash without reward
|
|
tx = threshold_staking.functions.slashStaker(staker, amount_to_slash, investigator, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
# Staker has no more stake
|
|
assert escrow.functions.getAllTokens(staker).call() == stake - 2 * amount_to_slash
|
|
assert token.functions.balanceOf(investigator).call() == reward + amount_to_slash
|
|
|
|
events = slashing_log.get_all_entries()
|
|
assert 3 == len(events)
|
|
event_args = events[2]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['penalty'] == amount_to_slash
|
|
assert event_args['investigator'] == investigator
|
|
assert event_args['reward'] == 0
|
|
|
|
|
|
def test_request_merge(testerchain, threshold_staking, escrow):
|
|
staker1, staker2, staking_provider_1, staking_provider_2 = testerchain.client.accounts[0:4]
|
|
merge_requests_log = escrow.events.MergeRequested.createFilter(fromBlock='latest')
|
|
|
|
# Can't request merge directly
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.requestMerge(staker1, staking_provider_1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Requesting merge for non-existent staker will return zero
|
|
tx = threshold_staking.functions.requestMerge(staker1, staking_provider_1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker1).call() == 0
|
|
assert escrow.functions.stakerInfo(staker1).call()[STAKING_PROVIDER_SLOT] == staking_provider_1
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_1).call()[0] == 0
|
|
|
|
events = merge_requests_log.get_all_entries()
|
|
assert len(events) == 1
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker1
|
|
assert event_args['stakingProvider'] == staking_provider_1
|
|
|
|
# Request can be made several times
|
|
tx = threshold_staking.functions.requestMerge(staker1, staking_provider_1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker1).call() == 0
|
|
assert escrow.functions.stakerInfo(staker1).call()[STAKING_PROVIDER_SLOT] == staking_provider_1
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_1).call()[0] == 0
|
|
assert len(merge_requests_log.get_all_entries()) == 1
|
|
|
|
# Can change provider if old provider has no delegated stake
|
|
tx = threshold_staking.functions.requestMerge(staker1, staker1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker1).call() == 0
|
|
assert escrow.functions.stakerInfo(staker1).call()[STAKING_PROVIDER_SLOT] == staker1
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_1).call()[0] == 0
|
|
|
|
events = merge_requests_log.get_all_entries()
|
|
assert len(events) == 2
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker1
|
|
assert event_args['stakingProvider'] == staker1
|
|
|
|
# Requesting merge for existent staker will return stake
|
|
value = 1000
|
|
tx = escrow.functions.setStaker(staker2, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = threshold_staking.functions.requestMerge(staker2, staking_provider_2).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker2).call() == value
|
|
assert escrow.functions.stakerInfo(staker2).call()[STAKING_PROVIDER_SLOT] == staking_provider_2
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_2).call()[0] == value
|
|
|
|
events = merge_requests_log.get_all_entries()
|
|
assert len(events) == 3
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker2
|
|
assert event_args['stakingProvider'] == staking_provider_2
|
|
|
|
# Request can be made several times
|
|
tx = threshold_staking.functions.requestMerge(staker2, staking_provider_2).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker2).call() == value
|
|
assert escrow.functions.stakerInfo(staker2).call()[STAKING_PROVIDER_SLOT] == staking_provider_2
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_2).call()[0] == value
|
|
|
|
tx = escrow.functions.setStaker(staker2, 2 * value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = threshold_staking.functions.requestMerge(staker2, staking_provider_2).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker2).call() == 2 * value
|
|
assert escrow.functions.stakerInfo(staker2).call()[STAKING_PROVIDER_SLOT] == staking_provider_2
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_2).call()[0] == 2 * value
|
|
|
|
assert len(merge_requests_log.get_all_entries()) == 3
|
|
|
|
# Request can be done only with the same provider when NU is staked
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = threshold_staking.functions.requestMerge(staker2, staking_provider_1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Unstake NU and try again
|
|
tx = threshold_staking.functions.setStakedNu(staking_provider_2, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = threshold_staking.functions.requestMerge(staker2, staking_provider_1).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker2).call() == 2 * value
|
|
assert escrow.functions.stakerInfo(staker2).call()[STAKING_PROVIDER_SLOT] == staking_provider_1
|
|
assert threshold_staking.functions.stakingProviders(staking_provider_1).call()[0] == 2 * value
|
|
|
|
events = merge_requests_log.get_all_entries()
|
|
assert len(events) == 4
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker2
|
|
assert event_args['stakingProvider'] == staking_provider_1
|
|
|
|
|
|
def test_withdraw(testerchain, token, worklock, threshold_staking, escrow):
|
|
creator, staker, staking_provider = testerchain.client.accounts[0:3]
|
|
withdrawal_log = escrow.events.Withdrawn.createFilter(fromBlock='latest')
|
|
|
|
# Deposit some tokens
|
|
value = NU(ONE_HOUR, 'NU').to_units() # Exclude rounding error
|
|
tx = token.functions.transfer(worklock.address, 10 * value).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker, value + 1, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Withdraw without requesting merge
|
|
tx = escrow.functions.withdraw(1).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker).call() == value
|
|
assert token.functions.balanceOf(staker).call() == 1
|
|
assert token.functions.balanceOf(escrow.address).call() == value
|
|
|
|
events = withdrawal_log.get_all_entries()
|
|
assert len(events) == 1
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['value'] == 1
|
|
|
|
tx = threshold_staking.functions.requestMerge(staker, staking_provider).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Can't withdraw because everything is staked
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(1).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Set vesting for the staker
|
|
tx = threshold_staking.functions.setStakedNu(staking_provider, value // 2).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
now = testerchain.w3.eth.getBlock('latest').timestamp
|
|
release_timestamp = now + ONE_HOUR
|
|
rate = 2 * value // ONE_HOUR
|
|
tx = escrow.functions.setupVesting([staker], [release_timestamp], [rate]).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Vesting parameters prevent from withdrawing
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(1).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Wait some time
|
|
testerchain.time_travel(seconds=40 * 60)
|
|
released = value - escrow.functions.getUnvestedTokens(staker).call()
|
|
|
|
# Can't withdraw more than released
|
|
to_withdraw = released + rate # +rate because in new tx timestamp will be one second more
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(to_withdraw + 1).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# to_withdraw += rate
|
|
tx = escrow.functions.withdraw(to_withdraw).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker).call() == value - to_withdraw
|
|
assert token.functions.balanceOf(staker).call() == to_withdraw + 1
|
|
assert token.functions.balanceOf(escrow.address).call() == value - to_withdraw
|
|
|
|
events = withdrawal_log.get_all_entries()
|
|
assert len(events) == 2
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['value'] == to_withdraw
|
|
|
|
# Can't withdraw more than unstaked
|
|
testerchain.time_travel(seconds=20 * 60)
|
|
unstaked = value // 2 - to_withdraw
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(unstaked + 1).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Can't withdraw 0 tokens
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(0).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Only staker can withdraw stake
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.withdraw(1).transact({'from': staking_provider})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
tx = escrow.functions.withdraw(unstaked).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker).call() == value // 2
|
|
assert token.functions.balanceOf(staker).call() == value // 2 + 1
|
|
assert token.functions.balanceOf(escrow.address).call() == value // 2
|
|
|
|
events = withdrawal_log.get_all_entries()
|
|
assert len(events) == 3
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['value'] == unstaked
|
|
|
|
# Now unstake and withdraw everything
|
|
tx = threshold_staking.functions.setStakedNu(staking_provider, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = escrow.functions.withdraw(value // 2).transact({'from': staker})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getAllTokens(staker).call() == 0
|
|
assert token.functions.balanceOf(staker).call() == value + 1
|
|
assert token.functions.balanceOf(escrow.address).call() == 0
|
|
|
|
events = withdrawal_log.get_all_entries()
|
|
assert len(events) == 4
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker
|
|
assert event_args['value'] == value // 2
|
|
|
|
|
|
def test_vesting(testerchain, token, worklock, escrow):
|
|
creator, staker1, staker2, staker3, staker4 = testerchain.client.accounts[0:5]
|
|
vesting_log = escrow.events.VestingSet.createFilter(fromBlock='latest')
|
|
|
|
value = NU(15_000, 'NU').to_units()
|
|
tx = token.functions.transfer(worklock.address, 10 * value).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker1, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
now = testerchain.w3.eth.getBlock('latest').timestamp
|
|
release_timestamp = now + ONE_HOUR
|
|
rate = 2 * value // ONE_HOUR
|
|
|
|
# Only owner can set vesting parameters
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting([staker1], [release_timestamp], [rate]).transact({'from': staker1})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# All input arrays must have same number of values
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting(
|
|
[staker1, staker2],
|
|
[release_timestamp, release_timestamp],
|
|
[rate]
|
|
).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting(
|
|
[staker1, staker2],
|
|
[release_timestamp],
|
|
[rate, rate]
|
|
).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting(
|
|
[staker1],
|
|
[release_timestamp, release_timestamp],
|
|
[rate, rate]
|
|
).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# At least some amount of tokens must be locked after setting parameters
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting([staker1], [now], [rate]).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting(
|
|
[staker1, staker2],
|
|
[release_timestamp, release_timestamp],
|
|
[rate, rate]
|
|
).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
tx = escrow.functions.setupVesting([staker1], [release_timestamp], [rate]).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
assert escrow.functions.getUnvestedTokens(staker1).call() == value
|
|
assert escrow.functions.stakerInfo(staker1).call()[VESTING_RELEASE_TIMESTAMP_SLOT] == release_timestamp
|
|
assert escrow.functions.stakerInfo(staker1).call()[VESTING_RELEASE_RATE_SLOT] == rate
|
|
|
|
events = vesting_log.get_all_entries()
|
|
assert len(events) == 1
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker1
|
|
assert event_args['releaseTimestamp'] == release_timestamp
|
|
assert event_args['releaseRate'] == rate
|
|
|
|
testerchain.time_travel(seconds=40 * 60)
|
|
now = testerchain.w3.eth.getBlock('latest').timestamp
|
|
vested = (release_timestamp - now) * rate
|
|
assert escrow.functions.getUnvestedTokens(staker1).call() == vested
|
|
|
|
testerchain.time_travel(seconds=20 * 60)
|
|
assert escrow.functions.getUnvestedTokens(staker1).call() == 0
|
|
|
|
# Can't set vesting again even after unlocking
|
|
with pytest.raises((TransactionFailed, ValueError)):
|
|
tx = escrow.functions.setupVesting([staker1], [release_timestamp], [rate]).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
# Try again with three other stakers
|
|
value = NU(ONE_HOUR, 'NU').to_units() # Exclude rounding error
|
|
tx = worklock.functions.depositFromWorkLock(staker2, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker3, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
tx = worklock.functions.depositFromWorkLock(staker4, value, 0).transact()
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
now = testerchain.w3.eth.getBlock('latest').timestamp + 1 # +1 sec because tx will be executed in new block
|
|
release_timestamp_2 = now + ONE_HOUR
|
|
release_timestamp_3 = now + 2 * ONE_HOUR
|
|
release_timestamp_4 = now + 2 * ONE_HOUR
|
|
rate_2 = value // ONE_HOUR // 2
|
|
rate_3 = value // ONE_HOUR // 4
|
|
rate_4 = 0
|
|
tx = escrow.functions.setupVesting(
|
|
[staker2, staker3, staker4],
|
|
[release_timestamp_2, release_timestamp_3, release_timestamp_4],
|
|
[rate_2, rate_3, rate_4]
|
|
).transact({'from': creator})
|
|
testerchain.wait_for_receipt(tx)
|
|
|
|
assert escrow.functions.getUnvestedTokens(staker2).call() == value // 2
|
|
assert escrow.functions.getUnvestedTokens(staker3).call() == value // 2
|
|
assert escrow.functions.getUnvestedTokens(staker4).call() == value
|
|
assert escrow.functions.stakerInfo(staker2).call()[VESTING_RELEASE_TIMESTAMP_SLOT] == release_timestamp_2
|
|
assert escrow.functions.stakerInfo(staker2).call()[VESTING_RELEASE_RATE_SLOT] == rate_2
|
|
assert escrow.functions.stakerInfo(staker3).call()[VESTING_RELEASE_TIMESTAMP_SLOT] == release_timestamp_3
|
|
assert escrow.functions.stakerInfo(staker3).call()[VESTING_RELEASE_RATE_SLOT] == rate_3
|
|
assert escrow.functions.stakerInfo(staker4).call()[VESTING_RELEASE_TIMESTAMP_SLOT] == release_timestamp_4
|
|
assert escrow.functions.stakerInfo(staker4).call()[VESTING_RELEASE_RATE_SLOT] == rate_4
|
|
|
|
events = vesting_log.get_all_entries()
|
|
assert len(events) == 4
|
|
event_args = events[-3]['args']
|
|
assert event_args['staker'] == staker2
|
|
assert event_args['releaseTimestamp'] == release_timestamp_2
|
|
assert event_args['releaseRate'] == rate_2
|
|
event_args = events[-2]['args']
|
|
assert event_args['staker'] == staker3
|
|
assert event_args['releaseTimestamp'] == release_timestamp_3
|
|
assert event_args['releaseRate'] == rate_3
|
|
event_args = events[-1]['args']
|
|
assert event_args['staker'] == staker4
|
|
assert event_args['releaseTimestamp'] == release_timestamp_4
|
|
assert event_args['releaseRate'] == rate_4
|
|
|
|
testerchain.time_travel(seconds=ONE_HOUR)
|
|
assert escrow.functions.getUnvestedTokens(staker2).call() == 0
|
|
assert escrow.functions.getUnvestedTokens(staker3).call() == value // 4
|
|
assert escrow.functions.getUnvestedTokens(staker4).call() == value
|
|
|
|
testerchain.time_travel(seconds=ONE_HOUR)
|
|
assert escrow.functions.getUnvestedTokens(staker2).call() == 0
|
|
assert escrow.functions.getUnvestedTokens(staker3).call() == 0
|
|
assert escrow.functions.getUnvestedTokens(staker4).call() == 0
|