Ejemplo n.º 1
0
def test_does_not_exist():
    with pytest.raises(NameError):
        contract_strategy("Foo").validate()
Ejemplo n.º 2
0
class StateMachine:

    # stateful test information
    pool_info = {}
    added_pools = set()

    # state machine rules
    st_pool = contract_strategy("PoolMock")
    st_coin = contract_strategy("yERC20")
    st_coin2 = contract_strategy("yERC20")
    st_underlying = contract_strategy("ERC20")
    st_underlying2 = contract_strategy("ERC20")
    st_decimals = strategy('uint8[8]', min_value=1, max_value=42)
    st_amount = strategy('uint256', max_value=1e18)

    def __init__(cls, PoolMock, accounts, registry, coins, underlying, USDT):
        """
        Initial state machine setup.

        This method only runs once, prior to the initial snapshot.
        """
        cls.accounts = accounts
        cls.registry = registry

        # fund test account with initial balances
        for coin in coins + underlying:
            coin._mint_for_testing(1e18, {'from': accounts[0]})
            coin.approve(registry, 2**256 - 1, {'from': accounts[0]})

        # create pools
        for i in range(3):
            cls._create_pool(PoolMock, 2, coins[:2], underlying[:2], USDT)
            coins.insert(0, coins.pop())
            underlying.insert(0, underlying.pop())

        for i in range(2):
            cls._create_pool(PoolMock, 3, coins[:3], underlying[:3], USDT)
            coins.insert(0, coins.pop())
            underlying.insert(0, underlying.pop())

        cls._create_pool(PoolMock, 4, coins, underlying, USDT)

    @classmethod
    def _create_pool(cls, PoolMock, n_coins, coins, underlying, USDT):
        # Create a pool and add it to `cls.pool_info`
        coins = coins + ([ZERO_ADDRESS] * (8 - n_coins))
        underlying = underlying + ([ZERO_ADDRESS] * (8 - n_coins))

        pool = PoolMock.deploy(n_coins, coins[:4], underlying[:4], 70, 4000000,
                               {'from': cls.accounts[0]})
        cls.pool_info[pool] = {'coins': coins, 'underlying': underlying}

    def setup(self):
        """
        Reset test conditions between each run.
        """
        self.added_pools.clear()

    def rule_add_pool(self, st_pool, st_decimals):
        """
        Attempt to add a pool to the registry.

        Decimal values are randomized with `st_decimals` - this has no effect on
        other rules, and helps to verify `Registry.get_pool_coins`

        Revert Paths
        ------------
        * The pool has already been added
        """
        n_coins = st_pool.n_coins()

        if st_pool in self.added_pools:
            with brownie.reverts("dev: pool exists"):
                self.registry.add_pool(st_pool, n_coins, ZERO_ADDRESS,
                                       ZERO_ADDRESS, "0x00", "0x00", "0x00",
                                       True, True, {'from': self.accounts[0]})
        else:
            decimals = st_decimals[:n_coins] + [0] * (8 - n_coins)
            udecimals = st_decimals[-n_coins:] + [0] * (8 - n_coins)

            self.registry.add_pool(st_pool, n_coins, ZERO_ADDRESS,
                                   ZERO_ADDRESS, "0x00", pack_values(decimals),
                                   pack_values(udecimals), True, True,
                                   {'from': self.accounts[0]})
            self.added_pools.add(st_pool)
            self.pool_info[st_pool]['decimals'] = decimals
            self.pool_info[st_pool]['underlying_decimals'] = udecimals

    def rule_remove_pool(self, st_pool):
        """
        Attempt to remove a pool from the registry.

        Revert Paths
        ------------
        * The pool has not been added, or was already removed
        """
        if st_pool in self.added_pools:
            self.registry.remove_pool(st_pool, {'from': self.accounts[0]})
            self.added_pools.discard(st_pool)
        else:
            with brownie.reverts("dev: pool does not exist"):
                self.registry.remove_pool(st_pool, {'from': self.accounts[0]})

    def _exchange(self, pool, coin, coin2, amount):
        # attempt to perform an exchange
        if pool in self.added_pools and coin != coin2:
            from_balance = coin.balanceOf(self.accounts[0])
            to_balance = coin2.balanceOf(self.accounts[0])

            if amount <= from_balance:
                expected = self.registry.get_exchange_amount(
                    pool, coin, coin2, amount)
                self.registry.exchange(pool, coin, coin2, amount, 0)
                assert coin.balanceOf(
                    self.accounts[0]) == from_balance - amount
                assert coin2.balanceOf(
                    self.accounts[0]) == to_balance + expected
            else:
                with brownie.reverts():
                    self.registry.exchange(pool, coin, coin2, amount, 0)

        else:
            with brownie.reverts():
                self.registry.exchange(pool, coin, coin2, amount, 0)

    def rule_exchange(self, st_coin, st_coin2, st_amount):
        """
        Attempt to perform a token exchange.

        The exchange is attempted with the first pool in `cls.pool_info` that
        provides a market for `st_coin` and `st_coin2`.

        Revert Paths
        ------------
        * The pool has not been added
        * `st_coin` and `st_coin2` are the same coin
        * `accounts[0]` has an insufficient balance of `st_coin`
        """
        pool = next(k for k, v in self.pool_info.items()
                    if st_coin in v['coins'] and st_coin2 in v['coins'])
        self._exchange(pool, st_coin, st_coin2, st_amount)

    def rule_exchange_underlying(self, st_underlying, st_underlying2,
                                 st_amount):
        """
        Attempt to perform a token exchange with underlying tokens.

        The exchange is attempted with the first pool in `cls.pool_info` that
        provides a market for `st_underlying` and `st_underlying2`.

        Revert Paths
        ------------
        * The pool has not been added
        * `st_underlying` and `st_underlying` are the same coin
        * `accounts[0]` has an insufficient balance of `st_underlying`
        """
        pool = next(k for k, v in self.pool_info.items()
                    if st_underlying in v['underlying']
                    and st_underlying2 in v['underlying'])
        self._exchange(pool, st_underlying, st_underlying2, st_amount)

    def invariant_coins(self):
        """
        Invariant for `get_pool_coins`

        Checks
        ------
        * Added pools should return the correct coins, underlying coins, and decimals
        * Pools that were not added, or were removed, should return zero values
        """
        for pool in self.pool_info:
            coins = self.registry.get_pool_coins(pool)
            if pool in self.added_pools:
                coins = self.registry.get_pool_coins(pool)
                assert coins['coins'] == self.pool_info[pool]['coins']
                assert coins['underlying_coins'] == self.pool_info[pool][
                    'underlying']
                assert coins['decimals'] == self.pool_info[pool]['decimals']
                assert coins['underlying_decimals'] == self.pool_info[pool][
                    'underlying_decimals']
            else:
                assert coins['coins'] == [ZERO_ADDRESS] * 8
                assert coins['underlying_coins'] == [ZERO_ADDRESS] * 8
                assert coins['decimals'] == [0] * 8
                assert coins['underlying_decimals'] == [0] * 8
