예제 #1
0
def test_query_transactions_removed_address(
        rotkehlchen_api_server,
        ethereum_accounts,
):
    """Make sure that if an address is removed so are the transactions from the DB"""
    start_ts = 0
    end_ts = 1598453214
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    db = rotki.data.db
    transactions = [EthereumTransaction(
        tx_hash=b'1',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[0],
        to_address=make_ethereum_address(),
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=0,
    ), EthereumTransaction(
        tx_hash=b'2',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[0],
        to_address=make_ethereum_address(),
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=1,
    ), EthereumTransaction(  # should remain after deletining account[0]
        tx_hash=b'3',
        timestamp=0,
        block_number=0,
        from_address=make_ethereum_address(),
        to_address=ethereum_accounts[1],
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=55,
    ), EthereumTransaction(  # should remain after deletining account[0]
        tx_hash=b'4',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[1],
        to_address=ethereum_accounts[0],
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=0,
    ), EthereumTransaction(  # should remain after deletining account[0]
        tx_hash=b'5',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[0],
        to_address=ethereum_accounts[1],
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=0,
    )]
    db.add_ethereum_transactions(transactions, from_etherscan=True)
    # Also make sure to update query ranges so as not to query etherscan at all
    for address in ethereum_accounts:
        DBQueryRanges(db).update_used_query_range(
            location_string=f'ethtxs_{address}',
            start_ts=start_ts,
            end_ts=end_ts,
            ranges_to_query=[],
        )

    # Now remove the first account (do the mocking to not query etherscan for balances)
    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=[],
        eth_balances=['10000', '10000'],
    )
    with ExitStack() as stack:
        setup.enter_ethereum_patches(stack)
        response = requests.delete(api_url_for(
            rotkehlchen_api_server,
            "blockchainsaccountsresource",
            blockchain='ETH',
        ), json={'accounts': [ethereum_accounts[0]]})
    assert_proper_response_with_result(response)

    # Check that only the 3 remanining transactions from the other account is returned
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumtransactionsresource',
        ),
    )
    result = assert_proper_response_with_result(response)
    assert len(result['entries']) == 3
    assert result['entries_found'] == 3
예제 #2
0
def test_query_transactions_over_limit(
        rotkehlchen_api_server,
        ethereum_accounts,
        start_with_valid_premium,
):
    start_ts = 0
    end_ts = 1598453214
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    db = rotki.data.db
    all_transactions_num = FREE_ETH_TX_LIMIT + 50
    transactions = [EthereumTransaction(
        tx_hash=x.to_bytes(2, byteorder='little'),
        timestamp=x,
        block_number=x,
        from_address=ethereum_accounts[0],
        to_address=make_ethereum_address(),
        value=x,
        gas=x,
        gas_price=x,
        gas_used=x,
        input_data=x.to_bytes(2, byteorder='little'),
        nonce=x,
    ) for x in range(FREE_ETH_TX_LIMIT - 10)]
    transactions.extend([EthereumTransaction(
        tx_hash=(x + 500).to_bytes(2, byteorder='little'),
        timestamp=x,
        block_number=x,
        from_address=ethereum_accounts[1],
        to_address=make_ethereum_address(),
        value=x,
        gas=x,
        gas_price=x,
        gas_used=x,
        input_data=x.to_bytes(2, byteorder='little'),
        nonce=x,
    ) for x in range(60)])

    db.add_ethereum_transactions(transactions, from_etherscan=True)
    # Also make sure to update query ranges so as not to query etherscan at all
    for address in ethereum_accounts:
        DBQueryRanges(db).update_used_query_range(
            location_string=f'ethtxs_{address}',
            start_ts=start_ts,
            end_ts=end_ts,
            ranges_to_query=[],
        )

    free_expected_entries = [FREE_ETH_TX_LIMIT - 10, 10]
    premium_expected_entries = [FREE_ETH_TX_LIMIT - 10, 60]

    # Check that we get all transactions correctly even if we query two times
    for _ in range(2):
        for idx, address in enumerate(ethereum_accounts):
            response = requests.get(
                api_url_for(
                    rotkehlchen_api_server,
                    'ethereumtransactionsresource',
                ), json={'from_timestamp': start_ts, 'to_timestamp': end_ts, 'address': address},
            )
            result = assert_proper_response_with_result(response)

            if start_with_valid_premium:
                assert len(result['entries']) == premium_expected_entries[idx]
                assert result['entries_found'] == all_transactions_num
                assert result['entries_limit'] == -1
            else:
                assert len(result['entries']) == free_expected_entries[idx]
                assert result['entries_found'] == all_transactions_num
                assert result['entries_limit'] == FREE_ETH_TX_LIMIT
예제 #3
0
def test_query_transactions_from_to_address(
        rotkehlchen_api_server,
        ethereum_accounts,
):
    """Make sure that if a transaction is just being sent to an address it's also returned."""
    start_ts = 0
    end_ts = 1598453214
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    db = rotki.data.db
    transactions = [EthereumTransaction(
        tx_hash=b'1',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[0],
        to_address=make_ethereum_address(),
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=0,
    ), EthereumTransaction(
        tx_hash=b'2',
        timestamp=0,
        block_number=0,
        from_address=ethereum_accounts[0],
        to_address=ethereum_accounts[1],
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=1,
    ), EthereumTransaction(
        tx_hash=b'3',
        timestamp=0,
        block_number=0,
        from_address=make_ethereum_address(),
        to_address=ethereum_accounts[0],
        value=1,
        gas=1,
        gas_price=1,
        gas_used=1,
        input_data=b'',
        nonce=55,
    )]
    db.add_ethereum_transactions(transactions, from_etherscan=True)
    # Also make sure to update query ranges so as not to query etherscan at all
    for address in ethereum_accounts:
        DBQueryRanges(db).update_used_query_range(
            location_string=f'ethtxs_{address}',
            start_ts=start_ts,
            end_ts=end_ts,
            ranges_to_query=[],
        )

    expected_entries = {ethereum_accounts[0]: 3, ethereum_accounts[1]: 1}
    # Check that we get all transactions correctly even if we query two times
    for _ in range(2):
        for address in ethereum_accounts:
            response = requests.get(
                api_url_for(
                    rotkehlchen_api_server,
                    'ethereumtransactionsresource',
                ), json={'from_timestamp': start_ts, 'to_timestamp': end_ts, 'address': address},
            )
            result = assert_proper_response_with_result(response)
            assert len(result['entries']) == expected_entries[address]
            assert result['entries_found'] == 3
예제 #4
0
파일: globaldb.py 프로젝트: jsloane/rotki
from rotkehlchen.assets.asset import EthereumToken, UnderlyingToken
from rotkehlchen.fval import FVal
from rotkehlchen.tests.utils.constants import A_MKR
from rotkehlchen.tests.utils.factories import make_ethereum_address
from rotkehlchen.typing import Timestamp

underlying_address1 = make_ethereum_address()
underlying_address2 = make_ethereum_address()
underlying_address3 = make_ethereum_address()

