diff --git a/nucypher/blockchain/economics.py b/nucypher/blockchain/economics.py
index ad3a2fcd0..65dc04589 100644
--- a/nucypher/blockchain/economics.py
+++ b/nucypher/blockchain/economics.py
@@ -21,6 +21,8 @@ from typing import Tuple
from math import log
+from nucypher.blockchain.eth.agents import ContractAgency, NucypherTokenAgent, StakingEscrowAgent, AdjudicatorAgent
+from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.blockchain.eth.token import NU
@@ -28,6 +30,153 @@ LOG2 = Decimal(log(2))
class TokenEconomics:
+ """
+ Parameters to use in token and escrow blockchain deployments
+ from high-level human-understandable parameters.
+
+ --------------------------
+
+ Formula for staking in one period:
+ (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2
+
+ K2 - Staking coefficient
+ K1 - Locked periods coefficient
+
+ if allLockedPeriods > maximum_rewarded_periods then allLockedPeriods = maximum_rewarded_periods
+ kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2
+
+ """
+
+ # Token Denomination
+ __token_decimals = 18
+ nunits_per_token = 10 ** __token_decimals # Smallest unit designation
+
+ # Period Definition
+ __default_hours_per_period = 24
+
+ # Time Constraints
+ __default_minimum_worker_periods = 2
+ __default_minimum_locked_periods = 30 # 720 Hours minimum
+
+ # Value Constraints
+ __default_minimum_allowed_locked = NU(15_000, 'NU').to_nunits()
+ __default_maximum_allowed_locked = NU(4_000_000, 'NU').to_nunits()
+
+ # Slashing parameters
+ HASH_ALGORITHM_KECCAK256 = 0
+ HASH_ALGORITHM_SHA256 = 1
+ HASH_ALGORITHM_RIPEMD160 = 2
+
+ __default_hash_algorithm = HASH_ALGORITHM_SHA256
+ __default_base_penalty = 100
+ __default_penalty_history_coefficient = 10
+ __default_percentage_penalty_coefficient = 8
+ __default_reward_coefficient = 2
+
+ def __init__(self,
+ initial_supply: int,
+ total_supply: int,
+ staking_coefficient: int,
+ locked_periods_coefficient: int,
+ maximum_rewarded_periods: int,
+ hours_per_period: int = __default_hours_per_period,
+ minimum_locked_periods: int = __default_minimum_locked_periods,
+ minimum_allowed_locked: int = __default_minimum_allowed_locked,
+ maximum_allowed_locked: int = __default_maximum_allowed_locked,
+ minimum_worker_periods: int = __default_minimum_worker_periods,
+
+ hash_algorithm: int = __default_hash_algorithm,
+ base_penalty: int = __default_base_penalty,
+ penalty_history_coefficient: int = __default_penalty_history_coefficient,
+ percentage_penalty_coefficient: int = __default_percentage_penalty_coefficient,
+ reward_coefficient: int = __default_reward_coefficient
+ ):
+ """
+ :param initial_supply: Tokens at t=0
+ :param total_supply: Tokens at t=8
+ :param staking_coefficient: K2
+ :param locked_periods_coefficient: K1
+ :param maximum_rewarded_periods: Max periods that will be additionally rewarded
+ :param hours_per_period: Hours in single period
+ :param minimum_locked_periods: Min amount of periods during which tokens can be locked
+ :param minimum_allowed_locked: Min amount of tokens that can be locked
+ :param maximum_allowed_locked: Max amount of tokens that can be locked
+ :param minimum_worker_periods: Min amount of periods while a worker can't be changed
+
+ :param hash_algorithm: Hashing algorithm
+ :param base_penalty: Base for the penalty calculation
+ :param penalty_history_coefficient: Coefficient for calculating the penalty depending on the history
+ :param percentage_penalty_coefficient: Coefficient for calculating the percentage penalty
+ :param reward_coefficient: Coefficient for calculating the reward
+ """
+
+ self.initial_supply = initial_supply
+ # Remaining / Reward Supply - Escrow Parameter
+ self.reward_supply = total_supply - initial_supply
+ self.total_supply = total_supply
+ self.staking_coefficient = staking_coefficient
+ self.locked_periods_coefficient = locked_periods_coefficient
+ self.maximum_rewarded_periods = maximum_rewarded_periods
+ self.hours_per_period = hours_per_period
+ self.minimum_locked_periods = minimum_locked_periods
+ self.minimum_allowed_locked = minimum_allowed_locked
+ self.maximum_allowed_locked = maximum_allowed_locked
+ self.minimum_worker_periods = minimum_worker_periods
+ self.seconds_per_period = hours_per_period * 60 * 60 # Seconds in single period
+
+ self.hash_algorithm = hash_algorithm
+ self.base_penalty = base_penalty
+ self.penalty_history_coefficient = penalty_history_coefficient
+ self.percentage_penalty_coefficient = percentage_penalty_coefficient
+ self.reward_coefficient = reward_coefficient
+
+ @property
+ def erc20_initial_supply(self) -> int:
+ return int(self.initial_supply)
+
+ @property
+ def erc20_reward_supply(self) -> int:
+ return int(self.reward_supply)
+
+ @property
+ def erc20_total_supply(self) -> int:
+ return int(self.total_supply)
+
+ @property
+ def staking_deployment_parameters(self) -> Tuple[int, ...]:
+ """Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
+ deploy_parameters = (
+
+ # Period
+ self.hours_per_period, # Hours in single period
+
+ # Coefficients
+ self.staking_coefficient, # Staking coefficient (k2)
+ self.locked_periods_coefficient, # Locked periods coefficient (k1)
+ self.maximum_rewarded_periods, # Max periods that will be additionally rewarded (awarded_periods)
+
+ # Constraints
+ self.minimum_locked_periods, # Min amount of periods during which tokens can be locked
+ self.minimum_allowed_locked, # Min amount of tokens that can be locked
+ self.maximum_allowed_locked, # Max amount of tokens that can be locked
+ self.minimum_worker_periods # Min amount of periods while a worker can't be changed
+ )
+ return tuple(map(int, deploy_parameters))
+
+ @property
+ def slashing_deployment_parameters(self) -> Tuple[int, ...]:
+ """Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
+ deployment_parameters = [
+ self.hash_algorithm,
+ self.base_penalty,
+ self.penalty_history_coefficient,
+ self.percentage_penalty_coefficient,
+ self.reward_coefficient
+ ]
+ return tuple(map(int, deployment_parameters))
+
+
+class StandardTokenEconomics(TokenEconomics):
"""
Calculate parameters to use in token and escrow blockchain deployments
from high-level human-understandable parameters.
@@ -40,7 +189,7 @@ class TokenEconomics:
K2 - Staking coefficient
K1 - Locked periods coefficient
- if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
+ if allLockedPeriods > maximum_rewarded_periods then allLockedPeriods = maximum_rewarded_periods
kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2
...but also...
@@ -60,22 +209,6 @@ class TokenEconomics:
# Decimal
_precision = 28
- # Token Denomination
- __token_decimals = 18
- nunits_per_token = 10 ** __token_decimals # Smallest unit designation
-
- # Period Definition
- hours_per_period = 24 # Hours in single period
- seconds_per_period = hours_per_period * 60 * 60 # Seconds in single period
-
- # Time Constraints
- minimum_worker_periods = 2
- minimum_locked_periods = 30 # 720 Hours minimum
-
- # Value Constraints
- minimum_allowed_locked = NU(15_000, 'NU').to_nunits()
- maximum_allowed_locked = NU(4_000_000, 'NU').to_nunits()
-
# Supply
__default_initial_supply = NU(int(1_000_000_000), 'NU').to_nunits()
__default_initial_inflation = 1
@@ -88,7 +221,8 @@ class TokenEconomics:
initial_inflation: int = __default_initial_inflation,
halving_delay: int = __default_token_halving,
reward_saturation: int = __default_reward_saturation,
- small_stake_multiplier: Decimal = __default_small_stake_multiplier
+ small_stake_multiplier: Decimal = __default_small_stake_multiplier,
+ **kwargs
):
"""
:param initial_supply: Tokens at t=0
@@ -110,9 +244,6 @@ class TokenEconomics:
# ERC20 Token parameter (See Equation 4 in Mining paper)
total_supply = initial_supply * (1 + initial_inflation * halving_delay / LOG2)
- # Remaining / Reward Supply - Escrow Parameter
- reward_supply = total_supply - initial_supply
-
# k2 - Escrow parameter
staking_coefficient = 365 ** 2 * reward_saturation * halving_delay / LOG2 / (1 - small_stake_multiplier)
@@ -120,78 +251,82 @@ class TokenEconomics:
locked_periods_coefficient = 365 * reward_saturation * small_stake_multiplier / (1 - small_stake_multiplier)
# Awarded periods- Escrow parameter
- maximum_locked_periods = reward_saturation * 365
+ maximum_rewarded_periods = reward_saturation * 365
# Injected
- self.initial_supply = initial_supply
self.initial_inflation = initial_inflation
self.token_halving = halving_delay
self.token_saturation = reward_saturation
self.small_stake_multiplier = small_stake_multiplier
- # Calculated
- self.__total_supply = total_supply
- self.reward_supply = reward_supply
- self.staking_coefficient = staking_coefficient
- self.locked_periods_coefficient = locked_periods_coefficient
- self.maximum_locked_periods = maximum_locked_periods
-
- @property
- def erc20_initial_supply(self) -> int:
- return int(self.initial_supply)
-
- @property
- def erc20_reward_supply(self) -> int:
- return int(self.reward_supply)
-
- @property
- def erc20_total_supply(self) -> int:
- return int(self.__total_supply)
-
- @property
- def staking_deployment_parameters(self) -> Tuple[int, ...]:
- """Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
- deploy_parameters = (
-
- # Period
- self.hours_per_period, # Hours in single period
-
- # Coefficients
- self.staking_coefficient, # Staking coefficient (k2)
- self.locked_periods_coefficient, # Locked periods coefficient (k1)
- self.maximum_locked_periods, # Max periods that will be additionally rewarded (awarded_periods)
-
- # Constraints
- self.minimum_locked_periods, # Min amount of periods during which tokens can be locked
- self.minimum_allowed_locked, # Min amount of tokens that can be locked
- self.maximum_allowed_locked, # Max amount of tokens that can be locked
- self.minimum_worker_periods # Min amount of periods while a worker can't be changed
+ super(StandardTokenEconomics, self).__init__(
+ initial_supply,
+ total_supply,
+ staking_coefficient,
+ locked_periods_coefficient,
+ maximum_rewarded_periods,
+ **kwargs
)
- return tuple(map(int, deploy_parameters))
+
+ def token_supply_at_period(self, period: int) -> int:
+ if period < 0:
+ raise ValueError("Period must be a positive integer")
+
+ with localcontext() as ctx:
+ ctx.prec = self._precision
+
+ # Eq. 3 of the mining paper
+ # https://github.com/nucypher/mining-paper/blob/master/mining-paper.pdf
+ t = Decimal(period)
+ S_0 = self.erc20_initial_supply
+ i_0 = 1
+ I_0 = i_0 * S_0 # in 1/years
+ T_half = self.token_halving # in years
+ T_half_in_days = T_half * 365
+
+ S_t = S_0 + I_0 * T_half * (1 - 2**(-t / T_half_in_days)) / LOG2
+ return int(S_t)
+
+ def cumulative_rewards_at_period(self, period: int) -> int:
+ return self.token_supply_at_period(period) - self.erc20_initial_supply
+
+ def rewards_during_period(self, period: int) -> int:
+ return self.cumulative_rewards_at_period(period) - self.cumulative_rewards_at_period(period-1)
-class SlashingEconomics:
+class TokenEconomicsFactory:
+ # TODO: Enforce singleton
- HASH_ALGORITHM_KECCAK256 = 0
- HASH_ALGORITHM_SHA256 = 1
- HASH_ALGORITHM_RIPEMD160 = 2
+ __economics = dict()
- hash_algorithm = HASH_ALGORITHM_SHA256
- base_penalty = 100
- penalty_history_coefficient = 10
- percentage_penalty_coefficient = 8
- reward_coefficient = 2
+ @classmethod
+ def get_economics(cls, registry: BaseContractRegistry) -> TokenEconomics:
+ registry_id = registry.id
+ try:
+ return cls.__economics[registry_id]
+ except KeyError:
+ economics = TokenEconomicsFactory.retrieve_from_blockchain(registry=registry)
+ cls.__economics[registry_id] = economics
+ return economics
- @property
- def deployment_parameters(self) -> Tuple[int, ...]:
- """Cast coefficient attributes to uint256 compatible type for solidity+EVM"""
+ @staticmethod
+ def retrieve_from_blockchain(registry: BaseContractRegistry) -> TokenEconomics:
+ token_agent = ContractAgency.get_agent(NucypherTokenAgent, registry=registry)
+ staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry)
+ adjudicator_agent = ContractAgency.get_agent(AdjudicatorAgent, registry=registry)
- deployment_parameters = [
- self.hash_algorithm,
- self.base_penalty,
- self.penalty_history_coefficient,
- self.percentage_penalty_coefficient,
- self.reward_coefficient
- ]
+ total_supply = token_agent.contract.functions.totalSupply().call()
+ reward_supply = staking_agent.contract.functions.getReservedReward().call()
+ # it's not real initial_supply value because used current reward instead of initial
+ initial_supply = total_supply - reward_supply
- return tuple(map(int, deployment_parameters))
+ staking_parameters = list(staking_agent.staking_parameters())
+ seconds_per_period = staking_parameters.pop(0)
+ staking_parameters.insert(3, seconds_per_period // 60 // 60) # hours_per_period
+ slashing_parameters = adjudicator_agent.slashing_parameters()
+ economics_parameters = (initial_supply,
+ total_supply,
+ *staking_parameters,
+ *slashing_parameters)
+ economics = TokenEconomics(*economics_parameters)
+ return economics
diff --git a/nucypher/blockchain/eth/actors.py b/nucypher/blockchain/eth/actors.py
index b0329e2a8..b10474658 100644
--- a/nucypher/blockchain/eth/actors.py
+++ b/nucypher/blockchain/eth/actors.py
@@ -33,7 +33,7 @@ from eth_tester.exceptions import TransactionFailed
from eth_utils import keccak
from twisted.logger import Logger
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import TokenEconomics, StandardTokenEconomics, TokenEconomicsFactory
from nucypher.blockchain.eth.agents import (
NucypherTokenAgent,
StakingEscrowAgent,
@@ -157,7 +157,8 @@ class ContractAdministrator(NucypherTokenActor):
def __init__(self,
registry: BaseContractRegistry,
deployer_address: str = None,
- client_password: str = None):
+ client_password: str = None,
+ economics: TokenEconomics = None):
"""
Note: super() is not called here to avoid setting the token agent.
TODO: Review this logic ^^ "bare mode".
@@ -166,6 +167,7 @@ class ContractAdministrator(NucypherTokenActor):
self.deployer_address = deployer_address
self.checksum_address = self.deployer_address
+ self.economics = economics or StandardTokenEconomics()
self.registry = registry
self.user_escrow_deployers = dict()
@@ -202,11 +204,17 @@ class ContractAdministrator(NucypherTokenActor):
contract_name: str,
gas_limit: int = None,
plaintext_secret: str = None,
- progress=None
+ progress=None,
+ *args,
+ **kwargs,
) -> Tuple[dict, ContractDeployer]:
Deployer = self.__get_deployer(contract_name=contract_name)
- deployer = Deployer(registry=self.registry, deployer_address=self.deployer_address)
+ deployer = Deployer(registry=self.registry,
+ deployer_address=self.deployer_address,
+ economics=self.economics,
+ *args,
+ **kwargs)
if Deployer._upgradeable:
if not plaintext_secret:
raise ValueError("Upgrade plaintext_secret must be passed to deploy an upgradeable contract.")
@@ -395,10 +403,7 @@ class Staker(NucypherTokenActor):
class InsufficientTokens(StakerError):
pass
- def __init__(self,
- is_me: bool,
- economics: TokenEconomics = None,
- *args, **kwargs) -> None:
+ def __init__(self, is_me: bool, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.log = Logger("staker")
@@ -409,7 +414,7 @@ class Staker(NucypherTokenActor):
# Blockchain
self.policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=self.registry)
self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=self.registry)
- self.economics = economics or TokenEconomics()
+ self.economics = TokenEconomicsFactory.get_economics(registry=self.registry)
self.stakes = StakeList(registry=self.registry, checksum_address=self.checksum_address)
def to_dict(self) -> dict:
@@ -651,7 +656,6 @@ class BlockchainPolicyAuthor(NucypherTokenActor):
def __init__(self,
checksum_address: str,
- economics: TokenEconomics = None,
rate: int = None,
duration_periods: int = None,
first_period_reward: int = None,
@@ -668,7 +672,7 @@ class BlockchainPolicyAuthor(NucypherTokenActor):
self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=self.registry)
self.policy_agent = ContractAgency.get_agent(PolicyManagerAgent, registry=self.registry)
- self.economics = economics or TokenEconomics()
+ self.economics = TokenEconomicsFactory.get_economics(registry=self.registry)
self.rate = rate
self.duration_periods = duration_periods
self.first_period_reward = first_period_reward
@@ -700,7 +704,8 @@ class BlockchainPolicyAuthor(NucypherTokenActor):
# Calculate duration in periods and expiration datetime
if expiration:
- duration_periods = calculate_period_duration(future_time=expiration)
+ duration_periods = calculate_period_duration(future_time=expiration,
+ seconds_per_period=self.economics.seconds_per_period)
else:
duration_periods = duration_periods or self.duration_periods
expiration = datetime_at_period(self.staking_agent.get_current_period() + duration_periods)
diff --git a/nucypher/blockchain/eth/agents.py b/nucypher/blockchain/eth/agents.py
index 8c29cd08f..fa98b0e90 100644
--- a/nucypher/blockchain/eth/agents.py
+++ b/nucypher/blockchain/eth/agents.py
@@ -621,3 +621,17 @@ class AdjudicatorAgent(EthereumContractAgent):
def penalty_history(self, staker_address: str) -> int:
return self.contract.functions.penaltyHistory(staker_address).call()
+ def slashing_parameters(self) -> Tuple:
+ parameter_signatures = (
+ 'hashAlgorithm', # Hashing algorithm
+ 'basePenalty', # Base for the penalty calculation
+ 'penaltyHistoryCoefficient', # Coefficient for calculating the penalty depending on the history
+ 'percentagePenaltyCoefficient', # Coefficient for calculating the percentage penalty
+ 'rewardCoefficient', # Coefficient for calculating the reward
+ )
+
+ def _call_function_by_name(name: str):
+ return getattr(self.contract.functions, name)().call()
+
+ staking_parameters = tuple(map(_call_function_by_name, parameter_signatures))
+ return staking_parameters
diff --git a/nucypher/blockchain/eth/deployers.py b/nucypher/blockchain/eth/deployers.py
index 23d2c4464..4a602053a 100644
--- a/nucypher/blockchain/eth/deployers.py
+++ b/nucypher/blockchain/eth/deployers.py
@@ -22,7 +22,7 @@ from constant_sorrow.constants import CONTRACT_NOT_DEPLOYED, NO_DEPLOYER_CONFIGU
from eth_utils import is_checksum_address
from web3.contract import Contract
-from nucypher.blockchain.economics import TokenEconomics, SlashingEconomics
+from nucypher.blockchain.economics import TokenEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.agents import (
EthereumContractAgent,
StakingEscrowAgent,
@@ -67,10 +67,6 @@ class ContractDeployer:
if not isinstance(self.blockchain, BlockchainDeployerInterface):
raise ValueError("No deployer interface connection available.")
- if not economics:
- economics = TokenEconomics()
- self.__economics = economics
-
#
# Defaults
#
@@ -80,6 +76,7 @@ class ContractDeployer:
self.__proxy_contract = NotImplemented
self.__deployer_address = deployer_address
self.__ready_to_deploy = False
+ self.__economics = economics or StandardTokenEconomics()
@property
def economics(self) -> TokenEconomics:
@@ -272,11 +269,8 @@ class StakingEscrowDeployer(ContractDeployer):
_upgradeable = True
_proxy_deployer = DispatcherDeployer
- def __init__(self, economics: TokenEconomics = None, *args, **kwargs):
+ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
- if not economics:
- economics = TokenEconomics()
- self.__economics = economics
self.__dispatcher_contract = None
token_contract_name = NucypherTokenDeployer.contract_name
@@ -289,7 +283,7 @@ class StakingEscrowDeployer(ContractDeployer):
raise RuntimeError("PolicyManager contract is not initialized.")
def _deploy_essential(self, gas_limit: int = None):
- escrow_constructor_args = (self.token_contract.address, *self.__economics.staking_deployment_parameters)
+ escrow_constructor_args = (self.token_contract.address, *self.economics.staking_deployment_parameters)
the_escrow_contract, deploy_receipt = self.blockchain.deploy_contract(
self.deployer_address,
self.registry,
@@ -352,7 +346,7 @@ class StakingEscrowDeployer(ContractDeployer):
# 3 - Transfer the reward supply tokens to StakingEscrow #
reward_function = self.token_contract.functions.transfer(the_escrow_contract.address,
- self.__economics.erc20_reward_supply)
+ self.economics.erc20_reward_supply)
# TODO: Confirmations / Successful Transaction Indicator / Events ??
reward_receipt = self.blockchain.send_transaction(contract_function=reward_function,
@@ -430,7 +424,6 @@ class StakingEscrowDeployer(ContractDeployer):
return rollback_receipt
-
class PolicyManagerDeployer(ContractDeployer):
"""
Depends on StakingEscrow and NucypherTokenAgent
@@ -446,11 +439,6 @@ class PolicyManagerDeployer(ContractDeployer):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
-
- token_contract_name = NucypherTokenDeployer.contract_name
- self.token_contract = self.blockchain.get_contract_by_name(registry=self.registry,
- name=token_contract_name)
-
proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
staking_contract_name = StakingEscrowDeployer.contract_name
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
@@ -812,10 +800,8 @@ class AdjudicatorDeployer(ContractDeployer):
_upgradeable = True
_proxy_deployer = DispatcherDeployer
- def __init__(self, economics: SlashingEconomics = None, *args, **kwargs):
- if not economics:
- economics = SlashingEconomics()
- super().__init__(*args, economics=economics, **kwargs)
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
staking_contract_name = StakingEscrowDeployer.contract_name
proxy_name = StakingEscrowDeployer._proxy_deployer.contract_name
self.staking_contract = self.blockchain.get_contract_by_name(registry=self.registry,
@@ -824,7 +810,7 @@ class AdjudicatorDeployer(ContractDeployer):
def _deploy_essential(self, gas_limit: int = None):
constructor_args = (self.staking_contract.address,
- *self.economics.deployment_parameters)
+ *self.economics.slashing_deployment_parameters)
adjudicator_contract, deploy_receipt = self.blockchain.deploy_contract(self.deployer_address,
self.registry,
self.contract_name,
@@ -840,8 +826,8 @@ class AdjudicatorDeployer(ContractDeployer):
progress.update(1)
proxy_deployer = self._proxy_deployer(registry=self.registry,
- target_contract=adjudicator_contract,
- deployer_address=self.deployer_address)
+ target_contract=adjudicator_contract,
+ deployer_address=self.deployer_address)
proxy_deploy_receipts = proxy_deployer.deploy(secret_hash=secret_hash, gas_limit=gas_limit)
proxy_deploy_receipt = proxy_deploy_receipts[proxy_deployer.deployment_steps[0]]
@@ -888,9 +874,9 @@ class AdjudicatorDeployer(ContractDeployer):
use_proxy_address=False)
proxy_deployer = self._proxy_deployer(registry=self.registry,
- target_contract=existing_bare_contract,
- deployer_address=self.deployer_address,
- bare=True)
+ target_contract=existing_bare_contract,
+ deployer_address=self.deployer_address,
+ bare=True)
adjudicator_contract, deploy_receipt = self._deploy_essential(gas_limit=gas_limit)
diff --git a/nucypher/blockchain/eth/sol/source/contracts/Issuer.sol b/nucypher/blockchain/eth/sol/source/contracts/Issuer.sol
index 836fb8c6b..5707157e1 100644
--- a/nucypher/blockchain/eth/sol/source/contracts/Issuer.sol
+++ b/nucypher/blockchain/eth/sol/source/contracts/Issuer.sol
@@ -121,21 +121,15 @@ contract Issuer is Upgradeable {
Math.min(currentSupply1, currentSupply2) :
Math.max(currentSupply1, currentSupply2);
- //totalSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2) -
- //currentSupply * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2)
+ //(totalSupply - currentSupply) * lockedValue * (k1 + allLockedPeriods) / (totalLockedValue * k2)
uint256 allLockedPeriods = uint256(_allLockedPeriods <= rewardedPeriods ?
_allLockedPeriods : rewardedPeriods)
.add(lockedPeriodsCoefficient);
uint256 denominator = _totalLockedValue.mul(miningCoefficient);
- amount =
- totalSupply
- .mul(_lockedValue)
- .mul(allLockedPeriods)
- .div(denominator).sub(
- currentSupply
- .mul(_lockedValue)
- .mul(allLockedPeriods)
- .div(denominator));
+ amount = totalSupply.sub(currentSupply)
+ .mul(_lockedValue)
+ .mul(allLockedPeriods)
+ .div(denominator);
// rounding the last reward
if (amount == 0) {
amount = 1;
diff --git a/nucypher/blockchain/eth/token.py b/nucypher/blockchain/eth/token.py
index 41f84b657..82f3e2030 100644
--- a/nucypher/blockchain/eth/token.py
+++ b/nucypher/blockchain/eth/token.py
@@ -1,3 +1,20 @@
+"""
+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 .
+"""
+
from _pydecimal import Decimal
from collections import UserList
from typing import Union, Tuple, List
@@ -151,7 +168,7 @@ class Stake:
first_locked_period: int,
final_locked_period: int,
index: int,
- economics=None,
+ economics,
validate_now: bool = True):
self.log = Logger(f'stake-{checksum_address}-{index}')
@@ -174,19 +191,20 @@ class Stake:
# and no confirmation can be performed in this period for this stake.
self.final_locked_period = final_locked_period
- # Time
- self.start_datetime = datetime_at_period(period=first_locked_period)
- self.unlock_datetime = datetime_at_period(period=final_locked_period + 1)
-
# Blockchain
self.staking_agent = staking_agent
# Economics
- from nucypher.blockchain.economics import TokenEconomics
- self.economics = economics or TokenEconomics()
+ self.economics = economics
self.minimum_nu = NU(int(self.economics.minimum_allowed_locked), 'NuNit')
self.maximum_nu = NU(int(self.economics.maximum_allowed_locked), 'NuNit')
+ # Time
+ self.start_datetime = datetime_at_period(period=first_locked_period,
+ seconds_per_period=self.economics.seconds_per_period)
+ self.unlock_datetime = datetime_at_period(period=final_locked_period + 1,
+ seconds_per_period=self.economics.seconds_per_period)
+
if validate_now:
self.validate_duration()
@@ -219,6 +237,7 @@ class Stake:
checksum_address: str,
index: int,
stake_info: Tuple[int, int, int],
+ economics,
*args, **kwargs
) -> 'Stake':
@@ -230,6 +249,7 @@ class Stake:
first_locked_period=first_locked_period,
final_locked_period=final_locked_period,
value=NU(value, 'NuNit'),
+ economics=economics,
*args, **kwargs)
instance.worker_address = instance.staking_agent.get_worker_from_staker(staker_address=checksum_address)
@@ -378,7 +398,8 @@ class Stake:
first_locked_period=self.first_locked_period,
final_locked_period=self.final_locked_period,
value=remaining_stake_value,
- staking_agent=self.staking_agent)
+ staking_agent=self.staking_agent,
+ economics=self.economics)
# New Derived Stake
end_period = self.final_locked_period + additional_periods
@@ -387,7 +408,8 @@ class Stake:
final_locked_period=end_period,
value=target_value,
index=NEW_STAKE,
- staking_agent=self.staking_agent)
+ staking_agent=self.staking_agent,
+ economics=self.economics)
#
# Validate
@@ -425,7 +447,8 @@ class Stake:
final_locked_period=final_locked_period,
value=amount,
index=NEW_STAKE,
- staking_agent=staker.staking_agent)
+ staking_agent=staker.staking_agent,
+ economics=staker.economics)
# Validate
stake.validate_value()
@@ -550,6 +573,8 @@ class StakeList(UserList):
super().__init__(*args, **kwargs)
self.log = Logger('stake-tracker')
self.staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=registry)
+ from nucypher.blockchain.economics import TokenEconomicsFactory
+ self.economics = TokenEconomicsFactory.get_economics(registry=registry)
self.__terminal_period = NOT_STAKING
@@ -590,7 +615,8 @@ class StakeList(UserList):
onchain_stake = Stake.from_stake_info(checksum_address=self.checksum_address,
stake_info=stake_info,
staking_agent=self.staking_agent,
- index=onchain_index)
+ index=onchain_index,
+ economics=self.economics)
# rack the latest terminal period
if onchain_stake.final_locked_period > terminal_period:
diff --git a/nucypher/blockchain/eth/utils.py b/nucypher/blockchain/eth/utils.py
index bc00c8cb0..ceee07dd2 100644
--- a/nucypher/blockchain/eth/utils.py
+++ b/nucypher/blockchain/eth/utils.py
@@ -20,23 +20,22 @@ from constant_sorrow.constants import UNKNOWN_DEVELOPMENT_CHAIN_ID
from eth_utils import is_address, to_checksum_address, is_hex
-def epoch_to_period(epoch: int) -> int:
- from nucypher.blockchain.economics import TokenEconomics
- period = epoch // int(TokenEconomics.seconds_per_period)
+def epoch_to_period(epoch: int, seconds_per_period: int) -> int:
+ period = epoch // seconds_per_period
return period
-def datetime_to_period(datetime: maya.MayaDT) -> int:
+def datetime_to_period(datetime: maya.MayaDT, seconds_per_period: int) -> int:
"""Converts a MayaDT instance to a period number."""
- future_period = epoch_to_period(epoch=datetime.epoch)
+ future_period = epoch_to_period(epoch=datetime.epoch, seconds_per_period=seconds_per_period)
return int(future_period)
-def datetime_at_period(period: int) -> maya.MayaDT:
+def datetime_at_period(period: int, seconds_per_period: int) -> maya.MayaDT:
"""Returns the datetime object at a given period, future, or past."""
now = maya.now()
- current_period = datetime_to_period(datetime=now)
+ current_period = datetime_to_period(datetime=now, seconds_per_period=seconds_per_period)
delta_periods = period - current_period
# +
@@ -50,10 +49,10 @@ def datetime_at_period(period: int) -> maya.MayaDT:
return target_period
-def calculate_period_duration(future_time: maya.MayaDT) -> int:
+def calculate_period_duration(future_time: maya.MayaDT, seconds_per_period: int) -> int:
"""Takes a future MayaDT instance and calculates the duration from now, returning in periods"""
- future_period = datetime_to_period(datetime=future_time)
- current_period = datetime_to_period(datetime=maya.now())
+ future_period = datetime_to_period(datetime=future_time, seconds_per_period=seconds_per_period)
+ current_period = datetime_to_period(datetime=maya.now(), seconds_per_period=seconds_per_period)
periods = future_period - current_period
return periods
diff --git a/nucypher/characters/chaotic.py b/nucypher/characters/chaotic.py
index e490dda2f..6a7bd7c3e 100644
--- a/nucypher/characters/chaotic.py
+++ b/nucypher/characters/chaotic.py
@@ -20,7 +20,7 @@ from twisted.logger import Logger
from hendrix.deploy.base import HendrixDeploy
from hendrix.experience import hey_joe
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import TokenEconomicsFactory
from nucypher.blockchain.eth.actors import NucypherTokenActor
from nucypher.blockchain.eth.agents import NucypherTokenAgent, ContractAgency
from nucypher.blockchain.eth.interfaces import BlockchainInterface
@@ -164,7 +164,6 @@ class Felix(Character, NucypherTokenActor):
rest_port: int,
client_password: str = None,
crash_on_error: bool = False,
- economics: TokenEconomics = None,
distribute_ether: bool = True,
registry: BaseContractRegistry = None,
*args, **kwargs):
@@ -205,12 +204,9 @@ class Felix(Character, NucypherTokenActor):
self._distribution_task.clock = self._CLOCK
self.start_time = NOT_RUNNING
- if not economics:
- economics = TokenEconomics()
- self.economics = economics
-
- self.MAXIMUM_DISBURSEMENT = economics.maximum_allowed_locked
- self.INITIAL_DISBURSEMENT = economics.minimum_allowed_locked * 3
+ self.economics = TokenEconomicsFactory.get_economics(registry=registry)
+ self.MAXIMUM_DISBURSEMENT = self.economics.maximum_allowed_locked
+ self.INITIAL_DISBURSEMENT = self.economics.minimum_allowed_locked * 3
# Optionally send ether with each token transaction
self.distribute_ether = distribute_ether
diff --git a/nucypher/characters/lawful.py b/nucypher/characters/lawful.py
index 6efaa66e6..cf88ba2ae 100644
--- a/nucypher/characters/lawful.py
+++ b/nucypher/characters/lawful.py
@@ -42,7 +42,6 @@ from umbral.pre import UmbralCorrectnessError
from umbral.signing import Signature
import nucypher
-from nucypher.blockchain.economics import TokenEconomics
from nucypher.blockchain.eth.actors import BlockchainPolicyAuthor, Worker, Staker
from nucypher.blockchain.eth.agents import StakingEscrowAgent, NucypherTokenAgent, ContractAgency
from nucypher.blockchain.eth.decorators import validate_checksum_address
diff --git a/nucypher/cli/characters/stake.py b/nucypher/cli/characters/stake.py
index d79cbe07f..41b7eb7eb 100644
--- a/nucypher/cli/characters/stake.py
+++ b/nucypher/cli/characters/stake.py
@@ -21,19 +21,14 @@ from web3 import Web3
from nucypher.characters.lawful import StakeHolder
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
-from nucypher.blockchain.eth.registry import BaseContractRegistry
from nucypher.blockchain.eth.token import NU
from nucypher.blockchain.eth.utils import datetime_at_period
-from nucypher.characters.banners import NU_BANNER
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.config import nucypher_click_config
from nucypher.cli.painting import paint_receipt_summary
from nucypher.cli.types import (
EIP55_CHECKSUM_ADDRESS,
- STAKE_VALUE,
- STAKE_DURATION,
- STAKE_EXTENSION,
EXISTING_READABLE_FILE
)
from nucypher.config.characters import StakeHolderConfiguration
@@ -142,6 +137,12 @@ def stake(click_config,
STAKEHOLDER = stakeholder_config.produce(initial_address=staking_address)
blockchain = BlockchainInterfaceFactory.get_interface(provider_uri=provider_uri) # Eager connection
+ economics = STAKEHOLDER.economics
+
+ min_locked = economics.minimum_allowed_locked
+ stake_value_range = click.FloatRange(min=NU.from_nunits(min_locked).to_tokens(), clamp=False)
+ stake_duration_range = click.IntRange(min=economics.minimum_locked_periods, clamp=False)
+ stake_extension_range = click.IntRange(min=1, max=economics.maximum_allowed_locked, clamp=False)
#
# Eager Actions
@@ -179,10 +180,10 @@ def stake(click_config,
# TODO: Double-check dates
current_period = STAKEHOLDER.staking_agent.get_current_period()
- bonded_date = datetime_at_period(period=current_period)
+ bonded_date = datetime_at_period(period=current_period, seconds_per_period=economics.seconds_per_period)
min_worker_periods = STAKEHOLDER.staking_agent.staking_parameters()[7]
release_period = current_period + min_worker_periods
- release_date = datetime_at_period(period=release_period)
+ release_date = datetime_at_period(period=release_period, seconds_per_period=economics.seconds_per_period)
emitter.echo(f"\nWorker {worker_address} successfully bonded to staker {staking_address}", color='green')
paint_receipt_summary(emitter=emitter,
@@ -217,7 +218,7 @@ def stake(click_config,
# TODO: Double-check dates
current_period = STAKEHOLDER.staking_agent.get_current_period()
- bonded_date = datetime_at_period(period=current_period)
+ bonded_date = datetime_at_period(period=current_period, seconds_per_period=economics.seconds_per_period)
emitter.echo(f"Successfully detached worker {worker_address} from staker {staking_address}", color='green')
paint_receipt_summary(emitter=emitter,
@@ -249,13 +250,14 @@ def stake(click_config,
#
if not value:
- min_locked = STAKEHOLDER.economics.minimum_allowed_locked
- value = click.prompt(f"Enter stake value in NU", type=STAKE_VALUE, default=NU.from_nunits(min_locked).to_tokens())
+ value = click.prompt(f"Enter stake value in NU",
+ type=stake_value_range,
+ default=NU.from_nunits(min_locked).to_tokens())
value = NU.from_tokens(value)
if not lock_periods:
prompt = f"Enter stake duration ({STAKEHOLDER.economics.minimum_locked_periods} periods minimum)"
- lock_periods = click.prompt(prompt, type=STAKE_DURATION)
+ lock_periods = click.prompt(prompt, type=stake_duration_range)
start_period = STAKEHOLDER.staking_agent.get_current_period()
end_period = start_period + lock_periods
@@ -302,12 +304,12 @@ def stake(click_config,
# Value
if not value:
value = click.prompt(f"Enter target value (must be less than or equal to {str(current_stake.value)})",
- type=STAKE_VALUE)
+ type=stake_value_range)
value = NU(value, 'NU')
# Duration
if not lock_periods:
- extension = click.prompt("Enter number of periods to extend", type=STAKE_EXTENSION)
+ extension = click.prompt("Enter number of periods to extend", type=stake_extension_range)
else:
extension = lock_periods
diff --git a/nucypher/cli/types.py b/nucypher/cli/types.py
index e95901c30..bf86e96fe 100644
--- a/nucypher/cli/types.py
+++ b/nucypher/cli/types.py
@@ -18,9 +18,9 @@ along with nucypher. If not, see .
from ipaddress import ip_address
import click
-from eth_utils import is_checksum_address, to_checksum_address
+from eth_utils import to_checksum_address
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import StandardTokenEconomics
from nucypher.blockchain.eth.token import NU
@@ -43,14 +43,8 @@ class IPv4Address(click.ParamType):
return value
-token_economics = TokenEconomics()
WEI = click.IntRange(min=1, clamp=False) # TODO: Better validation for ether and wei values?
-# Staking
-STAKE_DURATION = click.IntRange(min=token_economics.minimum_locked_periods, clamp=False)
-STAKE_EXTENSION = click.IntRange(min=1, max=token_economics.maximum_allowed_locked, clamp=False)
-STAKE_VALUE = click.FloatRange(min=NU.from_nunits(token_economics.minimum_allowed_locked).to_tokens(), clamp=False)
-
# Filesystem
EXISTING_WRITABLE_DIRECTORY = click.Path(exists=True, dir_okay=True, file_okay=False, writable=True)
EXISTING_READABLE_FILE = click.Path(exists=True, dir_okay=False, file_okay=True, readable=True)
diff --git a/nucypher/utilities/sandbox/blockchain.py b/nucypher/utilities/sandbox/blockchain.py
index 1a19d5503..1a980f0a6 100644
--- a/nucypher/utilities/sandbox/blockchain.py
+++ b/nucypher/utilities/sandbox/blockchain.py
@@ -24,7 +24,7 @@ from pytest_ethereum.deployer import Deployer
from twisted.logger import Logger
from web3 import Web3
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import TokenEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.actors import ContractAdministrator
from nucypher.blockchain.eth.agents import EthereumContractAgent
from nucypher.blockchain.eth.interfaces import BlockchainDeployerInterface, BlockchainInterfaceFactory
@@ -87,6 +87,8 @@ class TesterBlockchain(BlockchainDeployerInterface):
_FIRST_URSULA = _FIRST_STAKER + NUMBER_OF_STAKERS_IN_BLOCKCHAIN_TESTS
_ursulas_range = range(NUMBER_OF_URSULAS_IN_BLOCKCHAIN_TESTS)
+ _default_token_economics = StandardTokenEconomics()
+
def __init__(self,
test_accounts=None,
poa=True,
@@ -181,8 +183,8 @@ class TesterBlockchain(BlockchainDeployerInterface):
raise ValueError("Specify hours, seconds, or periods, not a combination")
if periods:
- duration = (TokenEconomics.hours_per_period * periods) * (60 * 60)
- base = TokenEconomics.hours_per_period * 60 * 60
+ duration = self._default_token_economics.seconds_per_period * periods
+ base = self._default_token_economics.seconds_per_period
elif hours:
duration = hours * (60*60)
base = 60 * 60
@@ -200,11 +202,13 @@ class TesterBlockchain(BlockchainDeployerInterface):
delta = maya.timedelta(seconds=end_timestamp-now)
self.log.info(f"Time traveled {delta} "
- f"| period {epoch_to_period(epoch=end_timestamp)} "
+ f"| period {epoch_to_period(epoch=end_timestamp, seconds_per_period=self._default_token_economics.seconds_per_period)} "
f"| epoch {end_timestamp}")
@classmethod
- def bootstrap_network(cls) -> 'TesterBlockchain':
+ def bootstrap_network(cls,
+ economics: TokenEconomics = None
+ ) -> Tuple['TesterBlockchain', 'InMemoryContractRegistry']:
"""For use with metric testing scripts"""
registry = InMemoryContractRegistry()
@@ -216,7 +220,9 @@ class TesterBlockchain(BlockchainDeployerInterface):
testerchain.transacting_power = power
origin = testerchain.client.etherbase
- deployer = ContractAdministrator(deployer_address=origin, registry=registry)
+ deployer = ContractAdministrator(deployer_address=origin,
+ registry=registry,
+ economics=economics or cls._default_token_economics)
secrets = dict()
for deployer_class in deployer.upgradeable_deployer_classes:
secrets[deployer_class.contract_name] = INSECURE_DEVELOPMENT_PASSWORD
diff --git a/tests/blockchain/eth/contracts/base/test_issuer.py b/tests/blockchain/eth/contracts/base/test_issuer.py
index 23baa9df1..bf433861b 100644
--- a/tests/blockchain/eth/contracts/base/test_issuer.py
+++ b/tests/blockchain/eth/contracts/base/test_issuer.py
@@ -20,32 +20,61 @@ import pytest
from eth_tester.exceptions import TransactionFailed
from web3.contract import Contract
+from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.eth.token import NU
+
SECRET_LENGTH = 32
+TOTAL_SUPPLY = 2 * 10 ** 40
@pytest.fixture()
def token(testerchain, deploy_contract):
# Create an ERC20 token
- token, _ = deploy_contract('NuCypherToken', 2 * 10 ** 40)
+ token, _ = deploy_contract('NuCypherToken', _totalSupply=TOTAL_SUPPLY)
return token
@pytest.mark.slow
def test_issuer(testerchain, token, deploy_contract):
+ economics = TokenEconomics(initial_supply=10 ** 30,
+ total_supply=TOTAL_SUPPLY,
+ staking_coefficient=10 ** 43,
+ locked_periods_coefficient=10 ** 4,
+ maximum_rewarded_periods=10 ** 4,
+ hours_per_period=1)
+
+ def calculate_reward(locked, total_locked, locked_periods):
+ return economics.erc20_reward_supply * locked * \
+ (locked_periods + economics.locked_periods_coefficient) // \
+ (total_locked * economics.staking_coefficient)
+
creator = testerchain.client.accounts[0]
ursula = testerchain.client.accounts[1]
# Only token contract is allowed in Issuer constructor
with pytest.raises((TransactionFailed, ValueError)):
- deploy_contract('IssuerMock', ursula, 1, 10 ** 43, 10 ** 4, 10 ** 4)
+ deploy_contract(
+ contract_name='IssuerMock',
+ _token=ursula,
+ _hoursPerPeriod=economics.hours_per_period,
+ _miningCoefficient=economics.staking_coefficient,
+ _lockedPeriodsCoefficient=economics.locked_periods_coefficient,
+ _rewardedPeriods=economics.maximum_rewarded_periods
+ )
# Creator deploys the issuer
- issuer, _ = deploy_contract('IssuerMock', token.address, 1, 10 ** 43, 10 ** 4, 10 ** 4)
+ issuer, _ = deploy_contract(
+ contract_name='IssuerMock',
+ _token=token.address,
+ _hoursPerPeriod=economics.hours_per_period,
+ _miningCoefficient=economics.staking_coefficient,
+ _lockedPeriodsCoefficient=economics.locked_periods_coefficient,
+ _rewardedPeriods=economics.maximum_rewarded_periods
+ )
events = issuer.events.Initialized.createFilter(fromBlock='latest')
# Give staker tokens for reward and initialize contract
- reserved_reward = 2 * 10 ** 40 - 10 ** 30
- tx = token.functions.transfer(issuer.address, reserved_reward).transact({'from': creator})
+ tx = token.functions.transfer(issuer.address, economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
# Only owner can initialize
@@ -57,7 +86,7 @@ def test_issuer(testerchain, token, deploy_contract):
events = events.get_all_entries()
assert 1 == len(events)
- assert reserved_reward == events[0]['args']['reservedReward']
+ assert economics.erc20_reward_supply == events[0]['args']['reservedReward']
balance = token.functions.balanceOf(issuer.address).call()
# Can't initialize second time
@@ -68,26 +97,30 @@ def test_issuer(testerchain, token, deploy_contract):
# Check result of minting tokens
tx = issuer.functions.testMint(0, 1000, 2000, 0).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
- assert 10 == token.functions.balanceOf(ursula).call()
- assert balance - 10 == token.functions.balanceOf(issuer.address).call()
+ reward = calculate_reward(1000, 2000, 0)
+ assert reward == token.functions.balanceOf(ursula).call()
+ assert balance - reward == token.functions.balanceOf(issuer.address).call()
# The result must be more because of a different proportion of lockedValue and totalLockedValue
tx = issuer.functions.testMint(0, 500, 500, 0).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
- assert 30 == token.functions.balanceOf(ursula).call()
- assert balance - 30 == token.functions.balanceOf(issuer.address).call()
+ reward += calculate_reward(500, 500, 0)
+ assert reward == token.functions.balanceOf(ursula).call()
+ assert balance - reward == token.functions.balanceOf(issuer.address).call()
# The result must be more because of bigger value of allLockedPeriods
tx = issuer.functions.testMint(0, 500, 500, 10 ** 4).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
- assert 70 == token.functions.balanceOf(ursula).call()
- assert balance - 70 == token.functions.balanceOf(issuer.address).call()
+ reward += calculate_reward(500, 500, 10 ** 4)
+ assert reward == token.functions.balanceOf(ursula).call()
+ assert balance - reward == token.functions.balanceOf(issuer.address).call()
# The result is the same because allLockedPeriods more then specified coefficient _rewardedPeriods
tx = issuer.functions.testMint(0, 500, 500, 2 * 10 ** 4).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
- assert 110 == token.functions.balanceOf(ursula).call()
- assert balance - 110 == token.functions.balanceOf(issuer.address).call()
+ reward += calculate_reward(500, 500, 10 ** 4)
+ assert reward == token.functions.balanceOf(ursula).call()
+ assert balance - reward == token.functions.balanceOf(issuer.address).call()
@pytest.mark.slow
@@ -97,14 +130,28 @@ def test_inflation_rate(testerchain, token, deploy_contract):
During one period inflation rate must be the same
"""
+ economics = TokenEconomics(initial_supply=10 ** 30,
+ total_supply=TOTAL_SUPPLY,
+ staking_coefficient=2 * 10 ** 19,
+ locked_periods_coefficient=1,
+ maximum_rewarded_periods=1,
+ hours_per_period=1)
+
creator = testerchain.client.accounts[0]
ursula = testerchain.client.accounts[1]
# Creator deploys the contract
- issuer, _ = deploy_contract('IssuerMock', token.address, 1, 2 * 10 ** 19, 1, 1)
+ issuer, _ = deploy_contract(
+ contract_name='IssuerMock',
+ _token=token.address,
+ _hoursPerPeriod=economics.hours_per_period,
+ _miningCoefficient=economics.staking_coefficient,
+ _lockedPeriodsCoefficient=economics.locked_periods_coefficient,
+ _rewardedPeriods=economics.maximum_rewarded_periods
+ )
# Give staker tokens for reward and initialize contract
- tx = token.functions.transfer(issuer.address, 2 * 10 ** 40 - 10 ** 30).transact({'from': creator})
+ tx = token.functions.transfer(issuer.address, economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = issuer.functions.initialize().transact({'from': creator})
testerchain.wait_for_receipt(tx)
@@ -166,11 +213,25 @@ def test_upgrading(testerchain, token, deploy_contract):
secret2_hash = testerchain.w3.keccak(secret2)
# Deploy contract
- contract_library_v1, _ = deploy_contract('Issuer', token.address, 1, 1, 1, 1)
+ contract_library_v1, _ = deploy_contract(
+ contract_name='Issuer',
+ _token=token.address,
+ _hoursPerPeriod=1,
+ _miningCoefficient=1,
+ _lockedPeriodsCoefficient=1,
+ _rewardedPeriods=1
+ )
dispatcher, _ = deploy_contract('Dispatcher', contract_library_v1.address, secret_hash)
# Deploy second version of the contract
- contract_library_v2, _ = deploy_contract('IssuerV2Mock', token.address, 2, 2, 2, 2)
+ contract_library_v2, _ = deploy_contract(
+ contract_name='IssuerV2Mock',
+ _token=token.address,
+ _hoursPerPeriod=2,
+ _miningCoefficient=2,
+ _lockedPeriodsCoefficient=2,
+ _rewardedPeriods=2
+ )
contract = testerchain.client.get_contract(
abi=contract_library_v2.abi,
address=dispatcher.address,
@@ -208,7 +269,14 @@ def test_upgrading(testerchain, token, deploy_contract):
assert 3 == contract.functions.valueToCheck().call()
# Can't upgrade to the previous version or to the bad version
- contract_library_bad, _ = deploy_contract('IssuerBad', token.address, 2, 2, 2, 2)
+ contract_library_bad, _ = deploy_contract(
+ contract_name='IssuerBad',
+ _token=token.address,
+ _hoursPerPeriod=2,
+ _miningCoefficient=2,
+ _lockedPeriodsCoefficient=2,
+ _rewardedPeriods=2
+ )
with pytest.raises((TransactionFailed, ValueError)):
tx = dispatcher.functions.upgrade(contract_library_v1.address, secret2, secret_hash)\
.transact({'from': creator})
diff --git a/tests/blockchain/eth/contracts/integration/test_contract_economics.py b/tests/blockchain/eth/contracts/integration/test_contract_economics.py
new file mode 100644
index 000000000..adcceae84
--- /dev/null
+++ b/tests/blockchain/eth/contracts/integration/test_contract_economics.py
@@ -0,0 +1,73 @@
+"""
+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 .
+"""
+
+import pytest
+from nucypher.crypto.powers import TransactingPower
+from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
+
+
+# Experimental max error
+MAX_ERROR = 0.0004751
+MAX_PERIODS = 100
+
+
+@pytest.mark.slow
+def test_reward(testerchain, agency, token_economics):
+ testerchain.time_travel(hours=1)
+ token_agent, staking_agent, _policy_agent = agency
+ origin = testerchain.etherbase_account
+ ursula = testerchain.ursula_account(0)
+
+ # Prepare one staker
+ _txhash = token_agent.transfer(amount=token_economics.minimum_allowed_locked,
+ target_address=ursula,
+ sender_address=origin)
+ testerchain.transacting_power = TransactingPower(password=INSECURE_DEVELOPMENT_PASSWORD,
+ account=ursula)
+ testerchain.transacting_power.activate()
+ _txhash = token_agent.approve_transfer(amount=token_economics.minimum_allowed_locked,
+ target_address=staking_agent.contract_address,
+ sender_address=ursula)
+ _txhash = staking_agent.deposit_tokens(amount=token_economics.minimum_allowed_locked,
+ lock_periods=100 * token_economics.maximum_rewarded_periods,
+ sender_address=ursula)
+ _txhash = staking_agent.set_worker(staker_address=ursula, worker_address=ursula)
+
+ # Get a reward for one period
+ _txhash = staking_agent.confirm_activity(worker_address=ursula)
+ testerchain.time_travel(periods=1)
+ _txhash = staking_agent.confirm_activity(worker_address=ursula)
+ assert staking_agent.calculate_staking_reward(staker_address=ursula) == 0
+ testerchain.time_travel(periods=1)
+ _txhash = staking_agent.confirm_activity(worker_address=ursula)
+
+ contract_reward = staking_agent.calculate_staking_reward(staker_address=ursula)
+ calculations_reward = token_economics.cumulative_rewards_at_period(1)
+ error = (contract_reward - calculations_reward) / calculations_reward
+ assert error > 0
+ assert error < MAX_ERROR
+
+ # Get a reward for other periods
+ for i in range(1, MAX_PERIODS):
+ testerchain.time_travel(periods=1)
+ _txhash = staking_agent.confirm_activity(worker_address=ursula)
+ contract_reward = staking_agent.calculate_staking_reward(staker_address=ursula)
+ calculations_reward = token_economics.cumulative_rewards_at_period(i + 1)
+ next_error = (contract_reward - calculations_reward) / calculations_reward
+ assert next_error > 0
+ assert next_error < error
+ error = next_error
diff --git a/tests/blockchain/eth/contracts/integration/test_intercontract_integration.py b/tests/blockchain/eth/contracts/integration/test_intercontract_integration.py
index 0c0240952..b1d9aff83 100644
--- a/tests/blockchain/eth/contracts/integration/test_intercontract_integration.py
+++ b/tests/blockchain/eth/contracts/integration/test_intercontract_integration.py
@@ -24,10 +24,10 @@ from eth_tester.exceptions import TransactionFailed
from eth_utils import to_canonical_address
from web3.contract import Contract
+from nucypher.blockchain.economics import TokenEconomics
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
-from nucypher.blockchain.eth.token import NU
from nucypher.crypto.api import sha256_digest
from nucypher.crypto.signing import SignatureStamp
@@ -44,26 +44,34 @@ adjudicator_secret = os.urandom(SECRET_LENGTH)
@pytest.fixture()
-def token(testerchain, deploy_contract):
+def token_economics():
+ economics = TokenEconomics(initial_supply=10 ** 9,
+ total_supply=2 * 10 ** 9,
+ staking_coefficient=8 * 10 ** 7,
+ locked_periods_coefficient=4,
+ maximum_rewarded_periods=4,
+ hours_per_period=1,
+ minimum_locked_periods=6,
+ minimum_allowed_locked=100,
+ maximum_allowed_locked=2000,
+ minimum_worker_periods=2,
+ base_penalty=300,
+ percentage_penalty_coefficient=2)
+ return economics
+
+
+@pytest.fixture()
+def token(token_economics, deploy_contract):
# Create an ERC20 token
- contract, _ = deploy_contract('NuCypherToken', _totalSupply=int(NU(2 * 10 ** 9, 'NuNit')))
+ contract, _ = deploy_contract('NuCypherToken', _totalSupply=token_economics.erc20_total_supply)
return contract
@pytest.fixture()
-def escrow(testerchain, token, deploy_contract):
+def escrow(testerchain, token, token_economics, deploy_contract):
# Creator deploys the escrow
contract, _ = deploy_contract(
- contract_name='StakingEscrow',
- _token=token.address,
- _hoursPerPeriod=1,
- _miningCoefficient=8*10**7,
- _lockedPeriodsCoefficient=4,
- _rewardedPeriods=4,
- _minLockedPeriods=6,
- _minAllowableLockedTokens=100,
- _maxAllowableLockedTokens=2000,
- _minWorkerPeriods=2
+ 'StakingEscrow', token.address, *token_economics.staking_deployment_parameters
)
secret_hash = testerchain.w3.keccak(escrow_secret)
@@ -101,22 +109,17 @@ def policy_manager(testerchain, escrow, deploy_contract):
@pytest.fixture()
-def adjudicator(testerchain, escrow, slashing_economics, deploy_contract):
+def adjudicator(testerchain, escrow, token_economics, deploy_contract):
escrow, _ = escrow
creator = testerchain.client.accounts[0]
secret_hash = testerchain.w3.keccak(adjudicator_secret)
- deployment_parameters = list(slashing_economics.deployment_parameters)
- # TODO: For some reason this test used non-standard slashing parameters (#354)
- deployment_parameters[1] = 300
- deployment_parameters[3] = 2
-
# Creator deploys the contract
contract, _ = deploy_contract(
'Adjudicator',
escrow.address,
- *deployment_parameters)
+ *token_economics.slashing_deployment_parameters)
dispatcher, _ = deploy_contract('Dispatcher', contract.address, secret_hash)
@@ -243,6 +246,7 @@ def execute_multisig_transaction(testerchain, multisig, accounts, tx):
@pytest.mark.slow
def test_all(testerchain,
+ token_economics,
token,
escrow,
policy_manager,
@@ -250,7 +254,6 @@ def test_all(testerchain,
worklock,
user_escrow_proxy,
multisig,
- slashing_economics,
mock_ursula_reencrypts,
deploy_contract):
@@ -304,8 +307,7 @@ def test_all(testerchain,
testerchain.wait_for_receipt(tx)
# Initialize escrow
- reward = 10 ** 9
- tx = token.functions.transfer(escrow.address, reward).transact({'from': creator})
+ tx = token.functions.transfer(escrow.address, token_economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.initialize().buildTransaction({'from': multisig.address, 'gasPrice': 0})
execute_multisig_transaction(testerchain, multisig, [contracts_owners[0], contracts_owners[1]], tx)
@@ -361,7 +363,7 @@ def test_all(testerchain,
tx = worklock.functions.claim().transact({'from': ursula2, 'gas_price': 0})
testerchain.wait_for_receipt(tx)
assert worklock.functions.getRemainingWork(ursula2).call() == deposit_rate * deposited_eth
- assert reward + 1000 == token.functions.balanceOf(escrow.address).call()
+ assert token_economics.erc20_reward_supply + 1000 == token.functions.balanceOf(escrow.address).call()
assert 1000 == escrow.functions.getAllTokens(ursula2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2).call()
assert 1000 == escrow.functions.getLockedTokens(ursula2, 1).call()
@@ -474,7 +476,7 @@ def test_all(testerchain,
testerchain.wait_for_receipt(tx)
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
- assert reward + 2000 == token.functions.balanceOf(escrow.address).call()
+ assert token_economics.erc20_reward_supply + 2000 == token.functions.balanceOf(escrow.address).call()
assert 9000 == token.functions.balanceOf(ursula1).call()
assert 0 == escrow.functions.getLockedTokens(ursula1).call()
assert 1000 == escrow.functions.getLockedTokens(ursula1, 1).call()
@@ -494,7 +496,7 @@ def test_all(testerchain,
assert 1000 == escrow.functions.getLockedTokens(user_escrow_1.address, 1).call()
assert 1000 == escrow.functions.getLockedTokens(user_escrow_1.address, 10).call()
assert 0 == escrow.functions.getLockedTokens(user_escrow_1.address, 11).call()
- assert reward + 3000 == token.functions.balanceOf(escrow.address).call()
+ assert token_economics.erc20_reward_supply + 3000 == token.functions.balanceOf(escrow.address).call()
assert 9000 == token.functions.balanceOf(user_escrow_1.address).call()
# Only user can deposit tokens to the staking escrow
@@ -680,16 +682,7 @@ def test_all(testerchain,
policy_manager_v1 = policy_manager.functions.target().call()
# Creator deploys the contracts as the second versions
escrow_v2, _ = deploy_contract(
- contract_name='StakingEscrow',
- _token=token.address,
- _hoursPerPeriod=1,
- _miningCoefficient=8 * 10 ** 7,
- _lockedPeriodsCoefficient=4,
- _rewardedPeriods=4,
- _minLockedPeriods=6,
- _minAllowableLockedTokens=100,
- _maxAllowableLockedTokens=2000,
- _minWorkerPeriods=2
+ 'StakingEscrow', token.address, *token_economics.staking_deployment_parameters
)
policy_manager_v2, _ = deploy_contract('PolicyManager', escrow.address)
# Ursula and Alice can't upgrade contracts, only owner can
@@ -839,12 +832,7 @@ def test_all(testerchain,
total_lock = escrow.functions.lockedPerPeriod(current_period).call()
alice1_balance = token.functions.balanceOf(alice1).call()
- deployment_parameters = list(slashing_economics.deployment_parameters)
- # TODO: For some reason this test used non-stadard slashing parameters (#354)
- deployment_parameters[1] = 300
- deployment_parameters[3] = 2
-
- algorithm_sha256, base_penalty, *coefficients = deployment_parameters
+ algorithm_sha256, base_penalty, *coefficients = token_economics.slashing_deployment_parameters
penalty_history_coefficient, percentage_penalty_coefficient, reward_coefficient = coefficients
data_hash, slashing_args = generate_args_for_slashing(mock_ursula_reencrypts, ursula1_with_stamp)
@@ -912,7 +900,7 @@ def test_all(testerchain,
adjudicator_v2, _ = deploy_contract(
'Adjudicator',
escrow.address,
- *slashing_economics.deployment_parameters)
+ *token_economics.slashing_deployment_parameters)
adjudicator_secret2 = os.urandom(SECRET_LENGTH)
adjudicator_secret2_hash = testerchain.w3.keccak(adjudicator_secret2)
# Ursula and Alice can't upgrade library, only owner can
diff --git a/tests/blockchain/eth/contracts/main/adjudicator/conftest.py b/tests/blockchain/eth/contracts/main/adjudicator/conftest.py
index 8f335841e..ac1b7e072 100644
--- a/tests/blockchain/eth/contracts/main/adjudicator/conftest.py
+++ b/tests/blockchain/eth/contracts/main/adjudicator/conftest.py
@@ -36,11 +36,11 @@ def escrow(testerchain, deploy_contract):
@pytest.fixture(params=[False, True])
-def adjudicator(testerchain, escrow, request, slashing_economics, deploy_contract):
+def adjudicator(testerchain, escrow, request, token_economics, deploy_contract):
contract, _ = deploy_contract(
'Adjudicator',
escrow.address,
- *slashing_economics.deployment_parameters)
+ *token_economics.slashing_deployment_parameters)
if request.param:
secret = os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH)
diff --git a/tests/blockchain/eth/contracts/main/adjudicator/test_adjudicator.py b/tests/blockchain/eth/contracts/main/adjudicator/test_adjudicator.py
index 2d98d7052..b036323eb 100644
--- a/tests/blockchain/eth/contracts/main/adjudicator/test_adjudicator.py
+++ b/tests/blockchain/eth/contracts/main/adjudicator/test_adjudicator.py
@@ -40,7 +40,7 @@ secret2 = (654321).to_bytes(32, byteorder='big')
def test_evaluate_cfrag(testerchain,
escrow,
adjudicator,
- slashing_economics,
+ token_economics,
blockchain_ursulas,
mock_ursula_reencrypts
):
@@ -56,10 +56,10 @@ def test_evaluate_cfrag(testerchain,
number_of_evaluations = 0
def compute_penalty_and_reward(stake: int, penalty_history: int) -> Tuple[int, int]:
- penalty_ = slashing_economics.base_penalty
- penalty_ += slashing_economics.penalty_history_coefficient * penalty_history
- penalty_ = min(penalty_, stake // slashing_economics.percentage_penalty_coefficient)
- reward_ = penalty_ // slashing_economics.reward_coefficient
+ penalty_ = token_economics.base_penalty
+ penalty_ += token_economics.penalty_history_coefficient * penalty_history
+ penalty_ = min(penalty_, stake // token_economics.percentage_penalty_coefficient)
+ reward_ = penalty_ // token_economics.reward_coefficient
return penalty_, reward_
# Prepare one staker
@@ -206,7 +206,7 @@ def test_evaluate_cfrag(testerchain,
previous_penalty = penalty
penalty, reward = compute_penalty_and_reward(worker_stake, worker_penalty_history)
# Penalty was increased because it's the second violation
- assert penalty == previous_penalty + slashing_economics.penalty_history_coefficient
+ assert penalty == previous_penalty + token_economics.penalty_history_coefficient
worker_stake -= penalty
investigator_balance += reward
worker_penalty_history += 1
@@ -252,7 +252,7 @@ def test_evaluate_cfrag(testerchain,
penalty, reward = compute_penalty_and_reward(worker_stake, worker_penalty_history)
# Penalty has reached maximum available percentage of value
- assert penalty == worker_stake // slashing_economics.percentage_penalty_coefficient
+ assert penalty == worker_stake // token_economics.percentage_penalty_coefficient
worker_stake -= penalty
investigator_balance += reward
worker_penalty_history += 1
diff --git a/tests/blockchain/eth/contracts/main/staking_escrow/conftest.py b/tests/blockchain/eth/contracts/main/staking_escrow/conftest.py
index b2348c430..27c646489 100644
--- a/tests/blockchain/eth/contracts/main/staking_escrow/conftest.py
+++ b/tests/blockchain/eth/contracts/main/staking_escrow/conftest.py
@@ -20,6 +20,7 @@ import pytest
from web3.contract import Contract
from eth_utils import keccak
+from nucypher.blockchain.economics import TokenEconomics
from nucypher.blockchain.eth.token import NU
VALUE_FIELD = 0
@@ -28,29 +29,33 @@ secret = (123456).to_bytes(32, byteorder='big')
@pytest.fixture()
-def token(testerchain, deploy_contract):
+def token_economics():
+ economics = TokenEconomics(initial_supply=10 ** 9,
+ total_supply=2 * 10 ** 9,
+ staking_coefficient=8 * 10 ** 7,
+ locked_periods_coefficient=4,
+ maximum_rewarded_periods=4,
+ hours_per_period=1,
+ minimum_locked_periods=2,
+ minimum_allowed_locked=100,
+ minimum_worker_periods=1)
+ return economics
+
+
+@pytest.fixture()
+def token(deploy_contract, token_economics):
# Create an ERC20 token
- token, _ = deploy_contract('NuCypherToken', _totalSupply=int(NU(2 * 10 ** 9, 'NuNit')))
+ token, _ = deploy_contract('NuCypherToken', _totalSupply=token_economics.erc20_total_supply)
return token
@pytest.fixture(params=[False, True])
-def escrow_contract(testerchain, token, request, deploy_contract):
+def escrow_contract(testerchain, token, token_economics, request, deploy_contract):
def make_escrow(max_allowed_locked_tokens):
# Creator deploys the escrow
- _staking_coefficient = 2 * 10 ** 7
- contract, _ = deploy_contract(
- contract_name='StakingEscrow',
- _token=token.address,
- _hoursPerPeriod=1,
- _miningCoefficient=4 * _staking_coefficient,
- _lockedPeriodsCoefficient=4,
- _rewardedPeriods=4,
- _minLockedPeriods=2,
- _minAllowableLockedTokens=100,
- _maxAllowableLockedTokens=max_allowed_locked_tokens,
- _minWorkerPeriods=1
- )
+ deploy_parameters = list(token_economics.staking_deployment_parameters)
+ deploy_parameters[-2] = max_allowed_locked_tokens
+ contract, _ = deploy_contract('StakingEscrow', token.address, *deploy_parameters)
if request.param:
secret_hash = keccak(secret)
diff --git a/tests/blockchain/eth/contracts/main/staking_escrow/test_staking.py b/tests/blockchain/eth/contracts/main/staking_escrow/test_staking.py
index 3f8c21972..53f3c7323 100644
--- a/tests/blockchain/eth/contracts/main/staking_escrow/test_staking.py
+++ b/tests/blockchain/eth/contracts/main/staking_escrow/test_staking.py
@@ -22,7 +22,8 @@ from web3.contract import Contract
@pytest.mark.slow
-def test_mining(testerchain, token, escrow_contract):
+def test_mining(testerchain, token, escrow_contract, token_economics):
+
escrow = escrow_contract(1500)
policy_manager_interface = testerchain.get_contract_factory('PolicyManagerForStakingEscrowMock')
policy_manager = testerchain.client.get_contract(
@@ -33,6 +34,13 @@ def test_mining(testerchain, token, escrow_contract):
ursula1 = testerchain.client.accounts[1]
ursula2 = testerchain.client.accounts[2]
+ current_supply = token_economics.erc20_initial_supply
+
+ def calculate_reward(locked, total_locked, locked_periods):
+ return (token_economics.erc20_total_supply - current_supply) * locked * \
+ (locked_periods + token_economics.locked_periods_coefficient) // \
+ (total_locked * token_economics.staking_coefficient)
+
staking_log = escrow.events.Mined.createFilter(fromBlock='latest')
deposit_log = escrow.events.Deposited.createFilter(fromBlock='latest')
lock_log = escrow.events.Locked.createFilter(fromBlock='latest')
@@ -41,7 +49,7 @@ def test_mining(testerchain, token, escrow_contract):
withdraw_log = escrow.events.Withdrawn.createFilter(fromBlock='latest')
# Give Escrow tokens for reward and initialize contract
- tx = token.functions.transfer(escrow.address, 10 ** 9).transact({'from': creator})
+ tx = token.functions.transfer(escrow.address, token_economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.initialize().transact({'from': creator})
testerchain.wait_for_receipt(tx)
@@ -49,7 +57,7 @@ def test_mining(testerchain, token, escrow_contract):
# Give Ursula and Ursula(2) some coins
tx = token.functions.transfer(ursula1, 10000).transact({'from': creator})
testerchain.wait_for_receipt(tx)
- tx = token.functions.transfer(ursula2, 10000).transact({'from': creator})
+ tx = token.functions.transfer(ursula2, 850).transact({'from': creator})
testerchain.wait_for_receipt(tx)
# Ursula can't confirm and mint because no locked tokens
@@ -67,13 +75,15 @@ def test_mining(testerchain, token, escrow_contract):
testerchain.wait_for_receipt(tx)
# Ursula and Ursula(2) transfer some tokens to the escrow and lock them
- tx = escrow.functions.deposit(1000, 2).transact({'from': ursula1})
+ ursula1_stake = 1000
+ ursula2_stake = 500
+ tx = escrow.functions.deposit(ursula1_stake, 2).transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setWorker(ursula1).transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
- tx = escrow.functions.deposit(500, 2).transact({'from': ursula2})
+ tx = escrow.functions.deposit(ursula2_stake, 2).transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setWorker(ursula2).transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
@@ -129,8 +139,11 @@ def test_mining(testerchain, token, escrow_contract):
current_period = escrow.functions.getCurrentPeriod().call()
# Check result of mining
- assert 1046 == escrow.functions.getAllTokens(ursula1).call()
- assert 525 == escrow.functions.getAllTokens(ursula2).call()
+ total_locked = ursula1_stake + ursula2_stake
+ ursula1_reward = calculate_reward(500, total_locked, 1) + calculate_reward(500, total_locked, 2)
+ assert ursula1_stake + ursula1_reward == escrow.functions.getAllTokens(ursula1).call()
+ ursula2_reward = calculate_reward(500, total_locked, 2)
+ assert ursula2_stake + ursula2_reward == escrow.functions.getAllTokens(ursula2).call()
# Check that downtime value has not changed
assert 1 == escrow.functions.getPastDowntimeLength(ursula1).call()
assert 1 == escrow.functions.getPastDowntimeLength(ursula2).call()
@@ -141,11 +154,11 @@ def test_mining(testerchain, token, escrow_contract):
assert 2 == len(events)
event_args = events[0]['args']
assert ursula1 == event_args['staker']
- assert 46 == event_args['value']
+ assert ursula1_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
event_args = events[1]['args']
assert ursula2 == event_args['staker']
- assert 25 == event_args['value']
+ assert ursula2_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
# Check parameters in call of the policy manager mock
@@ -157,14 +170,17 @@ def test_mining(testerchain, token, escrow_contract):
# Ursula tries to mint again and doesn't receive a reward
# There are no more confirmed periods that are ready to mint
+ ursula1_stake += ursula1_reward
+ ursula2_stake += ursula2_reward
tx = escrow.functions.mint().transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
- assert 1046 == escrow.functions.getAllTokens(ursula1).call()
+ assert ursula1_stake == escrow.functions.getAllTokens(ursula1).call()
events = staking_log.get_all_entries()
assert 2 == len(events)
# Ursula can't confirm next period because stake is unlocked in current period
testerchain.time_travel(hours=1)
+ current_supply += ursula1_reward + ursula2_reward
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
testerchain.wait_for_receipt(tx)
@@ -187,14 +203,16 @@ def test_mining(testerchain, token, escrow_contract):
# But Ursula(2) can't get reward because she did not confirm activity
tx = escrow.functions.mint().transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
- assert 1152 == escrow.functions.getAllTokens(ursula1).call()
- assert 525 == escrow.functions.getAllTokens(ursula2).call()
+ ursula1_reward = calculate_reward(500, 1000, 0) + calculate_reward(500, 1000, 1) + calculate_reward(500, 500, 0)
+ assert ursula1_stake + ursula1_reward == escrow.functions.getAllTokens(ursula1).call()
+ assert ursula2_stake == escrow.functions.getAllTokens(ursula2).call()
+ ursula1_stake += ursula1_reward
events = staking_log.get_all_entries()
assert 3 == len(events)
event_args = events[2]['args']
assert ursula1 == event_args['staker']
- assert 106 == event_args['value']
+ assert ursula1_reward == event_args['value']
assert current_period == event_args['period']
assert 4 == policy_manager.functions.getPeriodsLength(ursula1).call()
@@ -204,16 +222,19 @@ def test_mining(testerchain, token, escrow_contract):
# Ursula(2) mints tokens
testerchain.time_travel(hours=1)
+ current_supply += ursula1_reward
tx = escrow.functions.mint().transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
- assert 1152 == escrow.functions.getAllTokens(ursula1).call()
- assert 575 == escrow.functions.getAllTokens(ursula2).call()
+ ursula2_reward = calculate_reward(500, 500, 0)
+ assert ursula1_stake == escrow.functions.getAllTokens(ursula1).call()
+ assert ursula2_stake + ursula2_reward == escrow.functions.getAllTokens(ursula2).call()
+ ursula2_stake += ursula2_reward
events = staking_log.get_all_entries()
assert 4 == len(events)
event_args = events[3]['args']
assert ursula2 == event_args['staker']
- assert 50 == event_args['value']
+ assert ursula2_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
current_period = escrow.functions.getCurrentPeriod().call() - 1
@@ -231,7 +252,7 @@ def test_mining(testerchain, token, escrow_contract):
testerchain.wait_for_receipt(tx)
current_period = escrow.functions.getCurrentPeriod().call()
assert current_period - 2 == escrow.functions.getLastActivePeriod(ursula1).call()
- assert 1152 == escrow.functions.getAllTokens(ursula1).call()
+ assert ursula1_stake == escrow.functions.getAllTokens(ursula1).call()
# Ursula still can't confirm activity
with pytest.raises((TransactionFailed, ValueError)):
tx = escrow.functions.confirmActivity().transact({'from': ursula1})
@@ -244,6 +265,7 @@ def test_mining(testerchain, token, escrow_contract):
testerchain.wait_for_receipt(tx)
tx = escrow.functions.lock(500, 2).transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
+ ursula2_stake += 250
assert 3 == escrow.functions.getPastDowntimeLength(ursula2).call()
downtime = escrow.functions.getPastDowntime(ursula2, 2).call()
@@ -252,20 +274,24 @@ def test_mining(testerchain, token, escrow_contract):
# Ursula(2) mints only one period (by using deposit/approveAndCall function)
testerchain.time_travel(hours=5)
+ current_supply += ursula2_reward
current_period = escrow.functions.getCurrentPeriod().call()
assert current_period - 4 == escrow.functions.getLastActivePeriod(ursula2).call()
tx = token.functions.approveAndCall(escrow.address, 100, testerchain.w3.toBytes(2))\
.transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
+ ursula2_stake += 100
tx = escrow.functions.confirmActivity().transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
- assert 1152 == escrow.functions.getAllTokens(ursula1).call()
- assert 1025 == escrow.functions.getAllTokens(ursula2).call()
+ ursula2_reward = calculate_reward(250, 750, 4) + calculate_reward(500, 750, 4)
+ assert ursula1_stake == escrow.functions.getAllTokens(ursula1).call()
+ assert ursula2_stake + ursula2_reward == escrow.functions.getAllTokens(ursula2).call()
assert 4 == escrow.functions.getPastDowntimeLength(ursula2).call()
downtime = escrow.functions.getPastDowntime(ursula2, 3).call()
assert current_period - 3 == downtime[0]
assert current_period == downtime[1]
+ ursula2_stake += ursula2_reward
assert 4 == policy_manager.functions.getPeriodsLength(ursula2).call()
assert current_period - 4 == policy_manager.functions.getPeriod(ursula2, 3).call()
@@ -274,7 +300,7 @@ def test_mining(testerchain, token, escrow_contract):
assert 5 == len(events)
event_args = events[4]['args']
assert ursula2 == event_args['staker']
- assert 100 == event_args['value']
+ assert ursula2_reward == event_args['value']
assert escrow.functions.getCurrentPeriod().call() - 1 == event_args['period']
# Ursula(2) confirms activity for remaining periods
@@ -288,17 +314,18 @@ def test_mining(testerchain, token, escrow_contract):
# Ursula(2) withdraws all
testerchain.time_travel(hours=2)
+ ursula2_stake = escrow.functions.getAllTokens(ursula2).call()
assert 0 == escrow.functions.getLockedTokens(ursula2).call()
- tx = escrow.functions.withdraw(1083).transact({'from': ursula2})
+ tx = escrow.functions.withdraw(ursula2_stake).transact({'from': ursula2})
testerchain.wait_for_receipt(tx)
assert 0 == escrow.functions.getAllTokens(ursula2).call()
- assert 10233 == token.functions.balanceOf(ursula2).call()
+ assert ursula2_stake == token.functions.balanceOf(ursula2).call()
events = withdraw_log.get_all_entries()
assert 1 == len(events)
event_args = events[0]['args']
assert ursula2 == event_args['staker']
- assert 1083 == event_args['value']
+ assert ursula2_stake == event_args['value']
assert 4 == len(deposit_log.get_all_entries())
assert 6 == len(lock_log.get_all_entries())
@@ -307,7 +334,7 @@ def test_mining(testerchain, token, escrow_contract):
@pytest.mark.slow
-def test_slashing(testerchain, token, escrow_contract, deploy_contract):
+def test_slashing(testerchain, token, escrow_contract, token_economics, deploy_contract):
escrow = escrow_contract(1500)
adjudicator, _ = deploy_contract(
'AdjudicatorForStakingEscrowMock', escrow.address
@@ -321,7 +348,7 @@ def test_slashing(testerchain, token, escrow_contract, deploy_contract):
slashing_log = escrow.events.Slashed.createFilter(fromBlock='latest')
# Give Escrow tokens for reward and initialize contract
- tx = token.functions.transfer(escrow.address, 10 ** 9).transact({'from': creator})
+ tx = token.functions.transfer(escrow.address, token_economics.erc20_reward_supply).transact({'from': creator})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.initialize().transact({'from': creator})
testerchain.wait_for_receipt(tx)
diff --git a/tests/blockchain/eth/contracts/main/staking_escrow/test_staking_escrow_additional.py b/tests/blockchain/eth/contracts/main/staking_escrow/test_staking_escrow_additional.py
index 50e0cdac5..7b9baaa6d 100644
--- a/tests/blockchain/eth/contracts/main/staking_escrow/test_staking_escrow_additional.py
+++ b/tests/blockchain/eth/contracts/main/staking_escrow/test_staking_escrow_additional.py
@@ -32,7 +32,7 @@ secret2 = (654321).to_bytes(32, byteorder='big')
@pytest.mark.slow
-def test_upgrading(testerchain, token, deploy_contract):
+def test_upgrading(testerchain, token, token_economics, deploy_contract):
creator = testerchain.client.accounts[0]
staker = testerchain.client.accounts[1]
@@ -41,16 +41,7 @@ def test_upgrading(testerchain, token, deploy_contract):
# Deploy contract
contract_library_v1, _ = deploy_contract(
- contract_name='StakingEscrow',
- _token=token.address,
- _hoursPerPeriod=1,
- _miningCoefficient=8*10**7,
- _lockedPeriodsCoefficient=4,
- _rewardedPeriods=4,
- _minLockedPeriods=2,
- _minAllowableLockedTokens=100,
- _maxAllowableLockedTokens=1500,
- _minWorkerPeriods=1
+ 'StakingEscrow', token.address, *token_economics.staking_deployment_parameters
)
dispatcher, _ = deploy_contract('Dispatcher', contract_library_v1.address, secret_hash)
@@ -73,7 +64,7 @@ def test_upgrading(testerchain, token, deploy_contract):
abi=contract_library_v2.abi,
address=dispatcher.address,
ContractFactoryClass=Contract)
- assert 1500 == contract.functions.maxAllowableLockedTokens().call()
+ assert token_economics.maximum_allowed_locked == contract.functions.maxAllowableLockedTokens().call()
# Can't call `finishUpgrade` and `verifyState` methods outside upgrade lifecycle
with pytest.raises((TransactionFailed, ValueError)):
@@ -116,7 +107,7 @@ def test_upgrading(testerchain, token, deploy_contract):
testerchain.wait_for_receipt(tx)
# Check constructor and storage values
assert contract_library_v2.address == dispatcher.functions.target().call()
- assert 1500 == contract.functions.maxAllowableLockedTokens().call()
+ assert token_economics.maximum_allowed_locked == contract.functions.maxAllowableLockedTokens().call()
assert policy_manager.address == contract.functions.policyManager().call()
assert 2 == contract.functions.valueToCheck().call()
# Check new ABI
@@ -248,7 +239,7 @@ def test_re_stake(testerchain, token, escrow_contract):
testerchain.wait_for_receipt(tx)
tx = token.functions.approve(escrow.address, 10000).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
- sub_stake = 1000
+ sub_stake = 100
tx = escrow.functions.deposit(sub_stake, 10).transact({'from': ursula})
testerchain.wait_for_receipt(tx)
tx = escrow.functions.setWorker(ursula).transact({'from': ursula})
@@ -398,8 +389,8 @@ def test_re_stake(testerchain, token, escrow_contract):
# To calculate amount of re-stake we can split Ursula1's reward according sub stakes ratio:
# first sub stake is 2/3 of entire stake and second sub stake is 1/3
- re_stake_for_first_sub_stake = ursula_reward * 2 // 3
- re_stake_for_second_sub_stake = ursula_reward - re_stake_for_first_sub_stake
+ re_stake_for_second_sub_stake = ursula_reward // 3
+ re_stake_for_first_sub_stake = ursula_reward - re_stake_for_second_sub_stake
# Check re-stake for Ursula1's sub stakes
assert stake + ursula_reward == escrow.functions.getLockedTokens(ursula).call()
assert sub_stake_1 + re_stake_for_first_sub_stake == escrow.functions.getSubStakeInfo(ursula, 0).call()[3]
diff --git a/tests/blockchain/eth/entities/actors/test_deployer.py b/tests/blockchain/eth/entities/actors/test_deployer.py
index 12dbfde35..65af33ef2 100644
--- a/tests/blockchain/eth/entities/actors/test_deployer.py
+++ b/tests/blockchain/eth/entities/actors/test_deployer.py
@@ -83,7 +83,7 @@ def test_rapid_deployment(token_economics, test_registry):
beneficiary_address = acct.address
amount = random.randint(token_economics.minimum_allowed_locked, token_economics.maximum_allowed_locked)
duration = random.randint(token_economics.minimum_locked_periods*ONE_YEAR_IN_SECONDS,
- (token_economics.maximum_locked_periods*ONE_YEAR_IN_SECONDS)*3)
+ (token_economics.maximum_rewarded_periods*ONE_YEAR_IN_SECONDS)*3)
random_allocation = {'beneficiary_address': beneficiary_address, 'amount': amount, 'duration_seconds': duration}
allocation_data.append(random_allocation)
diff --git a/tests/blockchain/eth/entities/actors/test_investigator.py b/tests/blockchain/eth/entities/actors/test_investigator.py
index b8a5dc11a..55565fd62 100644
--- a/tests/blockchain/eth/entities/actors/test_investigator.py
+++ b/tests/blockchain/eth/entities/actors/test_investigator.py
@@ -45,8 +45,7 @@ def test_investigator_requests_slashing(testerchain,
test_registry,
session_agency,
mock_ursula_reencrypts,
- token_economics,
- slashing_economics):
+ token_economics):
testerchain = testerchain
staker_account = testerchain.staker_account(0)
@@ -109,5 +108,5 @@ def test_investigator_requests_slashing(testerchain,
investigator_reward = investigator.token_balance - bobby_old_balance
assert investigator_reward > 0
- assert investigator_reward == slashing_economics.base_penalty / slashing_economics.reward_coefficient
+ assert investigator_reward == token_economics.base_penalty / token_economics.reward_coefficient
assert staker.locked_tokens(periods=1) < locked_tokens
diff --git a/tests/blockchain/eth/entities/actors/test_staker.py b/tests/blockchain/eth/entities/actors/test_staker.py
index 3a63c8c27..cd606b351 100644
--- a/tests/blockchain/eth/entities/actors/test_staker.py
+++ b/tests/blockchain/eth/entities/actors/test_staker.py
@@ -81,7 +81,8 @@ def test_staker_divides_stake(staker, token_economics):
value=yet_another_stake_value,
checksum_address=staker.checksum_address,
index=3,
- staking_agent=staker.staking_agent)
+ staking_agent=staker.staking_agent,
+ economics=token_economics)
assert 4 == len(staker.stakes), 'A new stake was not added after two stake divisions'
assert expected_old_stake == staker.stakes[stake_index + 1].to_stake_info(), 'Old stake values are invalid after two stake divisions'
diff --git a/tests/blockchain/eth/entities/agents/test_adjudicator_agent.py b/tests/blockchain/eth/entities/agents/test_adjudicator_agent.py
index 52d5e8ed5..a60028904 100644
--- a/tests/blockchain/eth/entities/agents/test_adjudicator_agent.py
+++ b/tests/blockchain/eth/entities/agents/test_adjudicator_agent.py
@@ -47,7 +47,6 @@ def test_adjudicator_slashes(agency,
testerchain,
mock_ursula_reencrypts,
token_economics,
- slashing_economics,
test_registry):
staker_account = testerchain.staker_account(0)
@@ -113,5 +112,5 @@ def test_adjudicator_slashes(agency,
investigator_reward = bobby.token_balance - bobby_old_balance
assert investigator_reward > 0
- assert investigator_reward == slashing_economics.base_penalty / slashing_economics.reward_coefficient
+ assert investigator_reward == token_economics.base_penalty / token_economics.reward_coefficient
assert staker.locked_tokens(periods=1) < locked_tokens
diff --git a/tests/blockchain/eth/entities/deployers/test_adjudicator_deployer.py b/tests/blockchain/eth/entities/deployers/test_adjudicator_deployer.py
index 897ac8744..2b5b35416 100644
--- a/tests/blockchain/eth/entities/deployers/test_adjudicator_deployer.py
+++ b/tests/blockchain/eth/entities/deployers/test_adjudicator_deployer.py
@@ -30,7 +30,7 @@ from nucypher.blockchain.eth.deployers import (
@pytest.mark.slow()
def test_adjudicator_deployer(testerchain,
- slashing_economics,
+ token_economics,
deployment_progress,
test_registry):
testerchain = testerchain
@@ -61,11 +61,11 @@ def test_adjudicator_deployer(testerchain,
# Check default Adjudicator deployment parameters
assert staking_escrow_deployer.deployer_address != staking_agent.contract_address
assert adjudicator_agent.staking_escrow_contract == staking_agent.contract_address
- assert adjudicator_agent.hash_algorithm == slashing_economics.hash_algorithm
- assert adjudicator_agent.base_penalty == slashing_economics.base_penalty
- assert adjudicator_agent.penalty_history_coefficient == slashing_economics.penalty_history_coefficient
- assert adjudicator_agent.percentage_penalty_coefficient == slashing_economics.percentage_penalty_coefficient
- assert adjudicator_agent.reward_coefficient == slashing_economics.reward_coefficient
+ assert adjudicator_agent.hash_algorithm == token_economics.hash_algorithm
+ assert adjudicator_agent.base_penalty == token_economics.base_penalty
+ assert adjudicator_agent.penalty_history_coefficient == token_economics.penalty_history_coefficient
+ assert adjudicator_agent.percentage_penalty_coefficient == token_economics.percentage_penalty_coefficient
+ assert adjudicator_agent.reward_coefficient == token_economics.reward_coefficient
# Retrieve the AdjudicatorAgent singleton
some_policy_agent = AdjudicatorAgent(registry=test_registry)
diff --git a/tests/blockchain/eth/entities/deployers/test_deploy_preallocations.py b/tests/blockchain/eth/entities/deployers/test_deploy_preallocations.py
index 07ad9c2bd..0fc913a02 100644
--- a/tests/blockchain/eth/entities/deployers/test_deploy_preallocations.py
+++ b/tests/blockchain/eth/entities/deployers/test_deploy_preallocations.py
@@ -74,7 +74,7 @@ def test_deploy_and_allocate(session_agency, user_escrow_proxy, token_economics,
for address, deployer in deployments.items():
assert deployer.deployer_address == origin
- deposit_receipt = deployer.initial_deposit(value=allocation, duration_seconds=token_economics.maximum_locked_periods)
+ deposit_receipt = deployer.initial_deposit(value=allocation, duration_seconds=token_economics.maximum_rewarded_periods)
deposit_receipts.append(deposit_receipt)
beneficiary = random.choice(testerchain.unassigned_accounts)
diff --git a/tests/blockchain/eth/entities/deployers/test_economics.py b/tests/blockchain/eth/entities/deployers/test_economics.py
index 8a71789ca..8e0a07b29 100644
--- a/tests/blockchain/eth/entities/deployers/test_economics.py
+++ b/tests/blockchain/eth/entities/deployers/test_economics.py
@@ -19,7 +19,7 @@ along with nucypher. If not, see .
from decimal import Decimal, localcontext
from math import log
-from nucypher.blockchain.economics import TokenEconomics, LOG2
+from nucypher.blockchain.economics import LOG2, StandardTokenEconomics
def test_rough_economics():
@@ -37,11 +37,11 @@ def test_rough_economics():
where allLockedPeriods == min(T, T1)
"""
- e = TokenEconomics(initial_supply=int(1e9),
- initial_inflation=1,
- halving_delay=2,
- reward_saturation=1,
- small_stake_multiplier=Decimal(0.5))
+ e = StandardTokenEconomics(initial_supply=int(1e9),
+ initial_inflation=1,
+ halving_delay=2,
+ reward_saturation=1,
+ small_stake_multiplier=Decimal(0.5))
assert float(round(e.erc20_total_supply / Decimal(1e9), 2)) == 3.89 # As per economics paper
@@ -112,7 +112,7 @@ def test_exact_economics():
# Use same precision as economics class
with localcontext() as ctx:
- ctx.prec = TokenEconomics._precision
+ ctx.prec = StandardTokenEconomics._precision
# Sanity check expected testing outputs
assert Decimal(expected_total_supply) / expected_initial_supply == expected_supply_ratio
@@ -134,10 +134,10 @@ def test_exact_economics():
#
# Check creation
- e = TokenEconomics()
+ e = StandardTokenEconomics()
with localcontext() as ctx:
- ctx.prec = TokenEconomics._precision
+ ctx.prec = StandardTokenEconomics._precision
# Check that total_supply calculated correctly
assert Decimal(e.erc20_total_supply) / e.initial_supply == expected_supply_ratio
@@ -159,14 +159,35 @@ def test_exact_economics():
assert e.erc20_initial_supply == expected_initial_supply
assert e.erc20_reward_supply == expected_reward_supply
+ # Additional checks on supply
+ assert e.token_supply_at_period(period=0) == expected_initial_supply
+ assert e.cumulative_rewards_at_period(0) == 0
+
+ # Last NuNit is mined after 184 years (or 67000 periods).
+ # That's the year 2203, if token is launched in 2019.
+ # 23rd century schizoid man!
+ assert expected_total_supply == e.token_supply_at_period(period=67000)
+
+ # After 1 year:
+ assert 1_845_111_188_584347879497984668 == e.token_supply_at_period(period=365)
+ assert 845_111_188_584347879497984668 == e.cumulative_rewards_at_period(365)
+ assert e.erc20_initial_supply + e.cumulative_rewards_at_period(365) == e.token_supply_at_period(period=365)
+
+ # Checking that the supply function is monotonic
+ todays_supply = e.token_supply_at_period(period=0)
+ for t in range(67000):
+ tomorrows_supply = e.token_supply_at_period(period=t + 1)
+ assert tomorrows_supply >= todays_supply
+ todays_supply = tomorrows_supply
+
def test_economic_parameter_aliases():
- e = TokenEconomics()
+ e = StandardTokenEconomics()
assert e.locked_periods_coefficient == 365
assert int(e.staking_coefficient) == 768812
- assert e.maximum_locked_periods == 365
+ assert e.maximum_rewarded_periods == 365
deployment_params = e.staking_deployment_parameters
assert isinstance(deployment_params, tuple)
diff --git a/tests/blockchain/eth/interfaces/test_token_and_stake.py b/tests/blockchain/eth/interfaces/test_token_and_stake.py
index fe7db7988..5d01bdc90 100644
--- a/tests/blockchain/eth/interfaces/test_token_and_stake.py
+++ b/tests/blockchain/eth/interfaces/test_token_and_stake.py
@@ -3,7 +3,7 @@ from decimal import InvalidOperation, Decimal
import pytest
from web3 import Web3
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import TokenEconomics, StandardTokenEconomics
from nucypher.blockchain.eth.token import NU, Stake
from nucypher.utilities.sandbox.constants import INSECURE_DEVELOPMENT_PASSWORD
@@ -99,7 +99,7 @@ def test_NU(token_economics):
_nan = NU(float('NaN'), 'NU')
-def test_stake(testerchain, agency):
+def test_stake(testerchain, token_economics, agency):
token_agent, staking_agent, _policy_agent = agency
class FakeUrsula:
@@ -110,7 +110,6 @@ def test_stake(testerchain, agency):
staking_agent = staking_agent
token_agent = token_agent
blockchain = testerchain
- economics = TokenEconomics()
ursula = FakeUrsula()
stake = Stake(checksum_address=ursula.checksum_address,
@@ -118,7 +117,8 @@ def test_stake(testerchain, agency):
final_locked_period=100,
value=NU(100, 'NU'),
index=0,
- staking_agent=staking_agent)
+ staking_agent=staking_agent,
+ economics=token_economics)
assert stake.value, 'NU' == NU(100, 'NU')
diff --git a/tests/cli/ursula/test_stakeholder_and_ursula.py b/tests/cli/ursula/test_stakeholder_and_ursula.py
index 78e72fd9d..12851f614 100644
--- a/tests/cli/ursula/test_stakeholder_and_ursula.py
+++ b/tests/cli/ursula/test_stakeholder_and_ursula.py
@@ -133,7 +133,8 @@ def test_stake_init(click_runner,
stake = Stake.from_stake_info(index=0,
checksum_address=manual_staker,
stake_info=stakes[0],
- staking_agent=staking_agent)
+ staking_agent=staking_agent,
+ economics=token_economics)
assert stake.value == stake_value
assert stake.duration == token_economics.minimum_locked_periods
diff --git a/tests/fixtures.py b/tests/fixtures.py
index fa41e7b1c..2d54ae3cd 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -32,7 +32,7 @@ from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from web3 import Web3
-from nucypher.blockchain.economics import TokenEconomics, SlashingEconomics
+from nucypher.blockchain.economics import StandardTokenEconomics
from nucypher.blockchain.eth.actors import Staker
from nucypher.blockchain.eth.agents import NucypherTokenAgent
from nucypher.blockchain.eth.clients import NuCypherGethDevProcess
@@ -343,13 +343,7 @@ def federated_ursulas(ursula_federated_test_config):
@pytest.fixture(scope='session')
def token_economics():
- economics = TokenEconomics()
- return economics
-
-
-@pytest.fixture(scope='session')
-def slashing_economics():
- economics = SlashingEconomics()
+ economics = StandardTokenEconomics()
return economics
@@ -432,7 +426,7 @@ def _make_agency(testerchain, test_registry):
adjudicator_deployer.deploy(secret_hash=os.urandom(DispatcherDeployer.DISPATCHER_SECRET_LENGTH))
token_agent = token_deployer.make_agent() # 1 Token
- staking_agent = staking_escrow_deployer.make_agent() # 2 Miner Escrow
+ staking_agent = staking_escrow_deployer.make_agent() # 2 Staking Escrow
policy_agent = policy_manager_deployer.make_agent() # 3 Policy Agent
_adjudicator_agent = adjudicator_deployer.make_agent() # 4 Adjudicator
@@ -493,7 +487,7 @@ def stakers(testerchain, agency, token_economics, test_registry):
amount = random.randint(min_stake, balance)
# for a random lock duration
- min_locktime, max_locktime = token_economics.minimum_locked_periods, token_economics.maximum_locked_periods
+ min_locktime, max_locktime = token_economics.minimum_locked_periods, token_economics.maximum_rewarded_periods
periods = random.randint(min_locktime, max_locktime)
staker.initialize_stake(amount=amount, lock_periods=periods)
diff --git a/tests/metrics/estimate_gas.py b/tests/metrics/estimate_gas.py
index 9a03769c4..913b857b5 100755
--- a/tests/metrics/estimate_gas.py
+++ b/tests/metrics/estimate_gas.py
@@ -32,7 +32,7 @@ from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from zope.interface import provider
-from nucypher.blockchain.economics import TokenEconomics
+from nucypher.blockchain.economics import StandardTokenEconomics
from nucypher.blockchain.eth.agents import NucypherTokenAgent, StakingEscrowAgent, PolicyManagerAgent, AdjudicatorAgent
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
@@ -46,11 +46,11 @@ from fixtures import _mock_ursula_reencrypts as mock_ursula_reencrypts
ALGORITHM_SHA256 = 1
-TOKEN_ECONOMICS = TokenEconomics()
+TOKEN_ECONOMICS = StandardTokenEconomics()
MIN_ALLOWED_LOCKED = TOKEN_ECONOMICS.minimum_allowed_locked
MIN_LOCKED_PERIODS = TOKEN_ECONOMICS.minimum_locked_periods
MAX_ALLOWED_LOCKED = TOKEN_ECONOMICS.maximum_allowed_locked
-MAX_MINTING_PERIODS = TOKEN_ECONOMICS.maximum_locked_periods
+MAX_MINTING_PERIODS = TOKEN_ECONOMICS.maximum_rewarded_periods
class AnalyzeGas:
@@ -155,7 +155,13 @@ def estimate_gas(analyzer: AnalyzeGas = None) -> None:
log = Logger(AnalyzeGas.LOG_NAME)
# Blockchain
- testerchain, registry = TesterBlockchain.bootstrap_network()
+ economics = StandardTokenEconomics(
+ base_penalty=MIN_ALLOWED_LOCKED - 1,
+ penalty_history_coefficient=0,
+ percentage_penalty_coefficient=2,
+ reward_coefficient=2
+ )
+ testerchain, registry = TesterBlockchain.bootstrap_network(economics=economics)
web3 = testerchain.w3
# Accounts