Ejemplo n.º 3
0
def test_repr():
    assert repr(contract_strategy("Token")) == "sampled_from(Token)"
Ejemplo n.º 4
0
def test_strategy():
    assert isinstance(contract_strategy("ERC20"), DeferredStrategy)
Ejemplo n.º 5
0
#!/usr/bin/python3

import pytest
from hypothesis import HealthCheck, given, settings
from hypothesis.strategies._internal.deferred import DeferredStrategy

from brownie.network.contract import ProjectContract
from brownie.test import contract_strategy


def test_strategy():
    assert isinstance(contract_strategy("ERC20"), DeferredStrategy)


def test_repr():
    assert repr(contract_strategy("Token")) == "sampled_from(Token)"


def test_does_not_exist():
    with pytest.raises(NameError):
        contract_strategy("Foo").validate()


@given(contract=contract_strategy("BrownieTester"))
@settings(suppress_health_check=[HealthCheck.function_scoped_fixture])
def test_given(tester, contract):
    assert isinstance(contract, ProjectContract)
class StateMachine:

    st_contract = contract_strategy("ERC20")
    st_token = strategy("uint256", max_value=9)
    st_amount = strategy("uint256", max_value=2**16 * 10**18)

    def __init__(cls, alice: Account, ERC20: ContractContainer,
                 xhibit: Contract):
        cls.alice = alice
        cls.xhibit = xhibit

        # create 10 mock ERC20 contracts
        for i in range(10):
            instance = alice.deploy(ERC20, f"Test Token {i}", f"TST{i}", 18)
            # mint a s#!t ton of tokens :)
            instance._mint_for_testing(alice, 2**256 - 1, {"from": alice})
            # approve ahead of time
            instance.approve(xhibit, 2**256 - 1, {"from": alice})
            # create 10 xhibit NFTs as well
            xhibit.mint(alice, {"from": alice})

    def setup(self):
        self.state = State()

    def rule_receive_erc20(
        self,
        st_contract: Contract,
        st_token: int,
        st_amount: int,
    ):
        if st_contract.balanceOf(self.alice) >= st_amount:
            st_contract.approve(self.xhibit, 2**256 - 1, {"from": self.alice})
            self.xhibit.getERC20(self.alice, st_token, st_contract, st_amount)
            self.state.receive_erc20(st_token, str(st_contract), st_amount)

    def rule_remove_erc20(self, st_contract: Contract, st_token: int,
                          st_amount: int):
        if self.xhibit.balanceOfERC20(st_token, st_contract) >= st_amount:
            self.xhibit.transferERC20(st_token, self.alice, st_contract,
                                      st_amount)
            self.state.remove_erc20(st_token, str(st_contract), st_amount)

    def rule_clear_erc20(self, st_contract: Contract, st_token: int):
        balance = self.xhibit.balanceOfERC20(st_token, st_contract)
        if balance > 0:
            self.xhibit.transferERC20(st_token, self.alice, st_contract,
                                      balance)
            self.state.remove_erc20(st_token, str(st_contract), balance)

    def invariant_totalERC20Contracts(self):
        # for each xhibit NFT verify the total # of child contracts
        for i in range(10):
            assert self.xhibit.totalERC20Contracts(
                i) == self.state.total_erc20_contracts(i)

    def invariant_erc20ContractByIndex(self):
        # for each xhibit NFT
        for i in range(10):
            erc20_contracts = {
                self.xhibit.erc20ContractByIndex(i, j)
                for j in range(self.state.total_erc20_contracts(i))
            }
            assert erc20_contracts == self.state.erc20_contracts(i)

    def invariant_erc20_balance(self):
        # for each xhibit NFT
        for i in range(10):
            erc20_contracts = {
                self.xhibit.erc20ContractByIndex(i, j)
                for j in range(self.state.total_erc20_contracts(i))
            }
            for contract in erc20_contracts:
                assert (self.xhibit.balanceOfERC20(
                    i, contract) == self.state.tokens[i][contract])
