diff --git a/handler/transactions.py b/handler/transactions.py index 7aef36a..cbcc991 100644 --- a/handler/transactions.py +++ b/handler/transactions.py @@ -76,10 +76,60 @@ class Transactions(object): self.log.info("Transaction processed: {formatted}", formatted=dumps(to_store, indent=2)) r.hmset(f"tx.{txid}", to_store) self.irc.sendmsg(f"AUTO Incoming transaction: {amount}{currency} ({reference}) - {state} - {description}") - # Try getting the trade by the reference ID given - stored_trade = self.get_ref(reference) + + # Partial reference implementation + # Account for silly people not removing the default string + # Split the reference into parts + ref_split = reference.split(" ") + # Get all existing references + existing_refs = self.get_refs() + # Get all parts of the given reference split that match the existing references + stored_trade_reference = set(existing_refs).intersection(set(ref_split)) + if len(stored_trade_reference) > 1: + self.log.error("Multiple references valid for TXID {txid}: {reference}", txid=txid, reference=reference) + self.irc.sendmsg(f"Multiple references valid for TXID {txid}: {reference}") + return + + stored_trade = False + looked_up_without_reference = False + + # Amount/currency lookup implementation + if not stored_trade_reference: + self.log.info(f"No reference in DB refs for {reference}", reference=reference) + self.irc.sendmsg(f"No reference in DB refs for {reference}") + # Try checking just amount and currency, as some people (usually people buying small amounts) + # are unable to put in a reference properly. + + self.log.info("Checking against amount and currency for TXID {txid}", txid=txid) + self.irc.sendmsg(f"Checking against amount and currency for TXID {txid}") + stored_trade = self.find_trade(txid, currency, amount) + if not stored_trade: + self.log.info( + "Failed to get reference by amount and currency: {txid} {currency} {amount}", + txid=txid, + currency=currency, + amount=amount, + ) + self.irc.sendmsg(f"Failed to get reference by amount and currency: {txid} {currency} {amount}") + return + if currency == "USD": + amount_usd = amount + else: + rates = self.agora.get_rates_all() + amount_usd = amount / rates[currency] + # Amount is reliable here as it is checked by find_trade, so no need for stored_trade["amount"] + if float(amount_usd) > float(settings.Agora.AcceptableAltLookupUSD): + self.log.info("Not checking against amount and currency as amount exceeds MAX") + self.irc.sendmsg(f"Not checking against amount and currency as amount exceeds MAX") + # Close here if the amount exceeds the allowable limit for no reference + return + # Note that we have looked it up without reference so we don't use +- below + # This might be redundant given the amount checks in find_trade, but better safe than sorry! + looked_up_without_reference = True if not stored_trade: - self.log.info(f"No reference in DB for {reference}", reference=reference) + stored_trade = self.get_ref(stored_trade_reference.pop()) + if not stored_trade: + self.log.info("No reference in DB for {reference}", reference=reference) self.irc.sendmsg(f"No reference in DB for {reference}") return @@ -88,25 +138,45 @@ class Transactions(object): # Make sure it was sent in the expected currency if not stored_trade["currency"] == currency: + self.log.info( + "Currency mismatch, Agora: {currency_agora} / Revolut: {currency}", + currency_agora=stored_trade["currency"], + currency=currency, + ) self.irc.sendmsg(f"Currency mismatch, Agora: {stored_trade['currency']} / Revolut: {currency}") return # Make sure the expected amount was sent if not stored_trade["amount"] == amount: + if looked_up_without_reference: + return # If the amount does not match exactly, get the min and max values for our given acceptable margins for trades min_amount, max_amount = self.agora.get_acceptable_margins(currency, amount) + self.log.info( + "Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}", + min_amount=min_amount, + max_amount=max_amount, + ) self.irc.sendmsg(f"Amount does not match exactly, trying with margins: min: {min_amount} / max: {max_amount}") if not min_amount < stored_trade["amount"] < max_amount: + self.log.info( + "Amount mismatch - not in margins: {amount} (min: {min_amount} / max: {max_amount}", + amount=stored_trade["amount"], + min_amount=min_amount, + max_amount=max_amount, + ) self.irc.sendmsg(f"Amount mismatch - not in margins: {stored_trade['amount']} (min: {min_amount} / max: {max_amount}") return # Make sure the account type was Revolut, as these are completed instantly if not account_type == "revolut": + self.log.info("Account type is not Revolut: {account_type}", account_type=account_type) self.irc.sendmsg(f"Account type is not Revolut: {account_type}") return + self.log.info("All checks passed, releasing funds for {trade_id} {reference}", trade_id=stored_trade["id"], reference=reference) self.irc.sendmsg(f"All checks passed, releasing funds for {stored_trade['id']} / {reference}") - rtrn = self.agora.release_funds(stored_trade["id"]) - self.agora.agora.contact_message_post(stored_trade["id"], "Thanks! Releasing now :)") - self.irc.sendmsg(dumps(rtrn)) + # rtrn = self.agora.release_funds(stored_trade["id"]) + # self.agora.agora.contact_message_post(stored_trade["id"], "Thanks! Releasing now :)") + # self.irc.sendmsg(dumps(rtrn)) def new_trade(self, trade_id, buyer, currency, amount, amount_xmr): """ @@ -156,6 +226,30 @@ class Transactions(object): return "AMOUNT_INVALID" return False + def find_trade(self, txid, currency, amount): + """ + Get a trade reference that matches the given currency and amount. + Only works if there is one result. + :param txid: Revolut transaction ID + :param currency: currency + :param amount: amount + :type txid: string + :type currency: string + :type amount: int + :return: matching trade object or False + :rtype: dict or bool + """ + refs = self.get_refs() + matching_refs = [] + for ref in refs: + stored_trade = self.get_ref(ref) + if stored_trade["currency"] == currency and float(stored_trade["amount"]) == float(amount): + matching_refs.append(stored_trade) + if len(matching_refs) != 1: + self.log.error("Find trade returned multiple results for TXID {txid}: {matching_refs}", txid=txid, matching_refs=matching_refs) + return False + return matching_refs[0] + def get_refs(self): """ Get all reference IDs for trades.