Skip to content

Commit

Permalink
Query monerium on re-decode
Browse files Browse the repository at this point in the history
  • Loading branch information
yabirgb authored and LefterisJP committed Jun 26, 2024
1 parent 568c8c1 commit 70321a9
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 15 deletions.
10 changes: 9 additions & 1 deletion rotkehlchen/api/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
query_gearbox_data,
save_gearbox_data_to_cache,
)
from rotkehlchen.chain.evm.decoding.monerium.constants import CPT_MONERIUM
from rotkehlchen.chain.evm.decoding.velodrome.velodrome_cache import (
query_velodrome_like_data,
save_velodrome_data_to_cache,
Expand Down Expand Up @@ -182,6 +183,7 @@
from rotkehlchen.exchanges.data_structures import Trade
from rotkehlchen.exchanges.utils import query_binance_exchange_pairs
from rotkehlchen.externalapis.github import Github
from rotkehlchen.externalapis.monerium import init_monerium
from rotkehlchen.fval import FVal
from rotkehlchen.globaldb.assets_management import export_assets_from_file, import_assets_from_file
from rotkehlchen.globaldb.cache import (
Expand Down Expand Up @@ -2820,12 +2822,18 @@ def decode_evm_transaction(
}

try:
chain_manager.transactions_decoder.decode_transaction_hashes(
events = chain_manager.transactions_decoder.decode_transaction_hashes(
tx_hashes=[tx_hash],
send_ws_notifications=True,
ignore_cache=True, # always redecode from here
delete_customized=True, # also delete customized events from here
)
if (
any(event.counterparty == CPT_MONERIUM for event in events) and
(monerium := init_monerium(self.rotkehlchen.data.db)) is not None
):
monerium.get_and_process_orders(tx_hash=tx_hash)

# Trigger the task to query the missing prices for the decoded events
events_filter = EvmEventFilterQuery.make(
tx_hashes=[tx_hash], # always same hash
Expand Down
30 changes: 26 additions & 4 deletions rotkehlchen/externalapis/monerium.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from rotkehlchen.errors.misc import RemoteError
from rotkehlchen.history.events.structures.types import HistoryEventSubType, HistoryEventType
from rotkehlchen.logging import RotkehlchenLogsAdapter
from rotkehlchen.types import Location, deserialize_evm_tx_hash
from rotkehlchen.types import EVMTxHash, Location, deserialize_evm_tx_hash
from rotkehlchen.utils.misc import set_user_agent, ts_now
from rotkehlchen.utils.serialization import jsonloads_list

Expand Down Expand Up @@ -40,13 +40,19 @@ def __init__(self, database: 'DBHandler', user: str, password: str) -> None:
def _query(
self,
endpoint: Literal['orders'],
params: dict[str, Any] | None = None,
) -> list[dict[str, Any]]:
"""Query a monerium API endpoint with basic authenication"""
querystr = 'https://api.monerium.app/' + endpoint
log.debug(f'Querying monerium API {querystr}')
timeout = CachedSettings().get_timeout_tuple()
try:
response = self.session.get(querystr, timeout=timeout, auth=(self.user, self.password))
response = self.session.get(
url=querystr,
params=params,
timeout=timeout,
auth=(self.user, self.password),
)
except requests.exceptions.RequestException as e:
raise RemoteError(f'Querying {querystr} failed due to {e!s}') from e

Expand All @@ -66,7 +72,7 @@ def _query(

return json_ret

def get_and_process_orders(self) -> None:
def get_and_process_orders(self, tx_hash: EVMTxHash | None = None) -> None:
"""Gets all monerium orders and processes them
Find all on-chain transactions that match those orders and enrich them appropriately.
Expand All @@ -93,7 +99,10 @@ def get_and_process_orders(self) -> None:
(DBCacheStatic.LAST_MONERIUM_QUERY_TS.value, str(ts_now())),
)

orders = self._query(endpoint='orders')
orders = self._query(
endpoint='orders',
params={'txHash': tx_hash.hex()} if tx_hash is not None else None,
)
dbevents = DBHistoryEvents(self.database)
for order in orders: # orders are returned latest first
log.debug(f'Processing monerium order {order}')
Expand Down Expand Up @@ -185,3 +194,16 @@ def get_and_process_orders(self) -> None:
bindings.append(events[0].identifier)
with self.database.user_write() as write_cursor:
write_cursor.execute(querystr, bindings)


def init_monerium(database: 'DBHandler') -> Monerium | None:
"""Create a monerium instance using the provided database"""
with database.conn.read_ctx() as cursor:
result = cursor.execute(
'SELECT api_key, api_secret FROM external_service_credentials WHERE name=?',
('monerium',),
).fetchone()
if result is None:
return None

return Monerium(database=database, user=result[0], password=result[1])
12 changes: 3 additions & 9 deletions rotkehlchen/tasks/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
from rotkehlchen.errors.api import PremiumAuthenticationError
from rotkehlchen.errors.asset import UnknownAsset, WrongAssetType
from rotkehlchen.errors.misc import RemoteError
from rotkehlchen.externalapis.monerium import Monerium
from rotkehlchen.externalapis.monerium import init_monerium
from rotkehlchen.globaldb.handler import GlobalDBHandler
from rotkehlchen.history.events.structures.evm_event import EvmEvent
from rotkehlchen.history.events.structures.types import EventDirection
Expand Down Expand Up @@ -860,15 +860,9 @@ def _maybe_query_monerium(self) -> Optional[list[gevent.Greenlet]]:
if should_run_periodic_task(self.database, DBCacheStatic.LAST_MONERIUM_QUERY_TS, HOUR_IN_SECONDS) is False: # noqa: E501
return None

with self.database.conn.read_ctx() as cursor:
result = cursor.execute(
'SELECT api_key, api_secret FROM external_service_credentials WHERE name=?',
('monerium',),
).fetchone()
if result is None:
return None
if (monerium := init_monerium(self.database)) is None:
return None

monerium = Monerium(database=self.database, user=result[0], password=result[1])
return [self.greenlet_manager.spawn_and_track(
after_seconds=None,
task_name='Query monerium',
Expand Down
64 changes: 63 additions & 1 deletion rotkehlchen/tests/external_apis/test_monerium.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@

import gevent
import pytest
import requests

from rotkehlchen.accounting.structures.balance import Balance
from rotkehlchen.api.server import APIServer
from rotkehlchen.chain.evm.decoding.monerium.constants import CPT_MONERIUM
from rotkehlchen.constants.assets import A_ETH_EURE
from rotkehlchen.db.filtering import EvmEventFilterQuery
from rotkehlchen.constants.misc import ONE
from rotkehlchen.db.filtering import EvmEventFilterQuery, HistoryEventFilterQuery
from rotkehlchen.db.history_events import DBHistoryEvents
from rotkehlchen.fval import FVal
from rotkehlchen.history.events.structures.evm_event import EvmEvent
from rotkehlchen.history.events.structures.types import HistoryEventSubType, HistoryEventType
from rotkehlchen.tests.utils.api import api_url_for, assert_proper_response
from rotkehlchen.tests.utils.mock import MockResponse
from rotkehlchen.types import ExternalService, Location, TimestampMS, deserialize_evm_tx_hash

Expand Down Expand Up @@ -192,3 +196,61 @@ def test_bridge_via_monerium(task_manager, database, monerium_credentials): # p
has_premium=True,
)
assert new_events == [gnosis_event, eth_event]


@pytest.mark.parametrize('default_mock_price_value', [ONE])
def test_query_info_on_redecode_request(rotkehlchen_api_server: APIServer):
"""Test that triggering a re-decode for a monerium transaction updates correctly the notes"""
database = rotkehlchen_api_server.rest_api.rotkehlchen.data.db
dbevents = DBHistoryEvents(database)
amount_str = '1500'
gnosishash = deserialize_evm_tx_hash(val='0x10d953610921f39d9d20722082077e03ec8db8d9c75e4b301d0d552119fd0354') # noqa: E501
gnosis_user_address = '0xbCCeE6Ff2bCAfA95300D222D316A29140c4746da'
gnosis_event = EvmEvent(
tx_hash=gnosishash,
sequence_index=113,
timestamp=TimestampMS(1701765059000),
location=Location.GNOSIS,
event_type=HistoryEventType.SPEND,
event_subtype=HistoryEventSubType.NONE,
asset=A_ETH_EURE,
balance=Balance(amount=FVal(amount_str)),
location_label=gnosis_user_address,
notes=f'Burn {amount_str} EURe',
counterparty=CPT_MONERIUM,
)
with database.user_write() as write_cursor:
write_cursor.execute( # not using the fixture since it has issues with the api
'INSERT OR REPLACE INTO external_service_credentials(name, api_key, api_secret) '
'VALUES(?, ?, ?)',
(ExternalService.MONERIUM.name.lower(), 'mockuser', 'mockpassword'),
)

def add_event(*args, **kwargs): # pylint: disable=unused-argument
with database.user_write() as write_cursor:
dbevents.add_history_events(write_cursor=write_cursor, history=[gnosis_event])
return [gnosis_event]

response_txt = '[{"id":"YYYY","profile":"PP","accountId":"PP","address":"0xbCCeE6Ff2bCAfA95300D222D316A29140c4746da","kind":"redeem","amount":"2353.57","currency":"eur","totalFee":"0","fees":[],"counterpart":{"details":{"name":"Yabir Benchakhtir","country":"ES","lastName":"Benchakhtir","firstName":"Yabir"},"identifier":{"iban":"ESXX KKKK OOOO IIII KKKK LLLL","standard":"iban"}},"memo":"Venta inversion","supportingDocumentId":"","chain":"gnosis","network":"mainnet","txHashes":["0x10d953610921f39d9d20722082077e03ec8db8d9c75e4b301d0d552119fd0354"],"meta":{"state":"processed","placedBy":"ii","placedAt":"2024-04-19T13:45:00.287212Z","processedAt":"2024-04-19T13:45:00.287212Z","approvedAt":"2024-04-19T13:45:00.287212Z","confirmedAt":"2024-04-19T13:45:00.287212Z","receivedAmount":"2353.57","sentAmount":"2353.57"}}]' # noqa: E501
with (
patch('requests.Session.get', side_effect=lambda *args, **kwargs: MockResponse(200, response_txt)), # noqa: E501
patch('rotkehlchen.chain.evm.decoding.decoder.EVMTransactionDecoder.decode_transaction_hashes', new=add_event), # noqa: E501
patch('rotkehlchen.chain.evm.transactions.EvmTransactions.get_or_query_transaction_receipt', return_value=None), # noqa: E501
):
response = requests.put(
api_url_for(
rotkehlchen_api_server,
'evmtransactionsresource',
),
json={'evm_chain': 'gnosis', 'tx_hash': gnosishash.hex()}, # pylint: disable=no-member
)
assert_proper_response(response)

with database.conn.read_ctx() as cursor:
events = dbevents.get_history_events(
cursor=cursor,
filter_query=HistoryEventFilterQuery.make(),
has_premium=True,
)

assert events[0].notes == 'Send 2353.57 EURe via bank transfer to Yabir Benchakhtir (ESXX KKKK OOOO IIII KKKK LLLL) with memo "Venta inversion"' # noqa: E501

0 comments on commit 70321a9

Please sign in to comment.