Ejemplo n.º 7
0
class StateMachine:

    st_contract = contract_strategy("ERC721")
    st_token = strategy("uint", max_value=9)

    def __init__(cls, alice: Account, ERC721: ContractContainer,
                 xhibit: Contract):
        cls.alice = alice
        cls.xhibit = xhibit

        # create 10 mock ERC721 contracts
        for _ in range(10):
            instance = alice.deploy(ERC721)
            # create 10 xhibit NFTs as well
            xhibit.mint(alice, {"from": alice})
            # create 10 NFTs in this contract instance
            for _ in range(10):
                instance._mint_for_testing(alice, {"from": alice})

    def setup(self):
        self.state = State()

    def rule_receive_child(
        self,
        st_contract: Contract,
        token_id: int = "st_token",
        receiving_token: int = "st_token",
    ):
        if st_contract.ownerOf(token_id) == self.alice:
            st_contract.safeTransferFrom(self.alice, self.xhibit, token_id,
                                         receiving_token, {"from": self.alice})
            self.state.receive_child(receiving_token, str(st_contract),
                                     token_id)

    def rule_remove_child(
        self,
        st_contract: Contract,
        child_token: int = "st_token",
    ):
        if st_contract.ownerOf(child_token) == self.xhibit:
            from_token = self.xhibit.ownerOfChild(st_contract, child_token)[1]
            self.xhibit.transferChild(from_token, self.alice, st_contract,
                                      child_token, {"from": self.alice})
            self.state.remove_child(from_token, str(st_contract), child_token)

    def invariant_totalChildContracts(self):
        # for each xhibit NFT verify the total # of child contracts
        for i in range(10):
            assert self.xhibit.totalChildContracts(
                i) == self.state.total_child_contracts(i)

    def invariant_childContracts(self):
        # for each xhibit NFT
        for i in range(10):
            child_contracts = {
                self.xhibit.childContractByIndex(i, j)
                for j in range(self.state.total_child_contracts(i))
            }
            assert child_contracts == self.state.child_contracts(i)

    def invariant_totalChildTokens(self):
        # for each xhibit NFT
        for i in range(10):
            for child_contract in self.state.child_contracts(i):
                assert self.xhibit.totalChildTokens(
                    i, child_contract) == self.state.total_child_tokens(
                        i, child_contract)

    def invariant_childTokens(self):
        for i in range(10):
            for child_contract in self.state.child_contracts(i):
                child_tokens = {
                    self.xhibit.childTokenByIndex(i, child_contract, j)
                    for j in range(
                        self.state.total_child_tokens(i, child_contract))
                }
                assert child_tokens == self.state.child_tokens(
                    i, child_contract)