custom_address1 = make_ethereum_address()
custom_address2 = make_ethereum_address()
INITIAL_TOKENS = [
    EthereumToken.initialize(
        address=custom_address1,
        decimals=4,
        name='Custom 1',
        symbol='CST1',
        started=Timestamp(0),
        swapped_for=A_MKR,
        coingecko='foo',
        cryptocompare='boo',
        protocol='uniswap',
        underlying_tokens=[
            UnderlyingToken(address=underlying_address1,
                            weight=FVal('0.5055')),
            UnderlyingToken(address=underlying_address2,
                            weight=FVal('0.1545')),
            UnderlyingToken(address=underlying_address3, weight=FVal('0.34')),
        ],
    ),
예제 #5
0
def test_queried_addresses_per_protocol(rotkehlchen_api_server):
    # First add some queried addresses per protocol
    address1 = make_ethereum_address()
    data = {'module': 'aave', 'address': address1}
    response = requests.put(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    result = assert_proper_response_with_result(response)
    assert result == {'aave': [address1]}

    address2 = make_ethereum_address()
    data = {'module': 'makerdao_vaults', 'address': address2}
    response = requests.put(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    result = assert_proper_response_with_result(response)
    assert_queried_addresses_match(result, {
        'aave': [address1],
        'makerdao_vaults': [address2],
    })

    # add same address to another module/protocol
    data = {'module': 'aave', 'address': address2}
    response = requests.put(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    result = assert_proper_response_with_result(response)
    assert_queried_addresses_match(result, {
        'aave': [address1, address2],
        'makerdao_vaults': [address2],
    })

    # try to add an address that already exists for a module/protocol and assert we get an error
    data = {'module': 'aave', 'address': address1}
    response = requests.put(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        f'{address1} is already in the queried addresses for aave',
        status_code=HTTPStatus.CONFLICT,
    )

    # add an address and then remove it
    address3 = make_ethereum_address()
    data = {'module': 'makerdao_dsr', 'address': address3}
    response = requests.put(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    result = assert_proper_response_with_result(response)
    assert_queried_addresses_match(
        result, {
            'aave': [address1, address2],
            'makerdao_vaults': [address2],
            'makerdao_dsr': [address3],
        })

    response = requests.delete(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    result = assert_proper_response_with_result(response)
    assert_queried_addresses_match(result, {
        'aave': [address1, address2],
        'makerdao_vaults': [address2],
    })

    # try to remove a non-existing address and module combination and assert we get an error
    data = {'module': 'makerdao_vaults', 'address': address1}
    response = requests.delete(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg=
        f'{address1} is not in the queried addresses for makerdao_vaults',
        status_code=HTTPStatus.CONFLICT,
    )

    # test that getting the queried addresses per module works
    response = requests.get(
        api_url_for(rotkehlchen_api_server, "queriedaddressesresource"))
    result = assert_proper_response_with_result(response)
    assert_queried_addresses_match(result, {
        'aave': [address1, address2],
        'makerdao_vaults': [address2],
    })
예제 #6
0
def test_global_db_restore(globaldb, database):
    """
    Check that the user can recreate assets information from the packaged
    database with rotki (hard reset). The test adds a new asset, restores
    the database and checks that the added token is not in there and that
    the amount of assets is the expected
    """
    # Add a custom eth token
    address_to_delete = make_ethereum_address()
    token_to_delete = EthereumToken.initialize(
        address=address_to_delete,
        decimals=18,
        name='willdell',
        symbol='DELME',
    )
    globaldb.add_asset(
        asset_id='DELMEID1',
        asset_type=AssetType.ETHEREUM_TOKEN,
        data=token_to_delete,
    )
    # Add a token with underlying token
    with_underlying_address = make_ethereum_address()
    with_underlying = EthereumToken.initialize(
        address=with_underlying_address,
        decimals=18,
        name="Not a scam",
        symbol="NSCM",
        started=0,
        underlying_tokens=[
            UnderlyingToken(
                address=address_to_delete,
                weight=1,
            )
        ],
    )
    globaldb.add_asset(
        asset_id='xDELMEID1',
        asset_type=AssetType.ETHEREUM_TOKEN,
        data=with_underlying,
    )
    # Add asset that is not a token
    globaldb.add_asset(
        asset_id='1',
        asset_type=AssetType.OWN_CHAIN,
        data={
            'name': 'Lolcoin',
            'symbol': 'LOLZ',
            'started': 0,
        },
    )

    # Add asset that is not a token
    globaldb.add_asset(
        asset_id='2',
        asset_type=AssetType.OWN_CHAIN,
        data={
            'name': 'Lolcoin2',
            'symbol': 'LOLZ2',
            'started': 0,
        },
    )

    database.add_asset_identifiers('1')
    database.add_asset_identifiers('2')

    # Try to reset DB it if we have a trade that uses a custom asset
    buy_asset = symbol_to_asset_or_token('LOLZ2')
    buy_amount = deserialize_asset_amount(1)
    sold_asset = symbol_to_asset_or_token('LOLZ')
    sold_amount = deserialize_asset_amount(2)
    rate = Price(buy_amount / sold_amount)
    trade = Trade(
        timestamp=Timestamp(12312312),
        location=Location.BLOCKFI,
        base_asset=buy_asset,
        quote_asset=sold_asset,
        trade_type=TradeType.BUY,
        amount=buy_amount,
        rate=rate,
        fee=None,
        fee_currency=None,
        link='',
        notes="",
    )

    database.add_trades([trade])
    status, _ = GlobalDBHandler().hard_reset_assets_list(database)
    assert status is False
    # Now do it without the trade
    database.delete_trade(trade.identifier)
    status, msg = GlobalDBHandler().hard_reset_assets_list(database, True)
    assert status, msg
    cursor = globaldb._conn.cursor()
    query = f'SELECT COUNT(*) FROM ethereum_tokens where address == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (0, ), 'Ethereum token should have been deleted'
    query = f'SELECT COUNT(*) FROM assets where details_reference == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (
        0, ), 'Ethereum token should have been deleted from assets'
    query = f'SELECT COUNT(*) FROM ethereum_tokens where address == "{with_underlying_address}";'
    r = cursor.execute(query)
    assert r.fetchone() == (
        0,
    ), 'Token with underlying token should have been deleted from assets'
    query = f'SELECT COUNT(*) FROM assets where details_reference == "{with_underlying_address}";'
    r = cursor.execute(query)
    assert r.fetchone() == (0, )
    query = f'SELECT COUNT(*) FROM underlying_tokens_list where address == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (0, )
    query = 'SELECT COUNT(*) FROM assets where identifier == "1";'
    r = cursor.execute(query)
    assert r.fetchone() == (0, ), 'Non ethereum token should be deleted'
    # Check that the user database is correctly updated
    query = 'SELECT identifier from assets'
    r = cursor.execute(query)
    user_db_cursor = database.conn.cursor()
    user_db_cursor.execute(query)
    assert r.fetchall() == user_db_cursor.fetchall()

    # Check that the number of assets is the expected
    root_dir = Path(__file__).resolve().parent.parent.parent
    builtin_database = root_dir / 'data' / 'global.db'
    conn = sqlite3.connect(builtin_database)
    cursor_clean_db = conn.cursor()
    tokens_expected = cursor_clean_db.execute('SELECT COUNT(*) FROM assets;')
    tokens_local = cursor.execute('SELECT COUNT(*) FROM assets;')
    assert tokens_expected.fetchone() == tokens_local.fetchone()
    cursor.execute('SELECT asset_id FROM user_owned_assets')
    msg = 'asset id in trade should not be in the owned table'
    assert "'2'" not in [entry[0] for entry in cursor.fetchall()], msg
    conn.close()
예제 #7
0
def test_blockchain_accounts_endpoint_errors(rotkehlchen_api_server, api_port,
                                             method):
    """
    Test /api/(version)/blockchains/(name) for edge cases and errors.

    Test for errors when both adding and removing a blockhain account. Both put/delete
    """
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    rotki.blockchain.cache_ttl_secs = 0

    # Provide unsupported blockchain name
    account = '0x00d74c25bbf93df8b2a41d82b0076843b4db0349'
    checksummed_account = to_checksum_address(account)
    data = {'accounts': [account]}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='DDASDAS'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Unrecognized value DDASDAS given for blockchain name',
    )

    # Provide no blockchain name
    response = getattr(requests, method)(
        f'http://localhost:{api_port}/api/1/blockchains',
        json=data,
    )
    assert_error_response(
        response=response,
        status_code=HTTPStatus.NOT_FOUND,
    )

    # Do not provide accounts
    data = {'dsadsad': 'foo'}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Missing data for required field',
    )

    # Provide wrong type of account
    data = {'accounts': 'foo'}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='is not a valid ETH address',
    )
    assert 'foo' not in rotki.blockchain.accounts.eth

    # Provide empty list
    data = {'accounts': []}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Empty list of blockchain accounts to add was given',
    )

    # Provide invalid ETH account (more byes)
    data = {'accounts': ['0x554FFc77f4251a9fB3c0E3590a6a205f8d4e067d01']}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    msg = 'string 0x554FFc77f4251a9fB3c0E3590a6a205f8d4e067d01 is not a valid ETH address'
    assert_error_response(
        response=response,
        contained_in_msg=msg,
    )

    # Provide invalid BTC account
    data = {'accounts': ['18ddjB7HWTaxzvTbLp1nWvaixU3U2oTZ1']}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='BTC'),
        json=data,
    )
    # Since validation depends on the querying of the balance for BTC the error only
    # is seen at addition and at removal another error is seen. But that's okay
    if method == 'put':
        msg = 'string 18ddjB7HWTaxzvTbLp1nWvaixU3U2oTZ1 is not a valid BTC address'
    else:
        msg = 'Tried to remove a non existing BTC account'
    assert_error_response(
        response=response,
        contained_in_msg=msg,
    )
    assert_msg = 'Invalid BTC account should not have been added'
    assert '18ddjB7HWTaxzvTbLp1nWvaixU3U2oTZ1' not in rotki.blockchain.accounts.btc, assert_msg

    # Provide not existing but valid ETH account for removal
    data = {'accounts': [make_ethereum_address()]}
    response = requests.delete(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to remove a non existing ETH account',
    )

    # Provide not existing but valid BTC account for removal
    data = {'accounts': ['18ddjB7HWTVxzvTbLp1nWvaBxU3U2oTZF2']}
    response = requests.delete(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='BTC'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Tried to remove a non existing BTC account',
    )

    # Provide list with one valid and one invalid account and make sure that the
    # valid one is added/removed but we get an error for the invalid one
    if method == 'delete':
        # Account should be an existing account
        account = rotki.blockchain.accounts.eth[0]
    # else keep the new account to add

    data = {'accounts': ['142', account]}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_proper_response(response=response)
    assert '142 is not a valid ETH address' in response.json()['message']
    if method == 'put':
        assert checksummed_account in rotki.blockchain.accounts.eth
    else:
        assert checksummed_account not in rotki.blockchain.accounts.eth

    # Provide invalid type for accounts
    data = {'accounts': [15]}
    response = getattr(requests, method)(
        api_url_for(rotkehlchen_api_server,
                    "blockchainsaccountsresource",
                    blockchain='ETH'),
        json=data,
    )
    assert_error_response(
        response=response,
        contained_in_msg='Not a valid string',
    )
