Example #1
0
def test_atoken_to_asset():
    cursor = GlobalDBHandler()._conn.cursor()
    result = cursor.execute(
        'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B '
        'WHERE A.address=B.details_reference AND A.protocol IN (?, ?)',
        ('aave', 'aave-v2'),
    )
    for entry in result:
        atoken = EthereumToken(entry[0])
        reserve_asset = atoken_to_asset(atoken)
        if atoken in ATOKENV1_TO_ASSET:
            assert reserve_asset == ATOKENV1_TO_ASSET[atoken]
        else:
            assert reserve_asset == ATOKENV2_ADDRESS_TO_RESERVE_ASSET[atoken.ethereum_address]

    for atokenv1, reserve_asset in ATOKENV1_TO_ASSET.items():
        assert atoken_to_asset(atokenv1) == reserve_asset
Example #2
0
def atoken_to_asset(atoken: EthereumToken) -> Optional[Asset]:
    if atoken == A_AETH_V1:
        return A_ETH
    if atoken == A_AREP_V1:
        return A_REP

    asset_symbol = atoken.symbol[1:]
    cursor = GlobalDBHandler().conn.cursor()
    result = cursor.execute(
        'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B '
        'WHERE A.address=B.details_reference AND B.symbol=? COLLATE NOCASE',
        (asset_symbol,),
    ).fetchall()
    if len(result) != 1:
        log.error(f'Could not find asset from {atoken} since multiple or no results were returned')
        return None

    return Asset(ethaddress_to_identifier(result[0][0]))
Example #3
0
def asset_to_atoken(asset: Asset, version: int) -> Optional[EthereumToken]:
    if asset == A_ETH:
        return A_AETH_V1

    protocol = 'aave' if version == 1 else 'aave-v2'
    cursor = GlobalDBHandler().conn.cursor()
    result = cursor.execute(
        'SELECT A.address from ethereum_tokens as A LEFT OUTER JOIN assets as B '
        'WHERE A.protocol==? AND A.address=B.details_reference AND B.symbol=?',
        (protocol, 'a' + asset.symbol),
    ).fetchall()
    if len(result) != 1:
        log.error(f'Could not derive atoken from {asset} since multiple or no results were returned')  # noqa: E501
        return None

    try:
        return EthereumToken(result[0][0])
    except UnknownAsset:  # should not happen
        log.error(f'Could not derive atoken from {asset}. Couldnt turn {result[0]} to EthereumToken')  # noqa: E501
        return None
Example #4
0
def export_assets_from_file(
    dirpath: Optional[Path],
    db_handler: 'DBHandler',
) -> Path:
    """
    Creates a zip file with a json file containing the assets added by the user.
    May raise:
    - PermissionError if the temp file can't be created
    """
    if dirpath is None:
        dirpath = Path(tempfile.TemporaryDirectory().name)
        dirpath.mkdir(parents=True, exist_ok=True)

    assets = GlobalDBHandler().get_user_added_assets(user_db=db_handler)
    serialized = []
    for asset_identifier in assets:
        try:
            asset = Asset(asset_identifier)
            serialized.append(asset.to_dict())
        except UnknownAsset as e:
            log.error(e)

    cursor = GlobalDBHandler().conn.cursor()
    query = cursor.execute('SELECT value from settings WHERE name="version";')
    version = query.fetchone()[0]
    data = {
        'version': version,
        'assets': serialized,
    }

    zip_path = dirpath / 'assets.zip'
    with ZipFile(file=zip_path, mode='w', compression=ZIP_DEFLATED) as assets_zip:
        assets_zip.writestr(
            zinfo_or_arcname='assets.json',
            data=json.dumps(data),
        )

    return zip_path
Example #5
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,
    )
Example #6
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