diff --git a/core/management/commands/show_balances.py b/core/management/commands/show_balances.py new file mode 100644 index 0000000..926c051 --- /dev/null +++ b/core/management/commands/show_balances.py @@ -0,0 +1,46 @@ +import json +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from core.monero import AuctionWallet +from sales.models import ItemSale + + +class Command(BaseCommand): + help = 'Shows balances of all sale items' + + def add_arguments(self, parser): + parser.add_argument('-a', '--all', action='store_true', help='Whether or not to scan whole wallet vs item sales', default=False) + + def handle(self, *args, **kwargs): + aw = AuctionWallet() + if aw.connected is False: + raise Exception('Unable to connect to auction wallet RPC endpoint.') + + msg = [] + + if kwargs.get('all', False): + self.stdout.write(self.style.SUCCESS(len(aw.wallet.accounts))) + for index,account in enumerate(aw.wallet.accounts): + msg.append({ + "index": index, + "address": str(account.address()), + "locked_balance": str(account.balances()[0]), + "unlocked_balance": str(account.balances()[1]), + "outgoing": account.outgoing(), + "incoming": account.incoming(), + }) + else: + item_sales = ItemSale.objects.all() + for sale in item_sales: + w = aw.wallet.accounts[sale.escrow_account_index] + msg.append({ + "sale_id": sale.id, + "address": str(w.address()), + "locked_balance": str(w.balances()[0]), + "unlocked_balance": str(w.balances()[1]), + "outgoing": w.outgoing(), + "incoming": w.incoming(), + }) + + for i in msg: + self.stdout.write(self.style.SUCCESS(i)) diff --git a/core/management/commands/show_mempool.py b/core/management/commands/show_mempool.py new file mode 100644 index 0000000..6280941 --- /dev/null +++ b/core/management/commands/show_mempool.py @@ -0,0 +1,17 @@ +import json +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from core.monero import AuctionDaemon +from sales.models import ItemSale + + +class Command(BaseCommand): + help = 'Shows mempool' + + def handle(self, *args, **kwargs): + ad = AuctionDaemon() + if ad.connected is False: + raise Exception('Unable to connect to auction daemon RPC endpoint.') + + msg = str(ad.daemon.mempool()) + self.stdout.write(self.style.SUCCESS(msg)) diff --git a/items/forms.py b/items/forms.py index 58d17d6..46b8c80 100644 --- a/items/forms.py +++ b/items/forms.py @@ -7,11 +7,12 @@ class CreateItemForm(forms.ModelForm): class Meta: model = Item - fields = ['name', 'description', 'ask_price_xmr', 'payout_address'] + fields = ['name', 'description', 'whereabouts', 'ask_price_xmr', 'payout_address'] labels = { 'ask_price_xmr': 'Asking Price (XMR)', 'payout_address': 'Payout Wallet Address' } help_texts = { - 'payout_address': 'Monero address where funds will be sent after sale is confirmed' + 'payout_address': 'Monero address where funds will be sent after sale is confirmed', + 'whereabouts': 'A simple pointer to your general region - a nearby ZIP code would be perfect.' } diff --git a/items/migrations/0002_item_whereabouts.py b/items/migrations/0002_item_whereabouts.py new file mode 100644 index 0000000..816987e --- /dev/null +++ b/items/migrations/0002_item_whereabouts.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.8 on 2020-01-10 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('items', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='item', + name='whereabouts', + field=models.CharField(default=0, max_length=100), + preserve_default=False, + ), + ] diff --git a/items/models.py b/items/models.py index dcfab6f..b02066e 100644 --- a/items/models.py +++ b/items/models.py @@ -19,6 +19,7 @@ class Item(models.Model): ask_price_xmr = models.FloatField() available = models.BooleanField(default=True) payout_address = models.CharField(max_length=100, validators=[address_is_valid_monero]) + whereabouts = models.CharField(max_length=100) def __str__(self): return f"{self.id} - {self.owner} - {self.name}" diff --git a/sales/tasks.py b/sales/tasks.py index 88bae2d..a886ff5 100644 --- a/sales/tasks.py +++ b/sales/tasks.py @@ -121,7 +121,7 @@ def notify_seller_of_funds_received(): else: return False -@periodic_task(crontab(minute='*/30')) +@periodic_task(crontab(minute='*/1')) def pay_sellers_on_sold_items(): aw = AuctionWallet() if aw.connected is False: @@ -129,6 +129,23 @@ def pay_sellers_on_sold_items(): item_sales = ItemSale.objects.filter(item_received=True, payment_received=True).filter(seller_paid=False) for sale in item_sales: + try: + print(f'sending agreed payment, {sale.agreed_price_xmr} XMR, from account index #{sale.escrow_account_index} to {sale.item.owner.username} for sale #{sale.id} ({sale.item.name})') + sale_account = aw.wallet.accounts[sale.escrow_account_index] + if sale_account.balances()[1] > Decimal(0.0): + aw.wallet.accounts[sale.escrow_account_index].transfer( + sale.item.payout_address, sale.agreed_price_xmr, relay=True + ) + sale.seller_paid = True + sale.escrow_complete = True + sale.save() + else: + print('not enough funds here to transfer. try later') + return False + except Exception as e: + print('unable to make payment: ', e) + return False + email_template = EmailTemplate( item=sale, scenario='sale_completed', @@ -140,22 +157,7 @@ def pay_sellers_on_sold_items(): sale.seller_notified_of_payout = True sale.save() - try: - txs = aw.wallet.accounts[sale.escrow_account_index].transfer( - sale.item.payout_address, sale.agreed_price_xmr, relay=True - ) - if txs: - sale.seller_paid = True - sale.escrow_complete = True - sale.save() - return True - else: - return False - except Exception as e: - print('unable to make payment: ', e) - return False - -@periodic_task(crontab(hour='*/6')) +@periodic_task(crontab(minute='*')) def pay_platform_on_sold_items(): aw = AuctionWallet() if aw.connected is False: @@ -163,17 +165,22 @@ def pay_platform_on_sold_items(): aof = settings.PLATFORM_WALLET_ADDRESS if aof is None: - aof = aw.wallet.accounts[0].address() + aof = str(aw.wallet.accounts[0].address()) item_sales = ItemSale.objects.filter(escrow_complete=True, seller_paid=True, item_received=True).filter(platform_paid=False) for sale in item_sales: + sale_account = aw.wallet.accounts[sale.escrow_account_index] try: - txs = aw.wallet.accounts[sale.escrow_account_index].sweep_all(aof) - if txs: + if sale_account.balances()[1] >= Decimal(0.0): + print(f'paying out platform wallet, {aof}, remaining funds in account #{sale.escrow_account_index} for sale #{sale.id}') + aw.wallet.accounts[sale.escrow_account_index].sweep_all(aof) sale.platform_paid = True sale.sale_finalized = True sale.save() return True + else: + print('not enough funds here to sweep. try later') + return False except Exception as e: print('unable to sweep funds: ', e) return False diff --git a/web/templates/items/get_item.html b/web/templates/items/get_item.html index 42a26e7..d6b9dde 100644 --- a/web/templates/items/get_item.html +++ b/web/templates/items/get_item.html @@ -10,7 +10,7 @@
{% if not item.available %}

This item is currently pending sale. Bidding is temporarily closed.

{% endif %} -

Whereabouts: Whereabouts

+

Whereabouts: {{ item.whereabouts }}

Creation: {{ item.list_date | date:"d M Y H:i:s" }}

Last Updated: {{ item.last_updated | date:"d M Y H:i:s" }}

Description: {{ item.description }}