예제 #8
0
        for key in keys:
            msg = f'Unexpected data for event with idx "{idx}" and key "{key}" of {name} vault'
            assert s[key] == result['events'][idx][key], msg
        assert FVal(s['from_value']['amount']) == FVal(
            result['events'][idx]['from_value']['amount'])  # noqa: E501
        assert FVal(s['to_value']['amount']) == FVal(
            result['events'][idx]['to_value']['amount'])  # noqa: E501
        if s['realized_pnl']:
            assert FVal(s['realized_pnl']['amount']) == FVal(
                result['events'][idx]['realized_pnl']['amount'])  # noqa: E501


# Try with 2 addresses to make sure that if an address does not have yearn vault history
# nothing breaks
@pytest.mark.parametrize('ethereum_accounts',
                         [[TEST_ACC1, make_ethereum_address()]])
@pytest.mark.parametrize('ethereum_modules', [['yearn_vaults']])
@pytest.mark.parametrize('should_mock_current_price_queries', [True])
@pytest.mark.parametrize('should_mock_price_queries', [True])
@pytest.mark.parametrize('default_mock_price_value', [FVal(1)])
@pytest.mark.parametrize('start_with_valid_premium', [True])
@pytest.mark.parametrize(  # Force infura to make sure one of our history tests work with web3
    'ethrpc_endpoint,ethereum_manager_connect_at_start',
    [(
        INFURA_TEST,
        (NodeName.OWN, ),
    )],
)
def test_query_yearn_vault_history(rotkehlchen_api_server, ethereum_accounts):
    """Check querying the yearn vaults history endpoint works. Uses real data."""
    async_query = random.choice([True, False])
예제 #9
0
def test_deleting_custom_tokens(rotkehlchen_api_server):
    """Test that the endpoint for deleting a custom ethereum token works"""
    token0_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[0].address
    token1_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[1].address
    underlying1_id = ETHEREUM_DIRECTIVE + underlying_address1
    underlying2_id = ETHEREUM_DIRECTIVE + underlying_address2
    underlying3_id = ETHEREUM_DIRECTIVE + underlying_address3
    cursor = GlobalDBHandler()._conn.cursor()
    # Make sure the equivalent assets we will delete exist in the DB
    result = cursor.execute(
        'SELECT COUNT(*) from assets WHERE identifier IN (?, ?, ?, ?, ?)',
        (token0_id, token1_id, underlying1_id, underlying2_id, underlying3_id),
    ).fetchone()[0]
    assert result == 5
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': INITIAL_TOKENS[1].address},
    )
    result = assert_proper_response_with_result(response)
    assert result == {'identifier': token1_id}

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ), )
    result = assert_proper_response_with_result(response)
    expected_tokens = INITIAL_EXPECTED_TOKENS[:-1]
    expected_result = [x.serialize() for x in expected_tokens]
    assert_token_entry_exists_in_result(result, expected_result)
    # also check the mapping for the underlying still tokens exists
    result = cursor.execute(
        'SELECT COUNT(*) from underlying_tokens_list').fetchone()[0]
    assert result == 3

    # test that deleting a non existing address is handled properly
    non_existing_address = make_ethereum_address()
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': non_existing_address},
    )
    expected_msg = (
        f'Tried to delete ethereum token with address {non_existing_address} '
        f'but it was not found in the DB', )
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # test that trying to delete an underlying token that exists in a mapping
    # of another token is handled correctly
    non_existing_address = make_ethereum_address()
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': underlying_address1},
    )
    expected_msg = (
        f'Tried to delete ethereum token with address {underlying_address1} '
        f'but its deletion would violate a constraint so deletion failed')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # Check that the initial token of the test has MKR as swapped for token
    # this is just a sanity check as the fixture initialization should take care of it
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': INITIAL_TOKENS[0].address},
    )
    result = assert_proper_response_with_result(response)
    assert result['swapped_for'] == 'MKR'

    # test that trying to delete a token (MKR) that is used as swapped_for
    # of another token is handled correctly
    non_existing_address = make_ethereum_address()
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': A_MKR.ethereum_address},
    )
    expected_msg = (
        f'Tried to delete ethereum token with address {A_MKR.ethereum_address} '
        f'but its deletion would violate a constraint so deletion failed')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # now test that deleting the token with underlying tokens works
    response = requests.delete(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'address': INITIAL_TOKENS[0].address},
    )
    result = assert_proper_response_with_result(response)
    assert result == {'identifier': token0_id}
    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ), )
    result = assert_proper_response_with_result(response)
    expected_tokens = INITIAL_EXPECTED_TOKENS[1:-1]
    expected_result = [x.serialize() for x in expected_tokens]
    assert_token_entry_exists_in_result(result, expected_result)
    # and removes the mapping of all underlying tokens
    result = cursor.execute(
        'SELECT COUNT(*) from underlying_tokens_list').fetchone()[0]
    assert result == 0
    # and that the equivalent asset entries were also deleted
    result = cursor.execute(
        'SELECT COUNT(*) from assets WHERE identifier IN (?, ?)',
        (token0_id, token1_id),
    ).fetchone()[0]
    assert result == 0
예제 #10
0
def test_exporting_custom_assets_list(rotkehlchen_api_server, globaldb,
                                      with_custom_path):
    """Test that the endpoint for exporting custom assets works correctly"""
    eth_address = make_ethereum_address()
    identifier = ethaddress_to_identifier(eth_address)
    globaldb.add_asset(
        asset_id=identifier,
        asset_type=AssetType.ETHEREUM_TOKEN,
        data=EthereumToken.initialize(
            address=eth_address,
            decimals=18,
            name='yabirtoken',
            symbol='YAB',
            coingecko='YAB',
            cryptocompare='YAB',
        ),
    )
    with tempfile.TemporaryDirectory() as path:
        if with_custom_path:
            response = requests.put(
                api_url_for(
                    rotkehlchen_api_server,
                    'userassetsresource',
                ),
                json={
                    'action': 'download',
                    'destination': path
                },
            )
        else:
            response = requests.put(
                api_url_for(
                    rotkehlchen_api_server,
                    'userassetsresource',
                ),
                json={'action': 'download'},
            )

        if with_custom_path:
            result = assert_proper_response_with_result(response)
            if with_custom_path:
                assert path in result['file']
            zip_file = ZipFile(result['file'])
            data = json.loads(zip_file.read('assets.json'))
            assert int(data['version']) == GLOBAL_DB_VERSION
            assert len(data['assets']) == 1
            assert data['assets'][0] == {
                'identifier': identifier,
                'name': 'yabirtoken',
                'decimals': 18,
                'symbol': 'YAB',
                'asset_type': 'ethereum token',
                'started': None,
                'forked': None,
                'swapped_for': None,
                'cryptocompare': 'YAB',
                'coingecko': 'YAB',
                'protocol': None,
                'underlying_tokens': None,
                'ethereum_address': eth_address,
            }
        else:
            assert response.status_code == HTTPStatus.OK
            assert response.headers['Content-Type'] == 'application/zip'

        # try to download again to see if the database is properly detached
        response = requests.put(
            api_url_for(
                rotkehlchen_api_server,
                'userassetsresource',
            ),
            json={
                'action': 'download',
                'destination': path
            },
        )
        result = assert_proper_response_with_result(response)
