Exemplo n.º 1
0
    def __init__(self,
                 checksum_address: str,
                 policy_agent: PolicyAgent = None,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:
        """
        :param policy_agent: A policy agent with the blockchain attached;
                             If not passed, a default policy agent and blockchain connection will
                             be created from default values.

        """
        super().__init__(checksum_address=checksum_address, *args, **kwargs)

        # From defaults
        if not policy_agent:
            self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
            self.staking_agent = StakingEscrowAgent(blockchain=self.blockchain)
            self.policy_agent = PolicyAgent(blockchain=self.blockchain)

        # Injected
        else:
            self.policy_agent = policy_agent

        self.economics = economics or TokenEconomics()
Exemplo n.º 2
0
    def __init__(self,
                 blockchain: BlockchainInterface,
                 sync_now: bool = True,
                 *args,
                 **kwargs):

        super().__init__(*args, **kwargs)

        self.log = Logger(f"stakeholder")

        # Blockchain and Contract connection
        self.blockchain = blockchain
        self.staking_agent = StakingEscrowAgent(blockchain=blockchain)
        self.token_agent = NucypherTokenAgent(blockchain=blockchain)
        self.economics = TokenEconomics()

        # Mode
        self.connect(blockchain=blockchain)

        self.__accounts = list()
        self.__stakers = dict()
        self.__transacting_powers = dict()

        self.__get_accounts()

        if sync_now:
            self.read_onchain_stakes()  # Stakes
Exemplo n.º 3
0
    def __init__(self,
                 checksum_address: str,
                 policy_agent=None,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:
        """
        :param policy_agent: A policy agent with the blockchain attached;
                             If not passed, a default policy agent and blockchain connection will
                             be created from default values.

        """
        super().__init__(checksum_address=checksum_address, *args, **kwargs)

        if not policy_agent:
            # From defaults
            self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
            self.miner_agent = MinerAgent(blockchain=self.blockchain)
            self.policy_agent = PolicyAgent(blockchain=self.blockchain)
        else:
            # Injected
            self.policy_agent = policy_agent

        if not economics:
            economics = TokenEconomics()
        self.economics = economics
Exemplo n.º 4
0
    def __init__(self, economics: TokenEconomics = None, *args, **kwargs):

        super().__init__(*args, **kwargs)
        self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
        if not economics:
            economics = TokenEconomics()
        self.__economics = economics
Exemplo n.º 5
0
    def __init__(self,
                 db_filepath: str,
                 rest_host: str,
                 rest_port: int,
                 client_password: str = None,
                 crash_on_error: bool = False,
                 economics: TokenEconomics = None,
                 distribute_ether: bool = True,
                 *args, **kwargs):

        # Character
        super().__init__(*args, **kwargs)
        self.log = Logger(f"felix-{self.checksum_address[-6::]}")

        # Network
        self.rest_port = rest_port
        self.rest_host = rest_host
        self.rest_app = NOT_RUNNING
        self.crash_on_error = crash_on_error

        # Database
        self.db_filepath = db_filepath
        self.db = NO_DATABASE_AVAILABLE
        self.db_engine = create_engine(f'sqlite:///{self.db_filepath}', convert_unicode=True)

        # Blockchain
        transacting_power = TransactingPower(blockchain=self.blockchain,
                                             password=client_password,
                                             account=self.checksum_address)
        self._crypto_power.consume_power_up(transacting_power)

        self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)
        self.reserved_addresses = [self.checksum_address, BlockchainInterface.NULL_ADDRESS]

        # Update reserved addresses with deployed contracts
        existing_entries = list(self.blockchain.registry.enrolled_addresses)
        self.reserved_addresses.extend(existing_entries)

        # Distribution
        self.__distributed = 0    # Track NU Output
        self.__airdrop = 0        # Track Batch
        self.__disbursement = 0   # Track Quantity
        self._distribution_task = LoopingCall(f=self.airdrop_tokens)
        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

        # Optionally send ether with each token transaction
        self.distribute_ether = distribute_ether

        # Banner
        self.log.info(FELIX_BANNER.format(self.checksum_address))
Exemplo n.º 6
0
    class FakeUrsula:
        token_agent, staking_agent, _policy_agent = agency

        burner_wallet = Web3().eth.account.create(
            INSECURE_DEVELOPMENT_PASSWORD)
        checksum_address = burner_wallet.address
        staking_agent = staking_agent
        token_agent = token_agent
        blockchain = testerchain
        economics = TokenEconomics()
Exemplo n.º 7
0
    class FakeUrsula:
        token_agent, miner_agent, _policy_agent = three_agents

        burner_wallet = Web3().eth.account.create(
            INSECURE_DEVELOPMENT_PASSWORD)
        checksum_public_address = burner_wallet.address
        miner_agent = miner_agent
        token_agent = token_agent
        blockchain = testerchain
        economics = TokenEconomics()
Exemplo n.º 8
0
    def __init__(self,
                 deployer_address: str,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:

        super().__init__(deployer_address=deployer_address, *args, **kwargs)
        self._creator = deployer_address
        if not economics:
            economics = TokenEconomics()
        self.__economics = economics
Exemplo n.º 9
0
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
def test_economic_parameter_aliases():

    e = TokenEconomics()

    assert e.locked_periods_coefficient == 365
    assert int(e.staking_coefficient) == 768812
    assert e.maximum_locked_periods == 365

    deployment_params = e.staking_deployment_parameters
    assert isinstance(deployment_params, tuple)
    for parameter in deployment_params:
        assert isinstance(parameter, int)
Exemplo n.º 11
0
    def __init__(self,
                 is_me: bool,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:

        super().__init__(*args, **kwargs)
        self.log = Logger("staker")
        self.stake_tracker = StakeTracker(
            checksum_addresses=[self.checksum_address])
        self.staking_agent = StakingEscrowAgent(blockchain=self.blockchain)
        self.economics = economics or TokenEconomics()
        self.is_me = is_me
Exemplo n.º 12
0
def make_decentralized_ursulas(ursula_config: UrsulaConfiguration,
                               ether_addresses: Union[list, int],
                               stake: bool = False,
                               know_each_other: bool = True,
                               economics: TokenEconomics = None,
                               **ursula_overrides) -> List[Ursula]:

    if not economics:
        economics = TokenEconomics()

    # Alternately accepts an int of the quantity of ursulas to make
    if isinstance(ether_addresses, int):
        ether_addresses = [to_checksum_address(secure_random(20)) for _ in range(ether_addresses)]

    if not MOCK_KNOWN_URSULAS_CACHE:
        starting_port = MOCK_URSULA_STARTING_PORT
    else:
        starting_port = max(MOCK_KNOWN_URSULAS_CACHE.keys()) + 1

    ursulas = list()
    for port, checksum_address in enumerate(ether_addresses, start=starting_port):

        ursula = ursula_config.produce(checksum_public_address=checksum_address,
                                       db_filepath=MOCK_URSULA_DB_FILEPATH,
                                       rest_port=port + 100,
                                       **ursula_overrides)
        if stake is True:

            min_stake, balance = economics.minimum_allowed_locked, ursula.token_balance
            amount = random.randint(min_stake, balance)

            # for a random lock duration
            min_locktime, max_locktime = economics.minimum_locked_periods, economics.maximum_locked_periods
            periods = random.randint(min_locktime, max_locktime)

            ursula.initialize_stake(amount=amount, lock_periods=periods)

        ursulas.append(ursula)
        # Store this Ursula in our global cache.
        port = ursula.rest_information()[0].port
        MOCK_KNOWN_URSULAS_CACHE[port] = ursula

    if know_each_other:

        for ursula_to_teach in ursulas:
            # Add other Ursulas as known nodes.
            for ursula_to_learn_about in ursulas:
                ursula_to_teach.remember_node(ursula_to_learn_about)

    return ursulas
Exemplo n.º 13
0
    def __init__(self,
                 is_me: bool,
                 start_staking_loop: bool = True,
                 economics: TokenEconomics = None,
                 *args,
                 **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.log = Logger("miner")
        self.is_me = is_me

        if not economics:
            economics = TokenEconomics()
        self.economics = economics

        #
        # Blockchain
        #

        if is_me:
            self.token_agent = NucypherTokenAgent(blockchain=self.blockchain)

            # Staking Loop
            self.__current_period = None
            self._abort_on_staking_error = True
            self._staking_task = task.LoopingCall(self.heartbeat)

        else:
            self.token_agent = STRANGER_MINER

        self.miner_agent = MinerAgent(blockchain=self.blockchain)

        #
        # Stakes
        #

        self.__stakes = UNKNOWN_STAKES
        self.__start_time = NOT_STAKING
        self.__uptime_period = NOT_STAKING
        self.__terminal_period = UNKNOWN_STAKES

        self.__read_stakes()  # "load-in":  Read on-chain stakes

        # Start the callbacks if there are active stakes
        if (self.stakes is not NO_STAKES) and start_staking_loop:
            self.stake()
def test_rough_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2

    K2 - Staking coefficient
    K1 - Locked periods coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    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))

    assert float(round(e.erc20_total_supply / Decimal(1e9),
                       2)) == 3.89  # As per economics paper

    # Check that we have correct numbers in day 1
    initial_rate = (e.erc20_total_supply - e.initial_supply) * (
        e.locked_periods_coefficient + 365) / e.staking_coefficient
    assert int(initial_rate) == int(e.initial_inflation * e.initial_supply /
                                    365)

    initial_rate_small = (
        e.erc20_total_supply - e.initial_supply
    ) * e.locked_periods_coefficient / e.staking_coefficient
    assert int(initial_rate_small) == int(initial_rate / 2)

    # Sanity check that total and reward supply calculated correctly
    assert int(LOG2 / (e.token_halving * 365) *
               (e.erc20_total_supply - e.initial_supply)) == int(initial_rate)
    assert int(e.reward_supply) == int(e.erc20_total_supply -
                                       Decimal(int(1e9)))

    # Sanity check for locked_periods_coefficient (k1) and staking_coefficient (k2)
    assert e.locked_periods_coefficient * e.token_halving == e.staking_coefficient * LOG2 * e.small_stake_multiplier / 365
Exemplo n.º 15
0
    def __init__(self,
                 checksum_address: str,
                 value: NU,
                 start_period: int,
                 end_period: int,
                 index: int,
                 economics=None,
                 validate_now: bool = True):

        self.log = Logger(f'stake-{checksum_address}-{index}')

        # Stake Metadata
        self.owner_address = checksum_address
        self.worker_address = UNKNOWN_WORKER_STATUS
        self.index = index
        self.value = value
        self.start_period = start_period
        self.end_period = end_period

        # Time
        self.start_datetime = datetime_at_period(period=start_period)
        self.end_datetime = datetime_at_period(period=end_period)
        self.duration_delta = self.end_datetime - self.start_datetime

        # Agency
        self.staking_agent = None
        self.token_agent = NucypherTokenAgent()  # TODO: Use Agency
        self.blockchain = self.token_agent.blockchain

        # Economics
        from nucypher.blockchain.economics import TokenEconomics
        self.economics = economics or TokenEconomics()
        self.minimum_nu = NU(int(self.economics.minimum_allowed_locked),
                             'NuNit')
        self.maximum_nu = NU(int(self.economics.maximum_allowed_locked),
                             'NuNit')

        if validate_now:
            self.validate_duration()

        self.transactions = NO_STAKING_RECEIPT
        self.receipt = NO_STAKING_RECEIPT
Exemplo n.º 16
0
def test_inflation_rate(testerchain, token, deploy_contract):
    """
    Check decreasing of inflation rate after minting.
    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(
        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, economics.erc20_reward_supply).transact({'from': creator})
    testerchain.wait_for_receipt(tx)
    tx = issuer.functions.initialize().transact({'from': creator})
    testerchain.wait_for_receipt(tx)
    reward = issuer.functions.getReservedReward().call()

    # Mint some tokens and save result of minting
    period = issuer.functions.getCurrentPeriod().call()
    tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    one_period = token.functions.balanceOf(ursula).call()

    # Mint more tokens in the same period, inflation rate must be the same as in previous minting
    tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    assert 2 * one_period == token.functions.balanceOf(ursula).call()
    assert reward - token.functions.balanceOf(ursula).call() == issuer.functions.getReservedReward().call()

    # Mint tokens in the next period, inflation rate must be lower than in previous minting
    tx = issuer.functions.testMint(period + 2, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    assert 3 * one_period > token.functions.balanceOf(ursula).call()
    assert reward - token.functions.balanceOf(ursula).call() == issuer.functions.getReservedReward().call()
    minted_amount = token.functions.balanceOf(ursula).call() - 2 * one_period

    # Mint tokens in the first period again, inflation rate must be the same as in previous minting
    # but can't be equals as in first minting because rate can't be increased
    tx = issuer.functions.testMint(period + 1, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    assert 2 * one_period + 2 * minted_amount == token.functions.balanceOf(ursula).call()
    assert reward - token.functions.balanceOf(ursula).call() == issuer.functions.getReservedReward().call()

    # Mint tokens in the next period, inflation rate must be lower than in previous minting
    tx = issuer.functions.testMint(period + 3, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    assert 2 * one_period + 3 * minted_amount > token.functions.balanceOf(ursula).call()
    assert reward - token.functions.balanceOf(ursula).call() == issuer.functions.getReservedReward().call()

    # Return some tokens as a reward
    balance = token.functions.balanceOf(ursula).call()
    reward = issuer.functions.getReservedReward().call()
    tx = issuer.functions.testUnMint(2 * one_period + 2 * minted_amount).transact()
    testerchain.wait_for_receipt(tx)
    assert reward + 2 * one_period + 2 * minted_amount == issuer.functions.getReservedReward().call()

    # Rate will be increased because some tokens were returned
    tx = issuer.functions.testMint(period + 3, 1, 1, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    assert balance + one_period == token.functions.balanceOf(ursula).call()
    assert reward + one_period + 2 * minted_amount == issuer.functions.getReservedReward().call()
Exemplo n.º 17
0
            value, param, ctx))


class IPv4Address(click.ParamType):
    name = 'ipv4_address'

    def convert(self, value, param, ctx):
        try:
            _address = ip_address(value)
        except ValueError as e:
            self.fail(str(e))
        else:
            return value


token_economics = TokenEconomics()

# Staking
STAKE_DURATION = click.IntRange(min=token_economics.minimum_locked_periods,
                                max=token_economics.maximum_locked_periods,
                                clamp=False)
STAKE_EXTENSION = click.IntRange(min=1,
                                 max=token_economics.maximum_allowed_locked,
                                 clamp=False)
STAKE_VALUE = click.IntRange(min=NU(token_economics.minimum_allowed_locked,
                                    'NuNit').to_tokens(),
                             max=NU(token_economics.maximum_allowed_locked,
                                    'NuNit').to_tokens(),
                             clamp=False)

# Filesystem
Exemplo n.º 18
0
from umbral.keys import UmbralPrivateKey
from umbral.signing import Signer
from zope.interface import provider

from nucypher.blockchain.economics import TokenEconomics
from nucypher.blockchain.eth.agents import NucypherTokenAgent, StakingEscrowAgent, PolicyAgent, AdjudicatorAgent
from nucypher.crypto.signing import SignatureStamp
from nucypher.policy.models import Policy
from nucypher.utilities.sandbox.blockchain import TesterBlockchain

# FIXME: Needed to use a fixture here, but now estimate_gas.py only runs if executed from main directory
sys.path.insert(0, abspath('tests'))
from fixtures import _mock_ursula_reencrypts as mock_ursula_reencrypts

ALGORITHM_SHA256 = 1
TOKEN_ECONOMICS = TokenEconomics()
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


class AnalyzeGas:
    """
    Callable twisted log observer with built-in record-keeping for gas estimation runs.
    """

    # Logging
    LOG_NAME = 'estimate-gas'
    LOG_FILENAME = '{}.log.json'.format(LOG_NAME)
    OUTPUT_DIR = os.path.join(abspath(dirname(__file__)), 'results')
Exemplo n.º 19
0
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(
            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(
            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
    tx = token.functions.transfer(issuer.address, economics.erc20_reward_supply).transact({'from': creator})
    testerchain.wait_for_receipt(tx)

    # Only owner can initialize
    with pytest.raises((TransactionFailed, ValueError)):
        tx = issuer.functions.initialize().transact({'from': ursula})
        testerchain.wait_for_receipt(tx)
    tx = issuer.functions.initialize().transact({'from': creator})
    testerchain.wait_for_receipt(tx)

    events = events.get_all_entries()
    assert 1 == len(events)
    assert economics.erc20_reward_supply == events[0]['args']['reservedReward']
    balance = token.functions.balanceOf(issuer.address).call()

    # Can't initialize second time
    with pytest.raises((TransactionFailed, ValueError)):
        tx = issuer.functions.initialize().transact({'from': creator})
        testerchain.wait_for_receipt(tx)

    # Check result of minting tokens
    tx = issuer.functions.testMint(0, 1000, 2000, 0).transact({'from': ursula})
    testerchain.wait_for_receipt(tx)
    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)
    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)
    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)
    reward += calculate_reward(500, 500, 10 ** 4)
    assert reward == token.functions.balanceOf(ursula).call()
    assert balance - reward == token.functions.balanceOf(issuer.address).call()
Exemplo n.º 20
0
def token_economics():
    economics = TokenEconomics()
    return economics
def test_exact_economics():
    """
    Formula for staking in one period:
    (totalSupply - currentSupply) * (lockedValue / totalLockedValue) * (k1 + allLockedPeriods) / k2

    K2 - Staking coefficient
    K1 - Locked periods coefficient

    if allLockedPeriods > awarded_periods then allLockedPeriods = awarded_periods
    kappa * log(2) / halving_delay === (k1 + allLockedPeriods) / k2

    kappa = small_stake_multiplier + (1 - small_stake_multiplier) * min(T, T1) / T1
    where allLockedPeriods == min(T, T1)
    """

    #
    # Expected Output
    #

    # Supply
    expected_total_supply = 3885390081777926911255691439
    expected_supply_ratio = Decimal('3.885390081777926911255691439')
    expected_initial_supply = 1000000000000000000000000000

    # Reward
    expected_reward_supply = 2885390081777926911255691439
    reward_saturation = 1

    # Staking
    halving = 2
    multiplier = 0.5
    expected_locked_periods_coefficient = 365
    expected_staking_coefficient = 768812

    assert expected_locked_periods_coefficient * halving == round(
        expected_staking_coefficient * log(2) * multiplier / 365)

    #
    # Sanity
    #

    # Sanity check ratio accuracy
    expected_scaled_ratio = str(expected_supply_ratio).replace('.', '')
    assert str(expected_total_supply) == expected_scaled_ratio

    # Sanity check denomination size
    expected_scale = 28
    assert len(str(expected_total_supply)) == expected_scale
    assert len(str(expected_initial_supply)) == expected_scale
    assert len(str(expected_reward_supply)) == expected_scale

    # Use same precision as economics class
    with localcontext() as ctx:
        ctx.prec = TokenEconomics._precision

        # Sanity check expected testing outputs
        assert Decimal(expected_total_supply
                       ) / expected_initial_supply == expected_supply_ratio
        assert expected_reward_supply == expected_total_supply - expected_initial_supply
        assert reward_saturation * 365 * multiplier == expected_locked_periods_coefficient * (
            1 - multiplier)
        assert int(365**2 * reward_saturation * halving / log(2) /
                   (1 - multiplier)) == expected_staking_coefficient

    # After sanity checking, assemble expected test deployment parameters
    expected_deployment_parameters = (
        24,  # Hours in single period
        768812,  # Staking coefficient (k2)
        365,  # Locked periods coefficient (k1)
        365,  # Max periods that will be additionally rewarded (awarded_periods)
        30,  # Min amount of periods during which tokens can be locked
        15000000000000000000000,  # min locked NuNits
        4000000000000000000000000)  # max locked NuNits

    #
    # Token Economics
    #

    # Check creation
    e = TokenEconomics()

    with localcontext() as ctx:
        ctx.prec = TokenEconomics._precision

        # Check that total_supply calculated correctly
        assert Decimal(
            e.erc20_total_supply) / e.initial_supply == expected_supply_ratio
        assert e.erc20_total_supply == expected_total_supply

        # Check reward rates
        initial_rate = Decimal(
            (e.erc20_total_supply - e.initial_supply) *
            (e.locked_periods_coefficient + 365) / e.staking_coefficient)
        assert initial_rate == Decimal(
            (e.initial_inflation * e.initial_supply) / 365)

        initial_rate_small = (
            e.erc20_total_supply - e.initial_supply
        ) * e.locked_periods_coefficient / e.staking_coefficient
        assert Decimal(initial_rate_small) == Decimal(initial_rate / 2)

        # Check reward supply
        assert Decimal(
            LOG2 / (e.token_halving * 365) *
            (e.erc20_total_supply - e.initial_supply)) == initial_rate
        assert e.reward_supply == expected_total_supply - expected_initial_supply

        # Check deployment parameters
        assert e.staking_deployment_parameters == expected_deployment_parameters
        assert e.erc20_initial_supply == expected_initial_supply
        assert e.erc20_reward_supply == expected_reward_supply