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
def get_current_dsr(self) -> DSRCurrentBalances: """Gets the current DSR balance for all accounts that have DAI in DSR and the current DSR percentage May raise: - RemoteError if etherscan is used and there is a problem with reaching it or with the returned result. - BlockchainQueryError if an ethereum node is used and the contract call queries fail for some reason """ with self.lock: proxy_mappings = self._get_accounts_having_maker_proxy() balances = {} try: current_dai_price = Inquirer().find_usd_price( EthereumToken('DAI')) except RemoteError: current_dai_price = Price(FVal(1)) for account, proxy in proxy_mappings.items(): guy_slice = MAKERDAO_POT.call(self.ethereum, 'pie', arguments=[proxy]) if guy_slice == 0: # no current DSR balance for this proxy continue chi = MAKERDAO_POT.call(self.ethereum, 'chi') dai_balance = _dsrdai_to_dai(guy_slice * chi) balances[account] = Balance( amount=dai_balance, usd_value=current_dai_price * dai_balance, ) current_dsr = MAKERDAO_POT.call(self.ethereum, 'dsr') # Calculation is from here: # https://docs.makerdao.com/smart-contract-modules/rates-module#a-note-on-setting-rates current_dsr_percentage = ( (FVal(current_dsr / RAY)**31622400) % 1) * 100 result = DSRCurrentBalances(balances=balances, current_dsr=current_dsr_percentage) return result
def get_token_balances(self, account: ChecksumEthAddress) -> Dict[EthereumToken, FVal]: """Auto-detect which tokens are owned and get token balances for the account The returned balance is already normalized for the token's decimals. May raise: - RemoteError if there is a problem contacting aleth.io """ balances = {} data = self._query(root_endpoint='accounts', path=f'{account}/tokenBalances') for entry in data: entry_type = entry.get('type', None) if entry_type == 'TokenBalance': attributes = entry.get('attributes', None) balance = None if attributes is not None: balance = attributes.get('balance', None) if balance is None: continue relationships = entry.get('relationships', None) if relationships is None: continue token = relationships.get('token', None) if token is None: continue if 'data' not in token: continue if 'id' not in token['data']: continue token_address = to_checksum_address(token['data']['id']) token_info = self.token_address_to_identifier(token_address) if token_info is None: continue amount = FVal(balance) / (FVal(10) ** FVal(token_info.decimal)) balances[EthereumToken(token_info.symbol)] = amount return balances
def get_owned_tokens(self) -> List[EthereumToken]: cursor = self.conn.cursor() query = cursor.execute( 'SELECT value FROM multisettings WHERE name="eth_token";', ) result = [] for q in query: try: result.append(EthereumToken(q[0])) except UnknownAsset: self.msg_aggregator.add_warning( f'Unknown/unsupported asset {q[0]} found in the database. ' f'If you believe this should be supported open an issue in github', ) continue except DeserializationError: self.msg_aggregator.add_error( f'Non-string type asset: {type(q[0])} found in the database. Ignoring it.', ) continue return result
def get_asset_balance_total(asset_symbol: str, setup: BalancesTestSetup) -> FVal: conversion_function = satoshis_to_btc if asset_symbol == 'BTC' else from_wei total = ZERO asset = Asset(asset_symbol) if asset_symbol in ('ETH', 'BTC'): asset_balances = getattr(setup, f'{asset_symbol.lower()}_balances') total += sum(conversion_function(FVal(b)) for b in asset_balances) elif asset.is_eth_token(): asset_balances = setup.token_balances[EthereumToken(asset_symbol)] total += sum(conversion_function(FVal(b)) for b in asset_balances) total += setup.binance_balances.get(asset_symbol, ZERO) total += setup.poloniex_balances.get(asset_symbol, ZERO) if setup.manually_tracked_balances: for entry in setup.manually_tracked_balances: if entry.asset.identifier == asset_symbol: total += entry.amount return total
def test_asset_and_price_not_found_in_history_processing(accountant): """ Make sure that in history processing if no price is found for a trade it's added to a `missing_prices` list and no error is logged. Regression for https://github.com/rotki/rotki/issues/432 Updated with https://github.com/rotki/rotki/pull/4196 """ fgp = EthereumToken('0xd9A8cfe21C232D485065cb62a96866799d4645f7') time = Timestamp(1492685761) trade = Trade( timestamp=time, location=Location.KRAKEN, base_asset=fgp, quote_asset=A_BTC, trade_type=TradeType.BUY, amount=FVal('2.5'), rate=FVal(.11000), fee=FVal('0.15'), fee_currency=fgp, link=None, ) history = [trade, trade] # duplicate missing price accounting_history_process( accountant, start_ts=0, end_ts=1514764799, # 31/12/2017 history_list=history, ) errors = accountant.msg_aggregator.consume_errors() assert len(errors) == 0 assert len(accountant.pots[0].cost_basis.missing_prices) == 1 assert list( accountant.pots[0].cost_basis.missing_prices)[0] == MissingPrice( from_asset=fgp, to_asset=A_EUR, time=time, )
def handle_protocols( self, protocol_name: str, token_symbol: str, normalized_balance: FVal, token_address: str, token_name: str, ) -> Optional[DefiBalance]: """Special handling for price for token/protocols which are easier to do onchain or need some kind of special treatment. This method can raise DeserializationError """ if protocol_name == 'PoolTogether': result = _handle_pooltogether(normalized_balance, token_name) if result is not None: return result asset = get_asset_by_symbol(token_symbol) if asset is None: return None token = EthereumToken.from_asset(asset) if token is None: return None underlying_asset_price = get_underlying_asset_price(token) usd_price = handle_defi_price_query(self.ethereum, token, underlying_asset_price) if usd_price is None: return None return DefiBalance( token_address=deserialize_ethereum_address(token_address), token_name=token_name, token_symbol=token_symbol, balance=Balance(amount=normalized_balance, usd_value=normalized_balance * usd_price), )
def mock_requests_get(url, *args, **kwargs): if not use_alethio: response = '{"message": "fail so that test switches to etherscan"}' return MockResponse(400, response) if 'tokenBalances' in url: addr = url[33:75] assert addr in eth_map, f'Queried alethio for {addr} which is not in the eth_map' response = '{"data":[' for symbol, balance in eth_map[addr].items(): if symbol == 'ETH': continue token = EthereumToken(symbol) if FVal(balance) == ZERO: continue response += f"""{{"type":"TokenBalance","id":"foo","attributes":{{"balance":"{balance}"}},"relationships":{{"account":{{"data":{{"type":"Account","id":"foo"}},"links":{{"related":"https://api.aleth.io/v1/token-balances/0x9531c059098e3d194ff87febb587ab07b30b13066b175474e89094c44da98b954eedeac495271d0f/account"}},"token":{{"data":{{"type":"Token","id":"{token.ethereum_address}" }}}}}}""" # noqa: E501 response += ']}' else: raise AssertionError(f'Unimplemented alethio mock for url: {url}') return MockResponse(200, response)
def _get_tokens_balance_and_price( self, address: ChecksumEthAddress, tokens: List[CustomEthereumTokenWithIdentifier], balances: Dict[EthereumToken, FVal], token_usd_price: Dict[EthereumToken, Price], call_order: Optional[Sequence[NodeName]], ) -> None: ret = self._get_multitoken_account_balance( tokens=tokens, account=address, call_order=call_order, ) for token_identifier, value in ret.items(): token = EthereumToken(token_identifier) balances[token] += value if token in token_usd_price: continue # else get the price try: usd_price = Inquirer().find_usd_price(token) except RemoteError: usd_price = Price(ZERO) token_usd_price[token] = usd_price
# Top holder of WBTC-WETH pool (0x1eff8af5d577060ba4ac8a29a13525bb0ee2a3d5) BALANCER_TEST_ADDR1 = string_to_ethereum_address( '0x49a2DcC237a65Cc1F412ed47E0594602f6141936') BALANCER_TEST_ADDR2 = string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b') BALANCER_TEST_ADDR3 = string_to_ethereum_address( '0x7716a99194d758c8537F056825b75Dd0C8FDD89f') BALANCER_TEST_ADDR4 = string_to_ethereum_address( '0x231DC6af3C66741f6Cf618884B953DF0e83C1A2A') BALANCER_TEST_ADDR3_POOL1 = EthereumToken.initialize( address=string_to_ethereum_address( '0x59A19D8c652FA0284f44113D0ff9aBa70bd46fB4'), symbol='BPT', protocol='balancer', underlying_tokens=[ UnderlyingToken(address=string_to_ethereum_address( '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'), weight=FVal(0.2)), # noqa: E501 # WETH UnderlyingToken(address=string_to_ethereum_address( '0xba100000625a3754423978a60c9317c58a424e3D'), weight=FVal(0.8)), # noqa: E501 # BAL ], ) BALANCER_TEST_ADDR3_POOL2 = EthereumToken.initialize( address=string_to_ethereum_address( '0x574FdB861a0247401B317a3E68a83aDEAF758cf6'), symbol='BPT', protocol='balancer', underlying_tokens=[ UnderlyingToken(address=string_to_ethereum_address( '0x0D8775F648430679A709E98d2b0Cb6250d2887EF'), weight=FVal(0.1)), # noqa: E501 # BAT
def test_simple_update(rotkehlchen_api_server, globaldb): """Test that the happy case of update works. - Test that up_to_version argument works - Test that only versions above current local are applied - Test that versions with min/max schema mismatch are skipped """ async_query = random.choice([False, True]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen update_3 = """INSERT INTO ethereum_tokens(address, decimals, protocol) VALUES("0xC2FEC534c461c45533e142f724d0e3930650929c", 18, NULL);INSERT INTO assets(identifier,type, name, symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("_ceth_0xC2FEC534c461c45533e142f724d0e3930650929c", "C", "AKB token", "AKB",123, NULL, NULL, "AIDU", "0xC2FEC534c461c45533e142f724d0e3930650929c"); * INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("121-ada-FADS-as", "F","A name","SYMBOL",NULL, NULL,"", "", "121-ada-FADS-as");INSERT INTO common_asset_details(asset_id, forked) VALUES("121-ada-FADS-as", "BTC"); * UPDATE assets SET name="Ευρώ" WHERE identifier="EUR"; INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("EUR", "A","Ευρώ","EUR",NULL, NULL,NULL,NULL, "EUR");INSERT INTO common_asset_details(asset_id, forked) VALUES("EUR", NULL); """ # noqa: E501 update_patch = mock_asset_updates( original_requests_get=requests.get, latest=999999996, updates={ "999999991": { "changes": 1, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }, "999999992": { "changes": 1, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }, "999999993": { "changes": 3, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }, "999999994": { "changes": 5, "min_schema_version": GLOBAL_DB_VERSION + 1, "max_schema_version": GLOBAL_DB_VERSION - 1, }, "999999995": { "changes": 5, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }, "999999996": { "changes": 5, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, }, }, sql_actions={ "999999991": "", "999999992": "", "999999993": update_3, "999999994": "", "999999995": "" }, # noqa: E501 ) globaldb.add_setting_value(ASSETS_VERSION_KEY, 999999992) with update_patch: response = requests.get( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) result = outcome['result'] assert outcome['message'] == '' else: result = assert_proper_response_with_result(response) assert result['local'] == 999999992 assert result['remote'] == 999999996 assert result[ 'new_changes'] == 13 # changes from 99[3 + 4 + 6], skipping 5 response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={ 'async_query': async_query, 'up_to_version': 999999995 }, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result(response) errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 1 assert 'Skipping assets update 999999994 since it requires a min schema of' in warnings[ 0] assert result is True assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999995 new_token = EthereumToken('0xC2FEC534c461c45533e142f724d0e3930650929c') assert new_token.identifier == strethaddress_to_identifier( '0xC2FEC534c461c45533e142f724d0e3930650929c') # noqa: E501 assert new_token.name == 'AKB token' assert new_token.symbol == 'AKB' assert new_token.asset_type == AssetType.ETHEREUM_TOKEN assert new_token.started == 123 assert new_token.forked is None assert new_token.swapped_for is None assert new_token.coingecko is None assert new_token.cryptocompare == 'AIDU' assert new_token.ethereum_address == '0xC2FEC534c461c45533e142f724d0e3930650929c' assert new_token.decimals == 18 assert new_token.protocol is None new_asset = Asset('121-ada-FADS-as') assert new_asset.identifier == '121-ada-FADS-as' assert new_asset.name == 'A name' assert new_asset.symbol == 'SYMBOL' assert new_asset.asset_type == AssetType.COUNTERPARTY_TOKEN assert new_asset.started is None assert new_asset.forked == 'BTC' assert new_asset.swapped_for is None assert new_asset.coingecko == '' assert new_asset.cryptocompare == '' assert Asset('EUR').name == 'Ευρώ'
def test_update_conflicts(rotkehlchen_api_server, globaldb): """Test that conflicts in an asset update are handled properly""" async_query = random.choice([False, True]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen update_1 = """INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("121-ada-FADS-as", "F","A name","SYMBOL",NULL, NULL,"", "", "121-ada-FADS-as");INSERT INTO common_asset_details(asset_id, forked) VALUES("121-ada-FADS-as", "BTC"); * INSERT INTO ethereum_tokens(address, decimals, protocol) VALUES("0x6B175474E89094C44Da98b954EedeAC495271d0F", 8, "maker");INSERT INTO assets(identifier,type, name, symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F", "C", "New Multi Collateral DAI", "NDAI", 1573672677, NULL, "dai", NULL, "0x6B175474E89094C44Da98b954EedeAC495271d0F"); * INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("DASH", "B","Dash","DASH",1337, NULL, "dash-coingecko", NULL, "DASH");INSERT INTO common_asset_details(asset_id, forked) VALUES("DASH", "BTC"); * """ # noqa: E501 update_patch = mock_asset_updates( original_requests_get=requests.get, latest=999999991, updates={ "999999991": { "changes": 3, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, } }, sql_actions={"999999991": update_1}, ) globaldb.add_setting_value(ASSETS_VERSION_KEY, 999999990) start_assets_num = len(globaldb.get_all_asset_data(mapping=False)) with update_patch: response = requests.get( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) result = outcome['result'] assert outcome['message'] == '' else: result = assert_proper_response_with_result(response) assert result['local'] == 999999990 assert result['remote'] == 999999991 assert result['new_changes'] == 3 response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome[ 'message'] == 'Found conflicts during assets upgrade' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='Found conflicts during assets upgrade', status_code=HTTPStatus.CONFLICT, ) # Make sure that nothing was committed assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999990 assert len( globaldb.get_all_asset_data(mapping=False)) == start_assets_num with pytest.raises(UnknownAsset): Asset('121-ada-FADS-as') errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 0, f'Found warnings: {warnings}' # See that we get two conflicts expected_result = [{ 'identifier': '_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F', 'local': { 'name': 'Multi Collateral Dai', 'symbol': 'DAI', 'asset_type': 'ethereum token', 'started': 1573672677, 'forked': None, 'swapped_for': None, 'ethereum_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'decimals': 18, 'cryptocompare': None, 'coingecko': 'dai', 'protocol': None, }, 'remote': { 'name': 'New Multi Collateral DAI', 'symbol': 'NDAI', 'asset_type': 'ethereum token', 'started': 1573672677, 'forked': None, 'swapped_for': None, 'ethereum_address': '0x6B175474E89094C44Da98b954EedeAC495271d0F', 'decimals': 8, 'cryptocompare': None, 'coingecko': 'dai', 'protocol': 'maker', }, }, { 'identifier': 'DASH', 'local': { 'name': 'Dash', 'symbol': 'DASH', 'asset_type': 'own chain', 'started': 1390095618, 'forked': None, 'swapped_for': None, 'ethereum_address': None, 'decimals': None, 'cryptocompare': None, 'coingecko': 'dash', 'protocol': None, }, 'remote': { 'name': 'Dash', 'symbol': 'DASH', 'asset_type': 'own chain', 'started': 1337, 'forked': 'BTC', 'swapped_for': None, 'ethereum_address': None, 'decimals': None, 'cryptocompare': None, 'coingecko': 'dash-coingecko', 'protocol': None, }, }] assert result == expected_result # now try the update again but specify the conflicts resolution conflicts = { '_ceth_0x6B175474E89094C44Da98b954EedeAC495271d0F': 'remote', 'DASH': 'local' } response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={ 'async_query': async_query, 'conflicts': conflicts }, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='', status_code=HTTPStatus.OK, ) # check conflicts were solved as per the given choices and new asset also added assert result is True assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999991 errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 0, f'Found warnings: {warnings}' dai = EthereumToken('0x6B175474E89094C44Da98b954EedeAC495271d0F') assert dai.identifier == strethaddress_to_identifier( '0x6B175474E89094C44Da98b954EedeAC495271d0F') # noqa: E501 assert dai.name == 'New Multi Collateral DAI' assert dai.symbol == 'NDAI' assert dai.asset_type == AssetType.ETHEREUM_TOKEN assert dai.started == 1573672677 assert dai.forked is None assert dai.swapped_for is None assert dai.coingecko == 'dai' assert dai.cryptocompare is None assert dai.ethereum_address == '0x6B175474E89094C44Da98b954EedeAC495271d0F' assert dai.decimals == 8 assert dai.protocol == 'maker' dash = Asset('DASH') assert dash.identifier == 'DASH' assert dash.name == 'Dash' assert dash.symbol == 'DASH' assert dash.asset_type == AssetType.OWN_CHAIN assert dash.started == 1390095618 assert dash.forked is None assert dash.swapped_for is None assert dash.coingecko == 'dash' assert dash.cryptocompare is None new_asset = Asset('121-ada-FADS-as') assert new_asset.identifier == '121-ada-FADS-as' assert new_asset.name == 'A name' assert new_asset.symbol == 'SYMBOL' assert new_asset.asset_type == AssetType.COUNTERPARTY_TOKEN assert new_asset.started is None assert new_asset.forked == 'BTC' assert new_asset.swapped_for is None assert new_asset.coingecko == '' assert new_asset.cryptocompare == ''
from rotkehlchen.fval import FVal from rotkehlchen.serialization.deserialize import deserialize_ethereum_address from rotkehlchen.typing import AssetAmount, Price, Timestamp # Logic: Get balances # Addresses TEST_ADDRESS_1 = deserialize_ethereum_address( '0xfeF0E7635281eF8E3B705e9C5B86e1d3B0eAb397') TEST_ADDRESS_2 = deserialize_ethereum_address( '0xcf2B8EeC2A9cE682822b252a1e9B78EedebEFB02') TEST_ADDRESS_3 = deserialize_ethereum_address( '0x7777777777777777777777777777777777777777') # Known tokens ASSET_USDT = EthereumToken('USDT') ASSET_WETH = EthereumToken('WETH') TOKEN_BASED = EthereumToken('$BASED') # Unknown tokens ASSET_SHUF = UnknownEthereumToken( ethereum_address=deserialize_ethereum_address( '0x3A9FfF453d50D4Ac52A6890647b823379ba36B9E'), symbol='SHUF', name='Shuffle.Monster V3', decimals=18, ) ASSET_TGX = UnknownEthereumToken( ethereum_address=deserialize_ethereum_address( '0x364A7381A5b378CeD7AB33d1CDf6ff1bf162Bfd6'), symbol='TGX',
from rotkehlchen.history.price import query_usd_price_zero_if_error from rotkehlchen.inquirer import Inquirer from rotkehlchen.premium.premium import Premium from rotkehlchen.typing import ChecksumEthAddress, Timestamp from rotkehlchen.user_messages import MessagesAggregator from rotkehlchen.utils.interfaces import EthereumModule from rotkehlchen.utils.misc import hexstr_to_int if TYPE_CHECKING: from rotkehlchen.chain.ethereum.manager import EthereumManager ADDRESS_TO_ASSETS = Dict[ChecksumEthAddress, Dict[Asset, Balance]] BLOCKS_PER_DAY = 4 * 60 * 24 DAYS_PER_YEAR = 365 ETH_MANTISSA = 10**18 A_COMP = EthereumToken('COMP') COMPTROLLER_PROXY = EthereumConstants().contract('COMPTROLLER_PROXY') COMP_DEPLOYED_BLOCK = 9601359 LEND_EVENTS_QUERY_PREFIX = """{graph_event_name} (where: {{blockTime_lte: $end_ts, blockTime_gte: $start_ts, {addr_position}: $address}}) {{ id amount to from blockNumber blockTime cTokenSymbol underlyingAmount }}}}"""
def test_curve_remove_imbalanced(database, ethereum_manager, eth_transactions): """Data for deposit taken from https://etherscan.io/tx/0xd8832abcf4773abe24d8cda5581fb53bfb3850c535c1956d1d120a72a4ebcbd8 This tests uses the steth pool to verify that withdrawals are correctly decoded when an internal transaction is made for eth transfers """ msg_aggregator = MessagesAggregator() tx_hex = '0xd8832abcf4773abe24d8cda5581fb53bfb3850c535c1956d1d120a72a4ebcbd8' location_label = '0x2fac74A3a04B031F240923621a578724C40678af' evmhash = deserialize_evm_tx_hash(tx_hex) transaction = EthereumTransaction( tx_hash=evmhash, timestamp=1650276061, block_number=14647221, from_address=location_label, to_address='0xbBC81d23Ea2c3ec7e56D39296F0cbB648873a5d3', value=0, gas=171249, gas_price=22990000000, gas_used=171249, input_data=hexstring_to_bytes('0x517a55a300000000000000000000000000000000000000000000001fa9ee7266a543831f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000003f487c50000000000000000000000000000000000000000000000000000000000000001'), # noqa: E501 nonce=5, ) receipt = EthereumTxReceipt( tx_hash=evmhash, contract_address=None, status=True, type=0, logs=[ EthereumTxReceiptLog( log_index=2183, data=hexstring_to_bytes('0x00000000000000000000000000000000000000000000001fa9ee7266a543831f'), # noqa: E501 address=string_to_ethereum_address('0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x0000000000000000000000002fac74a3a04b031f240923621a578724c40678af'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2184, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 address=string_to_ethereum_address('0x16de59092dAE5CcF4A1E6439D611fd0653f0Bd01'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x00000000000000000000000045f783cce6b7ff23b2ab2d70e416cdb7d6055f51'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2185, data=hexstring_to_bytes('0x000000000000000000000000000000000000000000000000000000001fdb750a'), # noqa: E501 address=string_to_ethereum_address('0xd6aD7a6750A7593E092a9B218d66C0A814a3436e'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x00000000000000000000000045f783cce6b7ff23b2ab2d70e416cdb7d6055f51'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2186, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 address=string_to_ethereum_address('0x83f798e925BcD4017Eb265844FDDAbb448f1707D'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x00000000000000000000000045f783cce6b7ff23b2ab2d70e416cdb7d6055f51'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2187, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 address=string_to_ethereum_address('0x73a052500105205d34Daf004eAb301916DA8190f'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x00000000000000000000000045f783cce6b7ff23b2ab2d70e416cdb7d6055f51'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2188, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 address=string_to_ethereum_address('0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2189, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001fdb750a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000045cf4bec2e53f0000000000000000000000000000000000000000000000000000000000000e07e000000000000000000000000000000000000000000000000000000000000570d0000000000000000000000000000000000000000000000000051077d9dc293100000000000000000000000000000000000000000000c740195f187122987a9ef0000000000000000000000000000000000000000000aeddccb3976328f7d90bd'), # noqa: E501 address=string_to_ethereum_address('0x45F783CCE6B7FF23B2ab2D70e416cdb7D6055f51'), removed=False, topics=[ hexstring_to_bytes('0xb964b72f73f5ef5bf0fdc559b2fab9a7b12a39e47817a547f1f0aee47febd602'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 ], ), EthereumTxReceiptLog( log_index=2189, data=hexstring_to_bytes('0x0000000000000000000000000000000000000000000000000000000027a72df9'), # noqa: E501 address=string_to_ethereum_address('0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48'), removed=False, topics=[ hexstring_to_bytes('0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'), # noqa: E501 hexstring_to_bytes('0x000000000000000000000000bbc81d23ea2c3ec7e56d39296f0cbb648873a5d3'), # noqa: E501 hexstring_to_bytes('0x0000000000000000000000002fac74a3a04b031f240923621a578724c40678af'), # noqa: E501 ], ), ], ) dbethtx = DBEthTx(database) dbethtx.add_ethereum_transactions([transaction], relevant_address=None) decoder = EVMTransactionDecoder( database=database, ethereum_manager=ethereum_manager, eth_transactions=eth_transactions, msg_aggregator=msg_aggregator, ) events = decoder.decode_transaction(transaction=transaction, tx_receipt=receipt) expected_events = [ HistoryBaseEntry( event_identifier=tx_hex, sequence_index=0, timestamp=1650276061000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, asset=A_ETH, balance=Balance( amount=FVal(0.00393701451), usd_value=ZERO, ), location_label=location_label, notes='Burned 0.00393701451 ETH in gas from 0x2fac74A3a04B031F240923621a578724C40678af', # noqa: E501 counterparty=CPT_GAS, ), HistoryBaseEntry( event_identifier=tx_hex, sequence_index=2184, timestamp=1650276061000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.RETURN_WRAPPED, asset=EthereumToken('0xdF5e0e81Dff6FAF3A7e52BA697820c5e32D806A8'), balance=Balance(amount=FVal('584.093916507047953183'), usd_value=ZERO), location_label=location_label, notes='Return 584.093916507047953183 yDAI+yUSDC+yUSDT+yTUSD', counterparty=CPT_CURVE, ), HistoryBaseEntry( event_identifier=tx_hex, sequence_index=2190, timestamp=1650276061000, location=Location.BLOCKCHAIN, event_type=HistoryEventType.WITHDRAWAL, event_subtype=HistoryEventSubType.REMOVE_ASSET, asset=A_USDC, balance=Balance(amount=FVal('665.267705'), usd_value=ZERO), location_label=location_label, notes='Receive 665.267705 USDC from the curve pool 0xbBC81d23Ea2c3ec7e56D39296F0cbB648873a5d3', # noqa: E501 counterparty=CPT_CURVE, )] assert expected_events == events
A_SUSD, A_TUSD, A_UNI, A_USDC, A_USDT, A_WBTC, A_WETH, A_YFI, A_ZRX, ) from rotkehlchen.constants.misc import ZERO from rotkehlchen.fval import FVal from rotkehlchen.tests.utils.constants import A_ADAI from rotkehlchen.typing import Timestamp A_AENJ_V1 = EthereumToken('0x712DB54daA836B53Ef1EcBb9c6ba3b9Efb073F40') A_ADAI_V1 = EthereumToken('0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d') A_ASUSD_V1 = EthereumToken('0x625aE63000f46200499120B906716420bd059240') A_ATUSD_V1 = EthereumToken('0x4DA9b813057D04BAef4e5800E36083717b4a0341') A_AUSDT_V1 = EthereumToken('0x71fc860F7D3A592A4a98740e39dB31d25db65ae8') A_ABUSD_V1 = EthereumToken('0x6Ee0f7BB50a54AB5253dA0667B0Dc2ee526C30a8') A_ABAT_V1 = EthereumToken('0xE1BA0FB44CCb0D11b80F92f4f8Ed94CA3fF51D00') A_AKNC_V1 = EthereumToken('0x9D91BE44C06d373a8a226E1f3b146956083803eB') A_ALEND_V1 = EthereumToken('0x7D2D3688Df45Ce7C552E19c27e007673da9204B8') A_AMANA_V1 = EthereumToken('0x6FCE4A401B6B80ACe52baAefE4421Bd188e76F6f') A_AMKR_V1 = EthereumToken('0x7deB5e830be29F91E298ba5FF1356BB7f8146998') A_AREN_V1 = EthereumToken('0x69948cC03f478B95283F7dbf1CE764d0fc7EC54C') A_ASNX_V1 = EthereumToken('0x328C4c80BC7aCa0834Db37e6600A6c49E12Da4DE') A_AWBTC_V1 = EthereumToken('0xFC4B8ED459e00e5400be803A9BB3954234FD50e3') A_AYFI_V1 = EthereumToken('0x12e51E77DAAA58aA0E9247db7510Ea4B46F9bEAd') A_AZRX_V1 = EthereumToken('0x6Fb0855c404E09c47C3fBCA25f08d4E41f9F062f')
from rotkehlchen.assets.asset import Asset, EthereumToken A_RDN = EthereumToken('RDN') A_GNO = EthereumToken('GNO') A_DAO = EthereumToken('DAO') A_DOGE = Asset('DOGE') A_XMR = Asset('XMR') A_DASH = Asset('DASH') A_IOTA = Asset('IOTA') A_BSV = Asset('BSV') A_BCH = Asset('BCH') A_GBP = Asset('GBP') A_SNGLS = Asset('SNGLS') A_BNB = Asset('BNB') A_USDT = Asset('USDT')
def find_usd_price( asset: Asset, ignore_cache: bool = False, ) -> Price: """Returns the current USD price of the asset Returns Price(ZERO) if all options have been exhausted and errors are logged in the logs """ if asset == A_USD: return Price(FVal(1)) instance = Inquirer() cache_key = (asset, A_USD) if ignore_cache is False: cache = instance.get_cached_current_price_entry( cache_key=cache_key) if cache is not None: return cache.price if asset.is_fiat(): try: return instance._query_fiat_pair(base=asset, quote=A_USD) except RemoteError: pass # continue, a price can be found by one of the oracles (CC for example) # Try and check if it is an ethereum token with specified protocol or underlying tokens is_known_protocol = False underlying_tokens = None try: token = EthereumToken.from_asset(asset) if token is not None: if token.protocol is not None: is_known_protocol = token.protocol in KnownProtocolsAssets underlying_tokens = GlobalDBHandler( ).get_ethereum_token( # type: ignore token.ethereum_address, ).underlying_tokens except UnknownAsset: pass # Check if it is a special token if asset in instance.special_tokens: ethereum = instance._ethereum assert ethereum, 'Inquirer should never be called before the injection of ethereum' assert token, 'all assets in special tokens are already ethereum tokens' underlying_asset_price = get_underlying_asset_price(token) usd_price = handle_defi_price_query( ethereum=ethereum, token=token, underlying_asset_price=underlying_asset_price, ) if usd_price is None: price = Price(ZERO) else: price = Price(usd_price) Inquirer._cached_current_price[cache_key] = CachedPriceEntry( price=price, time=ts_now()) # noqa: E501 return price if is_known_protocol is True or underlying_tokens is not None: assert token is not None result = get_underlying_asset_price(token) if result is None: usd_price = Price(ZERO) if instance._ethereum is not None: instance._ethereum.msg_aggregator.add_warning( f'Could not find price for {token}', ) else: usd_price = Price(result) Inquirer._cached_current_price[cache_key] = CachedPriceEntry( price=usd_price, time=ts_now(), ) return usd_price # BSQ is a special asset that doesnt have oracle information but its custom API if asset == A_BSQ: try: price_in_btc = get_bisq_market_price(asset) btc_price = Inquirer().find_usd_price(A_BTC) usd_price = Price(price_in_btc * btc_price) Inquirer._cached_current_price[cache_key] = CachedPriceEntry( price=usd_price, time=ts_now(), ) return usd_price except (RemoteError, DeserializationError) as e: msg = f'Could not find price for BSQ. {str(e)}' if instance._ethereum is not None: instance._ethereum.msg_aggregator.add_warning(msg) return Price(BTC_PER_BSQ * price_in_btc) if asset == A_KFEE: # KFEE is a kraken special asset where 1000 KFEE = 10 USD return Price(FVal(0.01)) return instance._query_oracle_instances(from_asset=asset, to_asset=A_USD)
def get_balances( self, given_defi_balances: GIVEN_DEFI_BALANCES, ) -> Dict[ChecksumEthAddress, Dict]: compound_balances = {} if isinstance(given_defi_balances, dict): defi_balances = given_defi_balances else: defi_balances = given_defi_balances() for account, balance_entries in defi_balances.items(): lending_map = {} borrowing_map = {} rewards_map = {} for balance_entry in balance_entries: if balance_entry.protocol.name not in ('Compound Governance', 'Compound'): continue entry = balance_entry.base_balance try: asset = Asset(entry.token_symbol) except UnknownAsset: log.error( f'Encountered unknown asset {entry.token_symbol} in compound. Skipping', ) continue unclaimed_comp_rewards = ( entry.token_address == A_COMP.ethereum_address and balance_entry.protocol.name == 'Compound Governance') if unclaimed_comp_rewards: rewards_map[A_COMP] = CompoundBalance( balance_type=BalanceType.ASSET, balance=entry.balance, apy=None, ) continue if balance_entry.balance_type == 'Asset': # Get the underlying balance underlying_symbol = balance_entry.underlying_balances[ 0].token_symbol try: underlying_asset = Asset(underlying_symbol) except UnknownAsset: log.error( f'Encountered unknown asset {underlying_symbol} in compound. Skipping', ) continue lending_map[underlying_asset.identifier] = CompoundBalance( balance_type=BalanceType.ASSET, balance=balance_entry.underlying_balances[0].balance, apy=self._get_apy(entry.token_address, supply=True), ) else: # 'Debt' try: ctoken = EthereumToken('c' + entry.token_symbol) except UnknownAsset: log.error( f'Encountered unknown asset {entry.token_symbol} in ' f'compound while figuring out cToken. Skipping', ) continue borrowing_map[asset.identifier] = CompoundBalance( balance_type=BalanceType.LIABILITY, balance=entry.balance, apy=self._get_apy(ctoken.ethereum_address, supply=False), ) if lending_map == {} and borrowing_map == {} and rewards_map == {}: # no balances for the account continue compound_balances[account] = { 'rewards': rewards_map, 'lending': lending_map, 'borrowing': borrowing_map, } return compound_balances
def find_uniswap_v2_lp_price( self, token: EthereumToken, ) -> Optional[Price]: """ Calculate the price for a uniswap v2 LP token. That is value = (Total value of liquidity pool) / (Current suply of LP tokens) We need: - Price of token 0 - Price of token 1 - Pooled amount of token 0 - Pooled amount of token 1 - Total supply of of pool token """ assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized' # noqa: E501 address = token.ethereum_address contract = EthereumContract(address=address, abi=UNISWAP_V2_LP_ABI, deployed_block=0) methods = [ 'token0', 'token1', 'totalSupply', 'getReserves', 'decimals' ] try: output = multicall_2( ethereum=self._ethereum, require_success=True, calls=[(address, contract.encode(method_name=method)) for method in methods], ) except RemoteError as e: log.error( f'Remote error calling multicall contract for uniswap v2 lp ' f'token {token.ethereum_address} properties: {str(e)}', ) return None # decode output decoded = [] for (method_output, method_name) in zip(output, methods): if method_output[0] and len(method_output[1]) != 0: decoded_method = contract.decode(method_output[1], method_name) if len(decoded_method) == 1: # https://github.com/PyCQA/pylint/issues/4739 decoded.append(decoded_method[0]) # pylint: disable=unsubscriptable-object else: decoded.append(decoded_method) else: log.debug( f'Multicall to Uniswap V2 LP failed to fetch field {method_name} ' f'for token {token.ethereum_address}', ) return None try: token0 = EthereumToken(decoded[0]) token1 = EthereumToken(decoded[1]) except UnknownAsset: return None try: token0_supply = FVal(decoded[3][0] * 10**-token0.decimals) token1_supply = FVal(decoded[3][1] * 10**-token1.decimals) total_supply = FVal(decoded[2] * 10**-decoded[4]) except ValueError as e: log.debug( f'Failed to deserialize token amounts for token {address} ' f'with values {str(decoded)}. f{str(e)}', ) return None token0_price = self.find_usd_price(token0) token1_price = self.find_usd_price(token1) if ZERO in (token0_price, token1_price): log.debug( f'Couldnt retrieve non zero price information for tokens {token0}, {token1} ' f'with result {token0_price}, {token1_price}', ) numerator = (token0_supply * token0_price + token1_supply * token1_price) share_value = numerator / total_supply return Price(share_value)
from rotkehlchen.assets.asset import Asset, EthereumToken from rotkehlchen.chain.ethereum.typing import string_to_ethereum_address A_RDN = EthereumToken('0x255Aa6DF07540Cb5d3d297f0D0D4D84cb52bc8e6') A_GNO = EthereumToken('0x6810e776880C02933D47DB1b9fc05908e5386b96') A_DAO = EthereumToken('0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413') A_MKR = EthereumToken('0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2') A_SNGLS = EthereumToken('0xaeC2E87E0A235266D9C5ADc9DEb4b2E29b54D009') A_PAXG = EthereumToken('0x45804880De22913dAFE09f4980848ECE6EcbAf78') A_ADAI = EthereumToken('0xfC1E690f61EFd961294b3e1Ce3313fBD8aa4f85d') A_KCS = EthereumToken('0xf34960d9d60be18cC1D5Afc1A6F012A723a28811') A_MCO = EthereumToken('0xB63B606Ac810a52cCa15e44bB630fd42D8d1d83d') A_CRO = EthereumToken('0xA0b73E1Ff0B80914AB6fe0444E65848C4C34450b') A_SUSHI = EthereumToken('0x6B3595068778DD592e39A122f4f5a5cF09C90fE2') A_SDT2 = EthereumToken('0x73968b9a57c6E53d41345FD57a6E6ae27d6CDB2F') A_QTUM = EthereumToken('0x9a642d6b3368ddc662CA244bAdf32cDA716005BC') A_OCEAN = EthereumToken('0x967da4048cD07aB37855c090aAF366e4ce1b9F48') A_GLM = EthereumToken('0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429') A_BUSD = EthereumToken('0x4Fabb145d64652a948d72533023f6E7A623C7C53') A_AMPL = EthereumToken('0xD46bA6D942050d489DBd938a2C909A5d5039A161') A_SYN = EthereumToken('0x1695936d6a953df699C38CA21c2140d497C08BD9') A_API3 = EthereumToken('0x0b38210ea11411557c13457D4dA7dC6ea731B88a') A_MFT = EthereumToken('0xDF2C7238198Ad8B389666574f2d8bc411A4b7428') A_SLP = EthereumToken('0x37236CD05b34Cc79d3715AF2383E96dd7443dCF1') A_DOLLAR_BASED = EthereumToken('0x68A118Ef45063051Eac49c7e647CE5Ace48a68a5') A_BAND = EthereumToken('0xBA11D00c5f74255f56a5E366F4F77f5A186d7f55') A_YAM_V1 = EthereumToken('0x0e2298E3B3390e3b945a5456fBf59eCc3f55DA16') A_CHI = EthereumToken('0x0000000000004946c0e9F43F4Dee607b0eF1fA1c') A_ADAI_V2 = EthereumToken('0x028171bCA77440897B824Ca71D1c56caC55b68A3')
def find_curve_pool_price( self, lp_token: EthereumToken, ) -> Optional[Price]: """ 1. Obtain the pool for this token 2. Obtain prices for assets in pool 3. Obtain the virtual price for share and the balances of each token in the pool 4. Calc the price for a share Returns the price of 1 LP token from the pool """ assert self._ethereum is not None, 'Inquirer ethereum manager should have been initialized' # noqa: E501 pools = get_curve_pools() if lp_token.ethereum_address not in pools: return None pool = pools[lp_token.ethereum_address] tokens = [] # Translate addresses to tokens try: for asset in pool.assets: if asset == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE': tokens.append(A_WETH) else: tokens.append(EthereumToken(asset)) except UnknownAsset: return None # Get price for each token in the pool prices = [] for token in tokens: price = self.find_usd_price(token) if price == Price(ZERO): log.error( f'Could not calculate price for {lp_token} due to inability to ' f'fetch price for {token}.', ) return None prices.append(price) # Query virtual price of LP share and balances in the pool for each token contract = EthereumContract( address=pool.pool_address, abi=CURVE_POOL_ABI, deployed_block=0, ) calls = [(pool.pool_address, contract.encode(method_name='get_virtual_price'))] calls += [(pool.pool_address, contract.encode(method_name='balances', arguments=[i])) for i in range(len(pool.assets))] output = multicall_2( ethereum=self._ethereum, require_success=False, calls=calls, ) # Check that the output has the correct structure if not all([len(call_result) == 2 for call_result in output]): log.debug( f'Failed to query contract methods while finding curve pool price. ' f'Not every outcome has length 2. {output}', ) return None # Check that all the requests were successful if not all([contract_output[0] for contract_output in output]): log.debug( f'Failed to query contract methods while finding curve price. {output}' ) return None # Deserialize information obtained in the multicall execution data = [] # https://github.com/PyCQA/pylint/issues/4739 virtual_price_decoded = contract.decode(output[0][1], 'get_virtual_price') # pylint: disable=unsubscriptable-object # noqa: E501 if not _check_curve_contract_call(virtual_price_decoded): log.debug( f'Failed to decode get_virtual_price while finding curve price. {output}' ) return None data.append(FVal(virtual_price_decoded[0])) # pylint: disable=unsubscriptable-object for i in range(len(pool.assets)): amount_decoded = contract.decode(output[i + 1][1], 'balances', arguments=[i]) if not _check_curve_contract_call(amount_decoded): log.debug( f'Failed to decode balances {i} while finding curve price. {output}' ) return None # https://github.com/PyCQA/pylint/issues/4739 amount = amount_decoded[0] # pylint: disable=unsubscriptable-object normalized_amount = token_normalized_value_decimals( amount, tokens[i].decimals) data.append(normalized_amount) # Prices and data should verify this relation for the following operations if len(prices) != len(data) - 1: log.debug( f'Length of prices {len(prices)} does not match len of data {len(data)} ' f'while querying curve pool price.', ) return None # Total number of assets price in the pool total_assets_price = sum(map(operator.mul, data[1:], prices)) if total_assets_price == 0: log.error( f'Curve pool price returned unexpected data {data} that lead to a zero price.', ) return None # Calculate weight of each asset as the proportion of tokens value weights = map(lambda x: data[x + 1] * prices[x] / total_assets_price, range(len(tokens))) assets_price = FVal(sum(map(operator.mul, weights, prices))) return (assets_price * FVal(data[0])) / (10**lp_token.decimals)
def test_foreignkey_conflict(rotkehlchen_api_server, globaldb): """Test that when a conflict that's not solvable happens the entry is ignored One such case is when the update of an asset would violate a foreign key constraint""" async_query = random.choice([False, True]) rotki = rotkehlchen_api_server.rest_api.rotkehlchen update_1 = """INSERT INTO assets(identifier,type,name,symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("121-ada-FADS-as", "F","A name","SYMBOL",NULL, NULL,"", "", "121-ada-FADS-as");INSERT INTO common_asset_details(asset_id, forked) VALUES("121-ada-FADS-as", "BTC"); * UPDATE assets SET swapped_for="_ceth_0xA8d35739EE92E69241A2Afd9F513d41021A07972" WHERE identifier="_ceth_0xa74476443119A942dE498590Fe1f2454d7D4aC0d"; INSERT INTO ethereum_tokens(address, decimals, protocol) VALUES("0xa74476443119A942dE498590Fe1f2454d7D4aC0d", 18, NULL);INSERT INTO assets(identifier,type, name, symbol,started, swapped_for, coingecko, cryptocompare, details_reference) VALUES("_ceth_0xa74476443119A942dE498590Fe1f2454d7D4aC0d", "C", "Golem", "GNT", 1478810650, "_ceth_0xA8d35739EE92E69241A2Afd9F513d41021A07972", "golem", NULL, "0xa74476443119A942dE498590Fe1f2454d7D4aC0d"); """ # noqa: E501 update_patch = mock_asset_updates( original_requests_get=requests.get, latest=999999991, updates={ "999999991": { "changes": 2, "min_schema_version": GLOBAL_DB_VERSION, "max_schema_version": GLOBAL_DB_VERSION, } }, sql_actions={"999999991": update_1}, ) globaldb.add_setting_value(ASSETS_VERSION_KEY, 999999990) start_assets_num = len(globaldb.get_all_asset_data(mapping=False)) with update_patch: response = requests.get( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) result = outcome['result'] assert outcome['message'] == '' else: result = assert_proper_response_with_result(response) assert result['local'] == 999999990 assert result['remote'] == 999999991 assert result['new_changes'] == 2 response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome[ 'message'] == 'Found conflicts during assets upgrade' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='Found conflicts during assets upgrade', status_code=HTTPStatus.CONFLICT, ) # Make sure that nothing was committed assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999990 assert len( globaldb.get_all_asset_data(mapping=False)) == start_assets_num with pytest.raises(UnknownAsset): Asset('121-ada-FADS-as') errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 0, f'Found warnings: {warnings}' # See that we get a conflict expected_result = [{ 'identifier': '_ceth_0xa74476443119A942dE498590Fe1f2454d7D4aC0d', 'local': { 'name': 'Golem', 'symbol': 'GNT', 'asset_type': 'ethereum token', 'started': 1478810650, 'forked': None, 'swapped_for': '_ceth_0x7DD9c5Cba05E151C895FDe1CF355C9A1D5DA6429', 'ethereum_address': '0xa74476443119A942dE498590Fe1f2454d7D4aC0d', 'decimals': 18, 'cryptocompare': None, 'coingecko': 'golem', 'protocol': None, }, 'remote': { 'name': 'Golem', 'symbol': 'GNT', 'asset_type': 'ethereum token', 'started': 1478810650, 'forked': None, 'swapped_for': '_ceth_0xA8d35739EE92E69241A2Afd9F513d41021A07972', 'ethereum_address': '0xa74476443119A942dE498590Fe1f2454d7D4aC0d', 'decimals': 18, 'cryptocompare': None, 'coingecko': 'golem', 'protocol': None, }, }] assert result == expected_result # now try the update again but specify the conflicts resolution conflicts = { '_ceth_0xa74476443119A942dE498590Fe1f2454d7D4aC0d': 'remote' } response = requests.post( api_url_for( rotkehlchen_api_server, 'assetupdatesresource', ), json={ 'async_query': async_query, 'conflicts': conflicts }, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server, task_id, ) assert outcome['message'] == '' result = outcome['result'] else: result = assert_proper_response_with_result( response, message='', status_code=HTTPStatus.OK, ) # check new asset was added and conflict was ignored with an error due to # inability to do anything with the missing swapped_for assert result is True assert globaldb.get_setting_value(ASSETS_VERSION_KEY, None) == 999999991 gnt = EthereumToken('0xa74476443119A942dE498590Fe1f2454d7D4aC0d') assert gnt.identifier == strethaddress_to_identifier( '0xa74476443119A942dE498590Fe1f2454d7D4aC0d') # noqa: E501 assert gnt.name == 'Golem' assert gnt.symbol == 'GNT' assert gnt.asset_type == AssetType.ETHEREUM_TOKEN assert gnt.started == 1478810650 assert gnt.forked is None assert gnt.swapped_for == A_GLM.identifier assert gnt.coingecko == 'golem' assert gnt.cryptocompare is None assert gnt.ethereum_address == '0xa74476443119A942dE498590Fe1f2454d7D4aC0d' assert gnt.decimals == 18 assert gnt.protocol is None new_asset = Asset('121-ada-FADS-as') assert new_asset.identifier == '121-ada-FADS-as' assert new_asset.name == 'A name' assert new_asset.symbol == 'SYMBOL' assert new_asset.asset_type == AssetType.COUNTERPARTY_TOKEN assert new_asset.started is None assert new_asset.forked == 'BTC' assert new_asset.swapped_for is None assert new_asset.coingecko == '' assert new_asset.cryptocompare == '' errors = rotki.msg_aggregator.consume_errors() warnings = rotki.msg_aggregator.consume_warnings() assert len(errors) == 0, f'Found errors: {errors}' assert len(warnings) == 1 assert f'Failed to resolve conflict for {gnt.identifier} in the DB during the v999999991 assets update. Skipping entry' in warnings[ 0] # noqa: E501
from rotkehlchen.constants import ZERO from rotkehlchen.constants.assets import A_USDT, A_WETH from rotkehlchen.fval import FVal from rotkehlchen.tests.utils.constants import A_DOLLAR_BASED from rotkehlchen.typing import AssetAmount, Price, Timestamp # Logic: Get balances # Addresses TEST_ADDRESS_1 = string_to_ethereum_address('0xfeF0E7635281eF8E3B705e9C5B86e1d3B0eAb397') TEST_ADDRESS_2 = string_to_ethereum_address('0xcf2B8EeC2A9cE682822b252a1e9B78EedebEFB02') TEST_ADDRESS_3 = string_to_ethereum_address('0x7777777777777777777777777777777777777777') # Tokens without oracle data (unknown tokens) A_SHL = EthereumToken('0x8542325B72C6D9fC0aD2Ca965A78435413a915A0') A_CAR = EthereumToken('0x4D9e23a3842fE7Eb7682B9725cF6c507C424A41B') A_BTR = EthereumToken('0xcbf15FB8246F679F9Df0135881CB29a3746f734b') # Method: `_get_balances_graph` # 'liquidityPositions' subgraph response data for TEST_ADDRESS_1 LIQUIDITY_POSITION_1 = { 'id': '0x260e069dead76baac587b5141bb606ef8b9bab6c-0xfef0e7635281ef8e3b705e9c5b86e1d3b0eab397', 'liquidityTokenBalance': '52.974048199782328795', 'pair': { 'id': '0x260e069dead76baac587b5141bb606ef8b9bab6c', 'reserve0': '135433.787685858453561892', 'reserve1': '72.576018267058292417', 'token0': { 'decimals': '18', 'id': '0x8542325b72c6d9fc0ad2ca965a78435413a915a0',
'aBAT': 9241085, 'aKNC': 9241097, 'aLEND': 9241081, 'aLINK': 9241091, 'aMANA': 9241110, 'aMKR': 9241106, 'aREP': 9241100, 'aREN': 10472062, 'aSNX': 9241118, 'aWBTC': 9241225, 'aYFI': 10748286, 'aZRX': 9241114, 'aAAVE': 11093579, 'aUNI': 11132284, } ATOKENS_LIST = [EthereumToken(x) for x in ATOKEN_TO_DEPLOYED_BLOCK] AAVE_RESERVE_TO_ASSET = { AAVE_ETH_RESERVE_ADDRESS: Asset('ETH'), '0xF629cBd94d3791C9250152BD8dfBDF380E2a3B9c': EthereumToken('ENJ'), '0x6B175474E89094C44Da98b954EedeAC495271d0F': EthereumToken('DAI'), '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': EthereumToken('USDC'), '0x57Ab1ec28D129707052df4dF418D58a2D46d5f51': EthereumToken('sUSD'), '0x0000000000085d4780B73119b644AE5ecd22b376': EthereumToken('TUSD'), '0xdAC17F958D2ee523a2206206994597C13D831ec7': EthereumToken('USDT'), '0x4Fabb145d64652a948d72533023f6E7A623C7C53': EthereumToken('BUSD'), '0x0D8775F648430679A709E98d2b0Cb6250d2887EF': EthereumToken('BAT'), '0xdd974D5C2e2928deA5F71b9825b8b646686BD200': EthereumToken('KNC'), '0x80fB784B7eD66730e8b1DBd9820aFD29931aab03': EthereumToken('LEND'), '0x514910771AF9Ca656af840dff83E8264EcF986CA': EthereumToken('LINK'), '0x0F5D2fB29fb7d3CFeE444a200298f468908cC942': EthereumToken('MANA'),
) ], profit_loss=Balance(amount=FVal('0.006054124919207989'), usd_value=ONE), ), } EXPECTED_V2_HISTORY = { '_ceth_0x1C6a9783F812b3Af3aBbf7de64c3cD7CC7D1af44': YearnVaultHistory( events=[ YearnVaultEvent( event_type='deposit', block_number=12462638, timestamp=Timestamp(1621397797), from_asset=EthereumToken( '0x94e131324b6054c0D789b190b2dAC504e4361b53'), from_value=Balance(amount=FVal('32064.715735449204040742'), usd_value=ONE), to_asset=EthereumToken( '0x1C6a9783F812b3Af3aBbf7de64c3cD7CC7D1af44'), to_value=Balance(amount=FVal('32064.715735449204040742'), usd_value=ONE), realized_pnl=None, tx_hash= '0x0a53f8817f44ac0f8b516b7fa7ecba2861c001f506dbc465fe289a7110fcc1ca', log_index=16, version=1, ), YearnVaultEvent( event_type='withdraw', block_number=12494161,
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 = [AaveSimpleEvent( event_type='deposit', asset=A_DAI, value=Balance(amount=FVal(1), usd_value=FVal(1)), block_number=1, timestamp=Timestamp(1), tx_hash='0x01653e88600a6492ad6e9ae2af415c990e623479057e4e93b163e65cfb2d4436', log_index=1, ), AaveSimpleEvent( event_type='withdrawal', asset=A_DAI, 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 = [AaveSimpleEvent( event_type='deposit', asset=A_DAI, value=Balance(amount=FVal(1), usd_value=FVal(1)), block_number=1, timestamp=Timestamp(1), tx_hash='0x8c094d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55', log_index=1, ), AaveSimpleEvent( event_type='withdrawal', asset=A_DAI, 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 = [AaveSimpleEvent( event_type='deposit', asset=A_DAI, value=Balance(amount=FVal(1), usd_value=FVal(1)), block_number=1, timestamp=Timestamp(1), tx_hash='0x9e394d58f33e8dedcd348cb33b58f3bd447602f1fecb99e51b1c2868029eab55', log_index=1, ), AaveSimpleEvent( event_type='withdrawal', asset=A_DAI, value=Balance(amount=FVal(1), usd_value=FVal(1)), block_number=2, timestamp=Timestamp(2), tx_hash='0x4c167445d26679623f9b7d56a8be260a275cb6744a1c1ae5a8d6883a5a5c03de', log_index=2, ), AaveSimpleEvent( event_type='interest', asset=Asset('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=Asset('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=Asset('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=Asset('ETH'), collateral_balance=Balance(amount=FVal(1), usd_value=FVal(1)), principal_asset=Asset('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=EthereumToken('aDAI')) assert events == addr1_events events = data.db.get_aave_events(address=addr2, atoken=EthereumToken('aDAI')) 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)
from rotkehlchen.constants.misc import ZERO from rotkehlchen.fval import FVal from rotkehlchen.serialization.deserialize import deserialize_ethereum_address from rotkehlchen.tests.api.test_balancer import BALANCER_TEST_ADDR2_EXPECTED_TRADES from rotkehlchen.typing import AssetAmount, Location, Price, Timestamp, TradeType TEST_SWAPS_TX_1 = [ AMMSwap( tx_hash='0x1b0d3525964d8e5fbcc0dcdeebcced4bec9017f648e97c3c9761fda1ca6e7b22', log_index=254, address=deserialize_ethereum_address('0x8e670b4d6651C4051e65B21AA4a575F3f99b8B83'), from_address=deserialize_ethereum_address('0x65003947dC16956AfC4400008606001500940000'), to_address=deserialize_ethereum_address('0x7860E28ebFB8Ae052Bfe279c07aC5d94c9cD2937'), timestamp=Timestamp(1614094145), location=Location.BALANCER, token0=EthereumToken('USDC'), token1=EthereumToken('AMPL'), amount0_in=AssetAmount(FVal('12401.224639')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('14285.153512382')), ), ] TEST_SWAPS_TX_2 = [ AMMSwap( tx_hash='0x1b0d3525964d8e5fbcc0dcdeebcced4bec9017f648e97c3c9761fda1ca6e7b22', log_index=254, address=deserialize_ethereum_address('0x8e670b4d6651C4051e65B21AA4a575F3f99b8B83'), from_address=deserialize_ethereum_address('0x65003947dC16956AfC4400008606001500940000'), to_address=deserialize_ethereum_address('0x7860E28ebFB8Ae052Bfe279c07aC5d94c9cD2937'), timestamp=Timestamp(1614094145),
def get_balancer_test_addr2_expected_trades(): """In a function since the new(unknown) assets needs to have been loaded in the DB""" A_WCRES = EthereumToken.initialize( # noqa: N806 address=string_to_ethereum_address( '0xa0afAA285Ce85974c3C881256cB7F225e3A1178a'), decimals=18, symbol='wCRES', ) return [ AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_AAVE, amount=AssetAmount(FVal('1.616934038985744521')), rate=Price(FVal('6.963972908793392530935439799')), trade_index=1, swaps=[ AMMSwap( tx_hash= '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589', # noqa: E501 log_index=24, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546' ), # noqa: E501 timestamp=Timestamp(1607008178), location=Location.BALANCER, token0=A_AAVE, token1=A_WETH, amount0_in=AssetAmount(FVal('11.260284842802604032')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('1.616934038985744521')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_AAVE, quote_asset=A_WETH, amount=AssetAmount(FVal('11.260286362820602094')), rate=Price(FVal('0.1416068599966922676173010716')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589', # noqa: E501 log_index=18, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x70985E557aE0CD6dC88189a532e54FbC61927BAd' ), # noqa: E501 timestamp=Timestamp(1607008178), location=Location.BALANCER, token0=A_WETH, token1=A_AAVE, amount0_in=AssetAmount(FVal('1.594533794502600192')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('11.260286362820602094')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_SYN, amount=AssetAmount(FVal('1.352902561458047718')), rate=Price(FVal('724.4303350385182691258363763')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x5e235216cb03e4eb234014f5ccf3efbfddd40c4576424e2a8204f1d12b96ed35', # noqa: E501 log_index=143, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x8982E9bBf7AC6A49c434aD81D2fF8e16895318e5' ), # noqa: E501 timestamp=Timestamp(1607008218), location=Location.BALANCER, token0=A_SYN, token1=A_WETH, amount0_in=AssetAmount(FVal('980.08365587152306176')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('1.352902561458047718')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_WCRES, amount=AssetAmount(FVal('0.205709519074945018')), rate=Price(FVal('232.7409943164679514496089589')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xf54be824b4619777f1db0e3da91b0cd52f6dba730c95a75644e2b085e6ab9824', # noqa: E501 log_index=300, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x10996eC4f3E7A1b314EbD966Fa8b1ad0fE0f8307' ), # noqa: E501 timestamp=Timestamp(1607009877), location=Location.BALANCER, token0=A_WCRES, token1=A_WETH, amount0_in=AssetAmount(FVal('47.87703800986513408')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.205709519074945018')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_API3, quote_asset=A_WETH, amount=AssetAmount(FVal('295.881648100500428692')), rate=Price(FVal('0.003346787723157288562491614498')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xfed4e15051e3ce4dc0d2816f719701e5920e40bf41614b5feaa3c5a6a0186c03', # noqa: E501 log_index=22, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x997c0fc9578a8194EFDdE2E0cD7aa6A69cFCD7c1' ), # noqa: E501 timestamp=Timestamp(1607010888), location=Location.BALANCER, token0=A_WETH, token1=A_API3, amount0_in=AssetAmount(FVal('0.990253067370299904')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('295.881648100500428692')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_MFT, amount=AssetAmount(FVal('0.686544199299304057')), rate=Price(FVal('243775.0324093115004367119900')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xf0147c4b81098676c08ae20ae5bf8f8b60d0ad79eec484f3f93ac6ab49a3c51c', # noqa: E501 log_index=97, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x2Eb6CfbFFC8785Cd0D9f2d233d0a617bF4269eeF' ), # noqa: E501 timestamp=Timestamp(1607015059), location=Location.BALANCER, token0=A_MFT, token1=A_WETH, amount0_in=AssetAmount(FVal('167362.334434612660404224')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.686544199299304057')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_AAVE, amount=AssetAmount(FVal('3.055412574642681758')), rate=Price(FVal('6.916116208273240607778771150')), trade_index=1, swaps=[ AMMSwap( tx_hash= '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b', # noqa: E501 log_index=37, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x0E552307659E70bF61f918f96AA880Cdec40d7E2' ), # noqa: E501 timestamp=Timestamp(1607015339), location=Location.BALANCER, token0=A_AAVE, token1=A_WETH, amount0_in=AssetAmount(FVal('21.131588430448123904')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('3.055412574642681758')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_AAVE, quote_asset=A_WETH, amount=AssetAmount(FVal('21.131588567541018817')), rate=Price(FVal('0.1435213742524287826717337545')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b', # noqa: E501 log_index=31, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546' ), # noqa: E501 timestamp=Timestamp(1607015339), location=Location.BALANCER, token0=A_WETH, token1=A_AAVE, amount0_in=AssetAmount(FVal('3.0328346313504')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('21.131588567541018817')), ), ], ), ]
from rotkehlchen.utils.interfaces import EthereumModule from rotkehlchen.utils.misc import address_to_bytes32, hex_or_bytes_to_int, ts_now if TYPE_CHECKING: from rotkehlchen.chain.ethereum.manager import EthereumManager from rotkehlchen.chain.ethereum.zerion import GIVEN_DEFI_BALANCES, DefiProtocolBalances from rotkehlchen.db.dbhandler import DBHandler BLOCKS_PER_YEAR = 2425846 YEARN_VAULTS = { 'yyDAI+yUSDC+yUSDT+yTUSD': YearnVault( name='YCRV Vault', contract=YEARN_YCRV_VAULT, underlying_token=EthereumToken('yDAI+yUSDC+yUSDT+yTUSD'), token=EthereumToken('yyDAI+yUSDC+yUSDT+yTUSD'), ), 'yDAI': YearnVault( name='YDAI Vault', contract=YEARN_DAI_VAULT, underlying_token=EthereumToken('DAI'), token=EthereumToken('yDAI'), ), 'yWETH': YearnVault( name='YWETH Vault', contract=YEARN_WETH_VAULT, underlying_token=EthereumToken('WETH'), token=EthereumToken('yWETH'),