예제 #11
0
def test_editing_custom_tokens(rotkehlchen_api_server):
    """Test that the endpoint for editing a custom ethereum token works"""
    new_token1 = INITIAL_TOKENS[0].serialize_all_info()
    del new_token1['identifier']
    new_name = 'Edited token'
    new_symbol = 'ESMBL'
    new_protocol = 'curve'
    new_swapped_for = A_BAT.identifier
    new_token1['name'] = new_name
    new_token1['symbol'] = new_symbol
    new_token1['swapped_for'] = new_swapped_for
    new_token1['protocol'] = new_protocol
    response = requests.patch(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': new_token1},
    )
    result = assert_proper_response_with_result(response)
    token0_id = ETHEREUM_DIRECTIVE + INITIAL_TOKENS[0].ethereum_address
    assert result == {'identifier': token0_id}

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
    )
    result = assert_proper_response_with_result(response)
    expected_tokens = deepcopy(INITIAL_EXPECTED_TOKENS)
    object.__setattr__(expected_tokens[0], 'name', new_name)
    object.__setattr__(expected_tokens[0], 'symbol', new_symbol)
    object.__setattr__(expected_tokens[0], 'protocol', new_protocol)
    object.__setattr__(expected_tokens[0], 'swapped_for', A_BAT)
    expected_result = [x.serialize_all_info() for x in expected_tokens]
    assert_token_entry_exists_in_result(result, expected_result)

    # test that editing an non existing address is handled properly
    non_existing_token = INITIAL_TOKENS[0].serialize_all_info()
    del non_existing_token['identifier']
    non_existing_address = make_ethereum_address()
    non_existing_token['address'] = non_existing_address
    response = requests.patch(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': non_existing_token},
    )
    expected_msg = (
        f'Tried to edit non existing ethereum token with '
        f'address {non_existing_address}',
    )
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # test that editing with an invalid coingecko identifier is handled
    bad_token = new_token1.copy()
    bad_identifier = 'INVALIDID'
    bad_token['coingecko'] = bad_identifier
    response = requests.patch(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': bad_token},
    )
    assert_error_response(
        response=response,
        contained_in_msg=f'Given coingecko identifier {bad_identifier} is not valid',
        status_code=HTTPStatus.BAD_REQUEST,
    )

    # test that editing with an invalid cryptocompare identifier is handled
    bad_token = new_token1.copy()
    bad_identifier = 'INVALIDID'
    bad_token['cryptocompare'] = bad_identifier
    response = requests.patch(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': bad_token},
    )
    assert_error_response(
        response=response,
        contained_in_msg=f'Given cryptocompare identifier {bad_identifier} isnt valid',
        status_code=HTTPStatus.BAD_REQUEST,
    )
예제 #12
0
def fixture_ethereum_accounts(number_of_eth_accounts) -> List[ChecksumEthAddress]:
    return [make_ethereum_address() for x in range(number_of_eth_accounts)]
예제 #13
0
def test_add_and_get_yearn_vault_events(data_dir, username):
    """Test that get yearn vault events works fine and returns only events for what we need"""
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)

    addr1 = make_ethereum_address()
    addr1_events = [YearnVaultEvent(
        event_type='deposit',
        from_asset=A_DAI,
        from_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        to_asset=Asset('yDAI'),
        to_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        realized_pnl=None,
        block_number=1,
        timestamp=Timestamp(1),
        tx_hash='0x01653e88600a6492ad6e9ae2af415c990e623479057e4e93b163e65cfb2d4436',
        log_index=1,
    ), YearnVaultEvent(
        event_type='withdraw',
        from_asset=Asset('yDAI'),
        from_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        to_asset=A_DAI,
        to_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        realized_pnl=Balance(amount=FVal('0.01'), usd_value=FVal('0.01')),
        block_number=2,
        timestamp=Timestamp(2),
        tx_hash='0x4147da3e5d3c0565a99192ce0b32182ab30b8e1067921d9b2a8ef3bd60b7e2ce',
        log_index=2,
    )]
    data.db.add_yearn_vaults_events(address=addr1, events=addr1_events)
    addr2 = make_ethereum_address()
    addr2_events = [YearnVaultEvent(
        event_type='deposit',
        from_asset=A_DAI,
        from_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        to_asset=Asset('yDAI'),
        to_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        realized_pnl=None,
        block_number=1,
        timestamp=Timestamp(1),
        tx_hash='0x8c094d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55',
        log_index=1,
    ), YearnVaultEvent(
        event_type='withdraw',
        from_asset=Asset('yDAI'),
        from_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        to_asset=A_DAI,
        to_value=Balance(amount=FVal(1), usd_value=FVal(1)),
        realized_pnl=Balance(amount=FVal('0.01'), usd_value=FVal('0.01')),
        block_number=2,
        timestamp=Timestamp(2),
        tx_hash='0x58c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=2,
    )]
    data.db.add_yearn_vaults_events(address=addr2, events=addr2_events)

    events = data.db.get_yearn_vaults_events(address=addr1, vault=YEARN_VAULTS['yDAI'])
    assert events == addr1_events
    events = data.db.get_yearn_vaults_events(address=addr2, vault=YEARN_VAULTS['yDAI'])
    assert events == addr2_events
예제 #14
0
def test_add_and_get_aave_events(data_dir, username):
    """Test that get aave events works fine and returns only events for what we need"""
    msg_aggregator = MessagesAggregator()
    data = DataHandler(data_dir, msg_aggregator)
    data.unlock(username, '123', create_new=True)

    addr1 = make_ethereum_address()
    addr1_events = [AaveDepositWithdrawalEvent(
        event_type='deposit',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=1,
        timestamp=Timestamp(1),
        tx_hash='0x01653e88600a6492ad6e9ae2af415c990e623479057e4e93b163e65cfb2d4436',
        log_index=1,
    ), AaveDepositWithdrawalEvent(
        event_type='withdrawal',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=2,
        timestamp=Timestamp(2),
        tx_hash='0x4147da3e5d3c0565a99192ce0b32182ab30b8e1067921d9b2a8ef3bd60b7e2ce',
        log_index=2,
    )]
    data.db.add_aave_events(address=addr1, events=addr1_events)

    addr2 = make_ethereum_address()
    addr2_events = [AaveDepositWithdrawalEvent(
        event_type='deposit',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=1,
        timestamp=Timestamp(1),
        tx_hash='0x8c094d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55',
        log_index=1,
    ), AaveDepositWithdrawalEvent(
        event_type='withdrawal',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=2,
        timestamp=Timestamp(2),
        tx_hash='0x58c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=2,
    )]
    data.db.add_aave_events(address=addr2, events=addr2_events)

    # addr3 has all types of aave events so we test serialization/deserialization
    addr3 = make_ethereum_address()
    addr3_events = [AaveDepositWithdrawalEvent(
        event_type='deposit',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=1,
        timestamp=Timestamp(1),
        tx_hash='0x9e394d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55',
        log_index=1,
    ), AaveDepositWithdrawalEvent(
        event_type='withdrawal',
        asset=A_DAI,
        atoken=A_ADAI_V1,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=2,
        timestamp=Timestamp(2),
        tx_hash='0x4c167445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=2,
    ), AaveInterestEvent(
        event_type='interest',
        asset=A_WBTC,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=4,
        timestamp=Timestamp(4),
        tx_hash='0x49c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=4,
    ), AaveBorrowEvent(
        event_type='borrow',
        asset=A_ETH,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=5,
        timestamp=Timestamp(5),
        tx_hash='0x19c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=5,
        borrow_rate_mode='stable',
        borrow_rate=FVal('0.05233232323423432'),
        accrued_borrow_interest=FVal('5.112234'),
    ), AaveRepayEvent(
        event_type='repay',
        asset=A_MANA,
        value=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=6,
        timestamp=Timestamp(6),
        tx_hash='0x29c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
        log_index=6,
        fee=Balance(amount=FVal('0.1'), usd_value=FVal('0.1')),
    ), AaveLiquidationEvent(
        event_type='liquidation',
        collateral_asset=A_ETH,
        collateral_balance=Balance(amount=FVal(1), usd_value=FVal(1)),
        principal_asset=A_ETH,
        principal_balance=Balance(amount=FVal(1), usd_value=FVal(1)),
        block_number=7,
        log_index=7,
        timestamp=Timestamp(7),
        tx_hash='0x39c67445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de',
    )]
    data.db.add_aave_events(address=addr3, events=addr3_events)

    events = data.db.get_aave_events(address=addr1, atoken=A_ADAI_V1)
    assert events == addr1_events
    events = data.db.get_aave_events(address=addr2, atoken=A_ADAI_V1)
    assert events == addr2_events
    events = data.db.get_aave_events(address=addr3)
    assert events == addr3_events

    # check that all aave events are properly hashable (aka can go in a set)
    test_set = set()
    for event in addr3_events:
        test_set.add(event)
    assert len(test_set) == len(addr3_events)
예제 #15
0
def mock_etherscan_for_dsr(
    etherscan: Etherscan,
    account1: ChecksumEthAddress,
    account2: ChecksumEthAddress,
    original_requests_get,
    params: DSRMockParameters,
) -> _patch:

    proxy1 = make_ethereum_address()
    proxy2 = make_ethereum_address()
    account1_join1_event = f"""{{"address": "{MAKERDAO_POT_ADDRESS}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501

    account1_join1_deposit = params.account1_join1_normalized_balance * params.account1_join1_chi
    account1_join1_move_event = f"""{{"address": "{MAKERDAO_VAT_ADDRESS}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT_ADDRESS}", "{int_to_32byteshexstr(account1_join1_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_join2_event = f"""{{"address": "{MAKERDAO_POT_ADDRESS}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join2_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join2_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_join2_deposit = params.account1_join2_normalized_balance * params.account1_join2_chi
    account1_join2_move_event = f"""{{"address": "{MAKERDAO_VAT_ADDRESS}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT_ADDRESS}", "{int_to_32byteshexstr(account1_join2_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501

    account1_exit1_event = f"""{{"address": "{MAKERDAO_POT_ADDRESS}", "topics": ["0x7f8661a100000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_exit1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_exit1_withdrawal = (params.account1_exit1_normalized_balance *
                                 params.account1_exit1_chi)
    account1_exit1_move_event = f"""{{"address": "{MAKERDAO_VAT_ADDRESS}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{MAKERDAO_POT_ADDRESS}", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(account1_exit1_withdrawal)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501
    account2_join1_event = f"""{{"address": "{MAKERDAO_POT_ADDRESS}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{int_to_32byteshexstr(params.account2_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0xwedontcare", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontacre", "transactionIndex": "0x79"}}"""  # noqa: E501

    account2_join1_deposit = params.account2_join1_normalized_balance * params.account2_join1_chi
    account2_join1_move_event = f"""{{"address": "{MAKERDAO_VAT_ADDRESS}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{MAKERDAO_POT_ADDRESS}", "{int_to_32byteshexstr(account2_join1_deposit)}"], "data": "0xwedontcare", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "dontcare", "gasUsed": "dontcare", "logIndex": "0x6c", "transactionHash": "dontcare", "transactionIndex": "0x79"}}"""  # noqa: E501

    def mock_requests_get(url, *args, **kwargs):
        if 'etherscan.io/api?module=proxy&action=eth_blockNumber' in url:
            response = f'{{"status":"1","message":"OK","result":"{TEST_LATEST_BLOCKNUMBER_HEX}"}}'

        elif 'etherscan.io/api?module=proxy&action=eth_call' in url:
            to_address = url.split(
                'https://api.etherscan.io/api?module=proxy&action=eth_call&to=',
            )[1][:42]
            input_data = url.split('data=')[1].split('&apikey')[0]
            if to_address == MAKERDAO_PROXY_REGISTRY_ADDRESS:
                if not input_data.startswith('0xc4552791'):
                    raise AssertionError(
                        'Call to unexpected method of DSR ProxyRegistry during tests',
                    )

                # It's a call to proxy registry. Return the mapping
                if account1[2:].lower() in input_data:
                    proxy_account = address_to_32byteshexstr(proxy1)
                elif account2[2:].lower() in input_data:
                    proxy_account = address_to_32byteshexstr(proxy2)
                else:
                    proxy_account = '0x' + '0' * 64
                response = f'{{"status":"1","message":"OK","result":"{proxy_account}"}}'
            elif to_address == MAKERDAO_POT_ADDRESS:
                if input_data.startswith('0x0bebac86'):  # pie
                    if proxy1[2:].lower() in input_data:
                        result = int_to_32byteshexstr(
                            params.account1_current_normalized_balance)
                    elif proxy2[2:].lower() in input_data:
                        result = int_to_32byteshexstr(
                            params.account2_current_normalized_balance)
                    else:
                        # result = int_to_32byteshexstr(0)
                        raise AssertionError(
                            'Pie call for unexpected account during tests')
                elif input_data.startswith('0xc92aecc4'):  # chi
                    result = int_to_32byteshexstr(params.current_chi)
                elif input_data.startswith('0x487bf082'):  # dsr
                    result = int_to_32byteshexstr(params.current_dsr)
                else:
                    raise AssertionError(
                        'Call to unexpected method of MakerDAO pot during tests',
                    )

                response = f'{{"status":"1","message":"OK","result":"{result}"}}'

            else:
                raise AssertionError(
                    f'Etherscan call to unknown contract {to_address} during tests',
                )
        elif 'etherscan.io/api?module=logs&action=getLogs' in url:
            contract_address = url.split('&address=')[1].split('&topic0')[0]
            topic0 = url.split('&topic0=')[1].split('&topic0_1')[0]
            topic1 = url.split('&topic1=')[1].split('&topic1_2')[0]
            topic2 = None
            if '&topic2=' in url:
                topic2 = url.split('&topic2=')[1].split('&')[0]
            from_block = int(url.split('&fromBlock=')[1].split('&')[0])
            to_block = int(url.split('&toBlock=')[1].split('&')[0])

            if contract_address == MAKERDAO_POT_ADDRESS:
                if topic0.startswith('0x049878f3'):  # join

                    events = []
                    if proxy1[2:].lower() in topic1:
                        if from_block <= params.account1_join1_blocknumber <= to_block:
                            events.append(account1_join1_event)
                        if from_block <= params.account1_join2_blocknumber <= to_block:
                            events.append(account1_join2_event)
                    elif proxy2[2:].lower() in topic1:
                        if from_block <= params.account2_join1_blocknumber <= to_block:
                            events.append(account2_join1_event)
                    else:
                        raise AssertionError(
                            f'Etherscan log query to makerdao POT contract for '
                            f'join for unknown account {topic1}', )
                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'

                elif topic0.startswith('0x7f8661a1'):  # exit
                    events = []
                    if proxy1[2:].lower() in topic1:
                        if from_block <= params.account1_exit1_blocknumber <= to_block:
                            events.append(account1_exit1_event)

                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
                else:
                    raise AssertionError(
                        'Etherscan unknown log query to makerdao POT contract')

            elif contract_address == MAKERDAO_VAT_ADDRESS:
                if topic0.startswith('0xbb35783b'):  # move
                    events = []
                    if proxy1[2:].lower() in topic1:  # deposit from acc1
                        if from_block <= params.account1_join1_blocknumber <= to_block:
                            events.append(account1_join1_move_event)
                        if from_block <= params.account1_join2_blocknumber <= to_block:
                            events.append(account1_join2_move_event)
                    elif proxy2[2:].lower() in topic1:  # deposit from acc2
                        if from_block <= params.account2_join1_blocknumber <= to_block:
                            events.append(account2_join1_move_event)
                    elif proxy1[2:].lower() in topic2:  # withdrawal from acc1

                        if from_block <= params.account1_exit1_blocknumber <= to_block:
                            events.append(account1_exit1_move_event)

                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
                else:
                    raise AssertionError(
                        'Etherscan unknown log query to makerdao VAT contract')
            else:
                raise AssertionError(
                    f'Etherscan getLogs call to unknown contract {contract_address} during tests',
                )
        else:
            return original_requests_get(url, *args, **kwargs)

        return MockResponse(200, response)

    return patch.object(etherscan.session, 'get', wraps=mock_requests_get)
예제 #16
0
def test_adding_custom_tokens(rotkehlchen_api_server):
    """Test that the endpoint for adding a custom ethereum token works"""
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': CUSTOM_TOKEN3.serialize()},
    )
    result = assert_proper_response_with_result(response)
    assert result == {'identifier': ETHEREUM_DIRECTIVE + CUSTOM_TOKEN3.address}

    response = requests.get(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ), )
    result = assert_proper_response_with_result(response)
    expected_tokens = INITIAL_EXPECTED_TOKENS.copy() + [
        CUSTOM_TOKEN3,
        CustomEthereumToken(address=underlying_address4),
    ]
    expected_result = [x.serialize() for x in expected_tokens]
    assert_token_entry_exists_in_result(result, expected_result)

    # test that adding an already existing address is handled properly
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': INITIAL_TOKENS[1].serialize()},
    )
    expected_msg = (
        f'Ethereum token with address {INITIAL_TOKENS[1].address} already '
        f'exists in the DB', )
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.CONFLICT,
    )

    # also test that the addition of underlying tokens has created proper asset entires for them
    cursor = GlobalDBHandler()._conn.cursor()
    result = cursor.execute(
        'SELECT COUNT(*) from assets WHERE identifier IN (?, ?, ?, ?)',
        [
            ETHEREUM_DIRECTIVE + x for x in [
                underlying_address1, underlying_address2, underlying_address3,
                underlying_address4
            ]
        ],  # noqa: E501
    ).fetchone()[0]
    assert result == 4
    result = cursor.execute(
        'SELECT COUNT(*) from ethereum_tokens WHERE address IN (?, ?, ?, ?)',
        (underlying_address1, underlying_address2, underlying_address3,
         underlying_address4),  # noqa: E501
    ).fetchone()[0]
    assert result == 4

    # now test that adding a token with underlying tokens adding up to more than 100% is caught
    bad_token = CustomEthereumToken(
        address=make_ethereum_address(),
        decimals=18,
        name='foo',
        symbol='BBB',
        underlying_tokens=[
            UnderlyingToken(address=make_ethereum_address(),
                            weight=FVal('0.5055')),
            UnderlyingToken(address=make_ethereum_address(),
                            weight=FVal('0.7055')),
        ],
    )
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': bad_token.serialize()},
    )
    expected_msg = (
        f'The sum of underlying token weights for {bad_token.address} is '
        f'121.1000 and exceeds 100%')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # and test that adding a token with underlying tokens adding up to less than 100% is caught
    bad_token = CustomEthereumToken(
        address=make_ethereum_address(),
        decimals=18,
        name='foo',
        symbol='BBB',
        underlying_tokens=[
            UnderlyingToken(address=make_ethereum_address(),
                            weight=FVal('0.1055')),
            UnderlyingToken(address=make_ethereum_address(),
                            weight=FVal('0.2055')),
        ],
    )
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': bad_token.serialize()},
    )
    expected_msg = (
        f'The sum of underlying token weights for {bad_token.address} is '
        f'31.1000 and does not add up to 100%')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.BAD_REQUEST,
    )
    # and test that adding a token with empty list of underlying tokens and not null is an error
    bad_token = CustomEthereumToken(
        address=make_ethereum_address(),
        decimals=18,
        name='foo',
        symbol='BBB',
        underlying_tokens=[],
    )
    serialized_bad_token = bad_token.serialize()
    serialized_bad_token['underlying_tokens'] = []
    response = requests.put(
        api_url_for(
            rotkehlchen_api_server,
            'ethereumassetsresource',
        ),
        json={'token': serialized_bad_token},
    )
    expected_msg = (
        f'Gave an empty list for underlying tokens of {bad_token.address}')
    assert_error_response(
        response=response,
        contained_in_msg=expected_msg,
        status_code=HTTPStatus.BAD_REQUEST,
    )
예제 #17
0
def test_eth2_deposits_serialization():
    addr1 = make_ethereum_address()
    addr2 = make_ethereum_address()
    deposits = [
        Eth2Deposit(
            from_address=addr1,
            pubkey=
            '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b',  # noqa: E501
            withdrawal_credentials=
            '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499',  # noqa: E501
            value=Balance(FVal(32), FVal(64)),
            tx_hash=deserialize_evm_tx_hash(
                '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1'
            ),  # noqa: E501
            tx_index=22,
            timestamp=Timestamp(int(1604506685)),
        ),
        Eth2Deposit(
            from_address=addr2,
            pubkey=
            '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3',  # noqa: E501
            withdrawal_credentials=
            '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817',  # noqa: E501
            value=Balance(FVal(32), FVal(64)),
            tx_hash=deserialize_evm_tx_hash(
                '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7'
            ),  # noqa: E501
            tx_index=221,
            timestamp=Timestamp(int(1605043544)),
        ),
    ]

    serialized = process_result_list(deposits)
    assert serialized == [
        {
            'from_address': addr1,
            'pubkey':
            '0xb016e31f633a21fbe42a015152399361184f1e2c0803d89823c224994af74a561c4ad8cfc94b18781d589d03e952cd5b',  # noqa: E501
            'withdrawal_credentials':
            '0x004c7691c2085648f394ffaef851f3b1d51b95f7263114bc923fc5338f5fc499',  # noqa: E501
            'value': {
                'amount': '32',
                'usd_value': '64'
            },
            'tx_hash':
            '0xd9eca1c2a0c5ff2f25071713432b21cc4d0ff2e8963edc63a48478e395e08db1',
            'tx_index': 22,
            'timestamp': 1604506685,
        },
        {
            'from_address': addr2,
            'pubkey':
            '0xa8ff5fc88412d080a297683c25a791ef77eb52d75b265fabab1f2c2591bb927c35818ac6289bc6680ab252787d0ebab3',  # noqa: E501
            'withdrawal_credentials':
            '0x00cfe1c10347d642a8b8daf86d23bcb368076972691445de2cf517ff43765817',  # noqa: E501
            'value': {
                'amount': '32',
                'usd_value': '64'
            },
            'tx_hash':
            '0x6905f4d1843fb8c003c1fbbc2c8e6c5f9792f4f44ddb1122553412ee0b128da7',
            'tx_index': 221,
            'timestamp': 1605043544,
        },
    ]
예제 #18
0
def mock_etherscan_for_dsr(
    etherscan: Etherscan,
    account1: ChecksumEthAddress,
    account2: ChecksumEthAddress,
    account3: ChecksumEthAddress,
    original_requests_get,
    params: DSRMockParameters,
) -> _patch:

    proxy1 = make_ethereum_address()
    proxy2 = make_ethereum_address()
    targets = [
        address_to_32byteshexstr(proxy1),
        address_to_32byteshexstr(proxy2),
        '0x0000000000000000000000000000000000000000000000000000000000000000'
    ]  # noqa: E501

    sorted_accounts = sorted(zip([account1, account2, account3], targets),
                             key=lambda x: x[0])
    proxies = [y[1] for y in sorted_accounts]

    account1_join1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0x1", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501

    account1_join1_deposit = params.account1_join1_normalized_balance * params.account1_join1_chi
    account1_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join1_deposit // 10 ** 27)}"], "data": "0x1", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_join2_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_join2_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0x1", "blockNumber": "{hex(params.account1_join2_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_join2_deposit = params.account1_join2_normalized_balance * params.account1_join2_chi
    account1_join2_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account1_join2_deposit // 10 ** 27)}"], "data": "0x1", "blockNumber": "{hex(params.account1_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_join2_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501

    account1_exit1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x7f8661a100000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(params.account1_exit1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0x1", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501
    account1_exit1_withdrawal = (params.account1_exit1_normalized_balance *
                                 params.account1_exit1_chi)
    account1_exit1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{MAKERDAO_POT.address}", "{address_to_32byteshexstr(proxy1)}", "{int_to_32byteshexstr(account1_exit1_withdrawal // 10 ** 27)}"], "data": "0x1", "blockNumber": "{hex(params.account1_exit1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account1_exit1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501
    account2_join1_event = f"""{{"address": "{MAKERDAO_POT.address}", "topics": ["0x049878f300000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{int_to_32byteshexstr(params.account2_join1_normalized_balance)}", "0x0000000000000000000000000000000000000000000000000000000000000000"], "data": "0x1", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0xf00", "transactionIndex": "0x79"}}"""  # noqa: E501

    account2_join1_deposit = params.account2_join1_normalized_balance * params.account2_join1_chi
    account2_join1_move_event = f"""{{"address": "{MAKERDAO_VAT.address}", "topics": ["0xbb35783b00000000000000000000000000000000000000000000000000000000", "{address_to_32byteshexstr(proxy2)}", "{MAKERDAO_POT.address}", "{int_to_32byteshexstr(account2_join1_deposit // 10 ** 27)}"], "data": "0x1", "blockNumber": "{hex(params.account2_join1_blocknumber)}", "timeStamp": "{hex(blocknumber_to_timestamp(params.account2_join1_blocknumber))}", "gasPrice": "0x1", "gasUsed": "0x1", "logIndex": "0x6c", "transactionHash": "0fx00", "transactionIndex": "0x79"}}"""  # noqa: E501

    # Not sure how to convince pylint that this ChecksumEthAddress IS a subscriptable object
    proxy1_contents = proxy1[2:].lower()  # pylint: disable=unsubscriptable-object
    proxy2_contents = proxy2[2:].lower()  # pylint: disable=unsubscriptable-object

    def mock_requests_get(url, *args, **kwargs):
        if 'etherscan.io/api?module=proxy&action=eth_blockNumber' in url:
            response = f'{{"status":"1","message":"OK","result":"{TEST_LATEST_BLOCKNUMBER_HEX}"}}'
        elif 'etherscan.io/api?module=proxy&action=eth_call' in url:
            to_address = url.split(
                'https://api.etherscan.io/api?module=proxy&action=eth_call&to=',
            )[1][:42]
            input_data = url.split('data=')[1].split('&apikey')[0]
            if to_address == DS_PROXY_REGISTRY.address:
                if not input_data.startswith('0xc4552791'):
                    raise AssertionError(
                        'Call to unexpected method of DSR ProxyRegistry during tests',
                    )

                # It's a call to proxy registry. Return the mapping
                if account1[2:].lower() in input_data:
                    proxy_account = address_to_32byteshexstr(proxy1)
                elif account2[2:].lower() in input_data:
                    proxy_account = address_to_32byteshexstr(proxy2)
                else:
                    proxy_account = '0x' + '0' * 64
                response = f'{{"status":"1","message":"OK","result":"{proxy_account}"}}'
            elif to_address == ETH_MULTICALL.address:
                web3 = Web3()
                contract = web3.eth.contract(address=ETH_MULTICALL.address,
                                             abi=ETH_MULTICALL.abi)
                data = url.split('data=')[1]
                if '&apikey' in data:
                    data = data.split('&apikey')[0]

                fn_abi = contract.functions.abi[1]
                output_types = get_abi_output_types(fn_abi)
                args = [1, proxies]
                result = '0x' + web3.codec.encode_abi(output_types, args).hex()
                response = f'{{"status":"1","message":"OK","result":"{result}"}}'
            elif to_address == MAKERDAO_POT.address:
                if input_data.startswith('0x0bebac86'):  # pie
                    if proxy1_contents in input_data:
                        result = int_to_32byteshexstr(
                            params.account1_current_normalized_balance)
                    elif proxy2_contents in input_data:
                        result = int_to_32byteshexstr(
                            params.account2_current_normalized_balance)
                    else:
                        # result = int_to_32byteshexstr(0)
                        raise AssertionError(
                            'Pie call for unexpected account during tests')
                elif input_data.startswith('0xc92aecc4'):  # chi
                    result = int_to_32byteshexstr(params.current_chi)
                elif input_data.startswith('0x487bf082'):  # dsr
                    result = int_to_32byteshexstr(params.current_dsr)
                else:
                    raise AssertionError(
                        'Call to unexpected method of MakerDao pot during tests',
                    )

                response = f'{{"status":"1","message":"OK","result":"{result}"}}'

            else:
                raise AssertionError(
                    f'Etherscan call to unknown contract {to_address} during tests',
                )
        elif 'etherscan.io/api?module=logs&action=getLogs' in url:
            contract_address = url.split('&address=')[1].split('&topic0')[0]
            topic0 = url.split('&topic0=')[1].split('&topic0_1')[0]
            topic1 = url.split('&topic1=')[1].split('&topic1_2')[0]
            topic2 = None
            if '&topic2=' in url:
                topic2 = url.split('&topic2=')[1].split('&')[0]
            from_block = int(url.split('&fromBlock=')[1].split('&')[0])
            to_block = int(url.split('&toBlock=')[1].split('&')[0])

            if contract_address == MAKERDAO_POT.address:
                if topic0.startswith('0x049878f3'):  # join

                    events = []
                    if proxy1_contents in topic1:
                        if from_block <= params.account1_join1_blocknumber <= to_block:
                            events.append(account1_join1_event)
                        if from_block <= params.account1_join2_blocknumber <= to_block:
                            events.append(account1_join2_event)
                    elif proxy2_contents in topic1:
                        if from_block <= params.account2_join1_blocknumber <= to_block:
                            events.append(account2_join1_event)
                    else:
                        raise AssertionError(
                            f'Etherscan log query to makerdao POT contract for '
                            f'join for unknown account {topic1}', )
                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'

                elif topic0.startswith('0x7f8661a1'):  # exit
                    events = []
                    if proxy1_contents in topic1:
                        if from_block <= params.account1_exit1_blocknumber <= to_block:
                            events.append(account1_exit1_event)

                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
                else:
                    raise AssertionError(
                        'Etherscan unknown log query to makerdao POT contract')

            elif contract_address == MAKERDAO_VAT.address:
                if topic0.startswith('0xbb35783b'):  # move
                    events = []
                    if proxy1_contents in topic1:  # deposit from acc1
                        if from_block <= params.account1_join1_blocknumber <= to_block:
                            events.append(account1_join1_move_event)
                        if from_block <= params.account1_join2_blocknumber <= to_block:
                            events.append(account1_join2_move_event)
                    elif proxy2_contents in topic1:  # deposit from acc2
                        if from_block <= params.account2_join1_blocknumber <= to_block:
                            events.append(account2_join1_move_event)
                    elif proxy1_contents in topic2:  # withdrawal from acc1

                        if from_block <= params.account1_exit1_blocknumber <= to_block:
                            events.append(account1_exit1_move_event)

                    response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'
                else:
                    raise AssertionError(
                        'Etherscan unknown log query to makerdao VAT contract')
            elif contract_address == MAKERDAO_DAI_JOIN.address:
                events = []
                if topic0.startswith('0x3b4da69f'):  # join
                    if proxy1_contents in topic1:  # deposit from acc1
                        if from_block <= params.account1_join1_blocknumber <= to_block:
                            events.append(account1_join1_move_event)
                        if from_block <= params.account1_join2_blocknumber <= to_block:
                            events.append(account1_join2_move_event)
                    elif proxy2_contents in topic1:  # deposit from acc2
                        if from_block <= params.account2_join1_blocknumber <= to_block:
                            events.append(account2_join1_move_event)
                elif topic0.startswith('0xef693bed'):  # exit
                    if from_block <= params.account1_exit1_blocknumber <= to_block:
                        events.append(account1_exit1_move_event)
                else:
                    raise AssertionError(
                        'Etherscan unknown call to makerdao DAIJOIN contract')
                response = f'{{"status":"1","message":"OK","result":[{",".join(events)}]}}'

            else:
                raise AssertionError(
                    f'Etherscan getLogs call to unknown contract {contract_address} during tests',
                )
        else:
            return original_requests_get(url, *args, **kwargs)

        return MockResponse(200, response)

    return patch.object(etherscan.session, 'get', wraps=mock_requests_get)
예제 #19
0
def test_global_db_reset(globaldb):
    """
    Check that the user can recreate assets information from the packaged
    database with rotki (soft reset). The test adds a new asset, restores
    the database and checks that the added tokens are still in the database.
    In addition a token is edited and we check that was correctly restored.
    """
    # Add a custom eth token
    address_to_delete = make_ethereum_address()
    token_to_delete = EthereumToken.initialize(
        address=address_to_delete,
        decimals=18,
        name='willdell',
        symbol='DELME',
    )
    globaldb.add_asset(
        asset_id='DELMEID1',
        asset_type=AssetType.ETHEREUM_TOKEN,
        data=token_to_delete,
    )
    # Add a token with underlying token
    with_underlying_address = make_ethereum_address()
    with_underlying = EthereumToken.initialize(
        address=with_underlying_address,
        decimals=18,
        name="Not a scam",
        symbol="NSCM",
        started=0,
        underlying_tokens=[
            UnderlyingToken(
                address=address_to_delete,
                weight=1,
            )
        ],
    )
    globaldb.add_asset(
        asset_id='xDELMEID1',
        asset_type=AssetType.ETHEREUM_TOKEN,
        data=with_underlying,
    )
    # Add asset that is not a token
    globaldb.add_asset(
        asset_id='1',
        asset_type=AssetType.OWN_CHAIN,
        data={
            'name': 'Lolcoin',
            'symbol': 'LOLZ',
            'started': 0,
        },
    )
    # Edit one token
    one_inch_update = EthereumToken.initialize(
        address='0x111111111117dC0aa78b770fA6A738034120C302',
        name='1inch boi',
    )
    GlobalDBHandler().edit_ethereum_token(one_inch_update)

    status, _ = GlobalDBHandler().soft_reset_assets_list()
    assert status
    cursor = globaldb._conn.cursor()
    query = f'SELECT COUNT(*) FROM ethereum_tokens where address == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (
        1, ), 'Custom ethereum tokens should not been deleted'
    query = f'SELECT COUNT(*) FROM assets where details_reference == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (1, )
    query = f'SELECT COUNT(*) FROM ethereum_tokens where address == "{with_underlying_address}";'
    r = cursor.execute(query)
    assert r.fetchone() == (
        1, ), 'Ethereum token with underlying token should not be deleted'
    query = f'SELECT COUNT(*) FROM assets where details_reference == "{with_underlying_address}";'
    r = cursor.execute(query)
    assert r.fetchone() == (1, )
    query = f'SELECT COUNT(*) FROM underlying_tokens_list where address == "{address_to_delete}";'
    r = cursor.execute(query)
    assert r.fetchone() == (1, )
    query = 'SELECT COUNT(*) FROM assets where identifier == "1";'
    r = cursor.execute(query)
    assert r.fetchone() == (
        1, ), 'Non ethereum token added should be in the db'
    # Check that the 1inch token was correctly fixed
    assert EthereumToken(
        '0x111111111117dC0aa78b770fA6A738034120C302').name != '1inch boi'

    # Check that the number of assets is the expected
    root_dir = Path(__file__).resolve().parent.parent.parent
    builtin_database = root_dir / 'data' / 'global.db'
    conn = sqlite3.connect(builtin_database)
    cursor_clean_db = conn.cursor()
    tokens_expected = cursor_clean_db.execute('SELECT COUNT(*) FROM assets;')
    tokens_local = cursor.execute('SELECT COUNT(*) FROM assets;')
    assert tokens_expected.fetchone()[0] + 3 == tokens_local.fetchone()[0]
    conn.close()
예제 #20
0
def test_add_blockchain_accounts_async(
    rotkehlchen_api_server,
    ethereum_accounts,
    btc_accounts,
    number_of_eth_accounts,
):
    """A simpler version of the above test for adding blockchain accounts for async

    The main purpose of this test is to see that querying the endpoint asynchronously also works"""
    # Disable caching of query results
    rotki = rotkehlchen_api_server.rest_api.rotkehlchen
    rotki.blockchain.cache_ttl_secs = 0
    # Test by having balances queried before adding an account
    eth_balances = ['1000000', '2000000']
    token_balances = {'RDN': ['0', '4000000']}
    setup = setup_balances(
        rotki,
        ethereum_accounts=ethereum_accounts,
        btc_accounts=btc_accounts,
        eth_balances=eth_balances,
        token_balances=token_balances,
    )

    new_eth_accounts = [make_ethereum_address(), make_ethereum_address()]
    all_eth_accounts = ethereum_accounts + new_eth_accounts
    eth_balances = ['1000000', '2000000', '3000000', '4000000']
    token_balances = {'RDN': ['0', '4000000', '0', '250000000']}
    setup = setup_balances(
        rotki,
        ethereum_accounts=all_eth_accounts,
        btc_accounts=btc_accounts,
        eth_balances=eth_balances,
        token_balances=token_balances,
    )

    # The application has started only with 2 ethereum accounts. Let's add two more
    with setup.etherscan_patch:
        response = requests.put(api_url_for(
            rotkehlchen_api_server,
            "blockchainsaccountsresource",
            blockchain='ETH',
        ),
                                json={
                                    'accounts': new_eth_accounts,
                                    'async_query': True
                                })
        task_id = assert_ok_async_response(response)
        outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
    assert_eth_balances_result(
        rotki=rotki,
        json_data=outcome,
        eth_accounts=all_eth_accounts,
        eth_balances=setup.eth_balances,
        token_balances=setup.token_balances,
        also_btc=False,  # All blockchain assets have not been queried yet
    )
    # Also make sure they are added in the DB
    accounts = rotki.data.db.get_blockchain_accounts()
    assert len(accounts.eth) == 4
    assert all(acc in accounts.eth for acc in all_eth_accounts)
    assert len(accounts.btc) == 2
    assert all(acc in accounts.btc for acc in btc_accounts)

    # Now try to query all balances to make sure the result is the stored
    with setup.etherscan_patch, setup.bitcoin_patch:
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                "blockchainbalancesresource",
            ))
    assert_proper_response(response)
    json_data = response.json()
    assert json_data['message'] == ''
    assert_eth_balances_result(
        rotki=rotki,
        json_data=json_data,
        eth_accounts=all_eth_accounts,
        eth_balances=setup.eth_balances,
        token_balances=setup.token_balances,
        also_btc=True,
    )

    # Now we will try to add a new BTC account. Setup the mocking infrastructure again
    all_btc_accounts = btc_accounts + [UNIT_BTC_ADDRESS3]
    setup = setup_balances(
        rotki,
        ethereum_accounts=all_eth_accounts,
        btc_accounts=all_btc_accounts,
        eth_balances=eth_balances,
        token_balances=token_balances,
        btc_balances=['3000000', '5000000', '600000000'],
    )
    # add the new BTC account
    with setup.bitcoin_patch:
        response = requests.put(api_url_for(
            rotkehlchen_api_server,
            "blockchainsaccountsresource",
            blockchain='BTC',
        ),
                                json={
                                    'accounts': [UNIT_BTC_ADDRESS3],
                                    'async_query': True
                                })
        task_id = assert_ok_async_response(response)
        outcome = wait_for_async_task(rotkehlchen_api_server, task_id)
    assert_btc_balances_result(
        json_data=outcome,
        btc_accounts=all_btc_accounts,
        btc_balances=setup.btc_balances,
        also_eth=True,
    )
    # Also make sure it's added in the DB
    accounts = rotki.data.db.get_blockchain_accounts()
    assert len(accounts.eth) == 4
    assert all(acc in accounts.eth for acc in all_eth_accounts)
    assert len(accounts.btc) == 3
    assert all(acc in accounts.btc for acc in all_btc_accounts)

    # Now try to query all balances to make sure the result is also stored
    with setup.etherscan_patch, setup.bitcoin_patch:
        response = requests.get(
            api_url_for(
                rotkehlchen_api_server,
                "blockchainbalancesresource",
            ))
    assert_proper_response(response)
    json_data = response.json()
    assert json_data['message'] == ''
    assert_btc_balances_result(
        json_data=json_data,
        btc_accounts=all_btc_accounts,
        btc_balances=setup.btc_balances,
        also_eth=True,
    )