Skip to content
Open
Show file tree
Hide file tree
Changes from 62 commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
9236481
Adding web3modal as a build package for now, changing multiple receiv…
mikulas-mrva May 20, 2022
5d62b24
removing wallet upload models and views, adding wallet connection js,…
mikulas-mrva May 22, 2022
01a04f1
Added SignedMessage model, view and serializer to send payment detail…
mikulas-mrva May 30, 2022
b106f91
Finished the FE code outline, added Permissions to allow POST and GET…
mikulas-mrva May 30, 2022
dcdb640
Switching to the correect way of signing a message with Metamask
mikulas-mrva May 31, 2022
7951816
Changing data signing to typed data
mikulas-mrva Jul 2, 2022
40f9028
Fixing data signing
mikulas-mrva Jul 3, 2022
f11ad3b
Confirming payments, deleting unused permissions module
mikulas-mrva Jul 4, 2022
f6d1579
Improving user flow of the payment process, displaying a message when…
mikulas-mrva Jul 7, 2022
6bf8d96
Missing migration
mikulas-mrva Jul 8, 2022
cba6705
Changing raw_message to text field to circumvent field length limitat…
mikulas-mrva Jul 8, 2022
c0bd7e0
Moving retry triggers from inline to js, adding transaction confirmat…
mikulas-mrva Jul 12, 2022
eefabbb
Handling user dialogue cancellations gracefully.
mikulas-mrva Jul 13, 2022
c958df3
Do not hide success message if the transaction was submitted successf…
mikulas-mrva Jul 14, 2022
0f531d0
chain_id should be larger than uint8 to accomodate all chains
mikulas-mrva Jul 14, 2022
1a6a8df
Fixing a typo, using signTypedData_v4 for signing
mikulas-mrva Jul 14, 2022
3760617
Network name should not be uppercase in _RPC_URL
mikulas-mrva Jul 14, 2022
24ad2d7
Extending chain id field
mikulas-mrva Jul 14, 2022
7fc5597
Bug fixes
mikulas-mrva Jul 14, 2022
5b125b6
Don't let the user pay for multiple order payments wwithin one order
mikulas-mrva Jul 14, 2022
964eacc
Disabling refunds
mikulas-mrva Jul 14, 2022
1e71c56
fixes:
mikulas-mrva Jul 14, 2022
e47ced1
Confirming ERC20 payments
mikulas-mrva Jul 14, 2022
12cb449
Don't confirm payments based on blocks that are too young
mikulas-mrva Jul 14, 2022
ead478f
Display transaction hash on refresh, display transaction details if t…
mikulas-mrva Jul 14, 2022
ba270eb
Add ERC20 balance check to prevent most failing transactions to be su…
mikulas-mrva Jul 15, 2022
39f197f
Automatically confirming payments, ignoring payments that didn't go t…
mikulas-mrva Jul 16, 2022
a4329ec
Preventing a payment to go through in case of insufficient balance er…
mikulas-mrva Jul 16, 2022
3746b18
Only display "please sign a message dialogue" in case it's about to p…
mikulas-mrva Jul 16, 2022
a6cfe42
Only display "please confirm a transaction" right before a transactio…
mikulas-mrva Jul 16, 2022
4e07d66
Optimizing "order_accepting_payments" criteria into a single db query
mikulas-mrva Jul 16, 2022
0d5bc6a
Fixing AttributeError: Manager isn't accessible via SignedMessage ins…
mikulas-mrva Jul 17, 2022
8117a5b
Using getReceipt in both ETH and DAI payment confirmation
mikulas-mrva Jul 17, 2022
64e6048
Using getReceipt in both ETH and DAI payment confirmation
mikulas-mrva Jul 17, 2022
58b1910
refactoring confirm_payments to use reeceieptsto check transaction st…
mikulas-mrva Jul 17, 2022
6a551ed
Refactoring js, splitting into multiple files
mikulas-mrva Aug 8, 2022
a12f5fa
Refactoring js, splitting into multiple files, fixes
mikulas-mrva Aug 9, 2022
a81c8d4
Periodic payment status change check should be running even if user r…
mikulas-mrva Aug 9, 2022
d016a4a
bugfixes
mikulas-mrva Aug 11, 2022
0d736ba
Deleting confirm_refunds.py management command as there are no refund…
mikulas-mrva Aug 23, 2022
5bc664d
Adding an endpoint that can access the status of an Order without aut…
mikulas-mrva Aug 29, 2022
14127b8
Updating Web3.js to a new version 1.7.5
mikulas-mrva Sep 6, 2022
9ec84db
Updating Web3Modal to a hotfixed version branched out from latest ver…
mikulas-mrva Sep 7, 2022
7bf5cc3
Adding *.infura.io to CSP
mikulas-mrva Sep 7, 2022
a3996a5
Fixing some CSP issues, adding signed message data to Order detail co…
mikulas-mrva Sep 7, 2022
9d2d8d3
Updating CSP and fixing javascript issues that require a double click…
mikulas-mrva Sep 12, 2022
9fdefbd
Handling missing providers gracefully, deleting a ton of debuggers;
mikulas-mrva Sep 12, 2022
e855d61
Bugfix: retrieving web3modal correctly
mikulas-mrva Sep 13, 2022
0085c61
Bugfix: retrieving web3modal correctly
mikulas-mrva Sep 13, 2022
d44866d
bugfixes
mikulas-mrva Sep 13, 2022
34c762b
bugfixes - two different kinds of provider
mikulas-mrva Sep 20, 2022
9cd0d30
adding loggers to confirm_payment mgmt commands
mikulas-mrva Sep 20, 2022
a52d961
adding loggers to confirm_payment mgmt commands, log level changed to…
mikulas-mrva Sep 20, 2022
847e251
Also checking for cancelled OrderPayments
mikulas-mrva Sep 20, 2022
e22c94e
ERC20ABIView should be public
mikulas-mrva Sep 20, 2022
acda5b8
Confirming DAI payments using the correct received address
mikulas-mrva Sep 20, 2022
e0eb0b3
Checking ERC20 contract address as well
mikulas-mrva Sep 20, 2022
3761064
Changing `url` template tag to `eventurl event`
mikulas-mrva Sep 20, 2022
da3860a
Changing `url` template tag to `eventurl event`
mikulas-mrva Sep 20, 2022
b06f143
Views should be compatible with multidomain as well as signel event p…
mikulas-mrva Sep 20, 2022
996d36d
Arbitrum DAI should have a checksum address
mikulas-mrva Sep 20, 2022
0afc1f1
All DAI addresses should be checksum addresses
mikulas-mrva Sep 20, 2022
6dedcf4
* code cleanup
mikulas-mrva Sep 29, 2022
cd57833
adding links to control.html list of payments
mikulas-mrva Sep 30, 2022
382e944
Adding payment data to exports
mikulas-mrva Sep 30, 2022
54c97b0
Fixing tests
mikulas-mrva Sep 30, 2022
e31a0a9
Linter complaints
mikulas-mrva Sep 30, 2022
d21ada1
Linter complaints
mikulas-mrva Sep 30, 2022
e5f1299
Removing Refunds from the exports logic, we don't support them anymore
mikulas-mrva Oct 11, 2022
f5060f4
Moving SAFETY_BLOCK_COUNT and PAYMENT_NOT_RECIEVED_RETRY_TIMEOUT valu…
mikulas-mrva Oct 11, 2022
366a1ba
Added a json validator for TOKEN_RATES field
mikulas-mrva Oct 11, 2022
49387f0
Removing an unused constant
mikulas-mrva Oct 11, 2022
4999c6a
Fixing a template bug
mikulas-mrva Nov 3, 2022
409c954
Rinkeby in Peace
mikulas-mrva Nov 3, 2022
3495877
safety_block_count can be None
mikulas-mrva Nov 3, 2022
9614138
Using Correct DAI contract
mikulas-mrva Nov 8, 2022
c7d9e2d
Adding Sepolia ETH to the config, not sure what the correct DAI contr…
mikulas-mrva Nov 8, 2022
82e5820
Putting Rinkeby back in place for Exports, added a DISABLED flag to p…
mikulas-mrva Nov 9, 2022
d320c79
Adding Token Rate at time of order to ETH payment export
mikulas-mrva Nov 9, 2022
8fe7659
Export token price in ether, not wei
mikulas-mrva Nov 10, 2022
79ab76c
Trying to fix infinite pip backtracking and adding python 3.10, 3.11 …
mikulas-mrva Dec 23, 2022
aaeb1ff
Fixinf dependency conflicts for Py<3.8
mikulas-mrva Dec 23, 2022
7fc5dcb
Temporarily disabling pip check
mikulas-mrva Dec 23, 2022
9c8df85
Fixing tests, pinning web3 to a beta of v6 temporarily to resolve an …
mikulas-mrva Dec 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions pretix_eth/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
OrderPayment,
OrderRefund,
)
from pretix_eth.models import WalletAddress

import pytz

Expand All @@ -27,7 +26,7 @@ def payment_to_row(payment):
fiat_amount = payment.amount
token_amount = payment.info_data.get("amount", "")

wallet_address = WalletAddress.objects.filter(order_payment=payment).first()
wallet_address = None # todo WalletAddress.objects.filter(order_payment=payment).first()
hex_wallet_address = wallet_address.hex_address if wallet_address else ""

row = [
Expand Down Expand Up @@ -57,7 +56,7 @@ def refund_to_row(refund):
fiat_amount = refund.amount
token_amount = refund.info_data.get("amount", "")

wallet_address = WalletAddress.objects.filter(order_payment=refund.payment).first()
wallet_address = None # todo WalletAddress.objects.filter(order_payment=refund.payment).first()
Comment thread
mikulas-mrva marked this conversation as resolved.
Outdated
Comment thread
mikulas-mrva marked this conversation as resolved.
Outdated
hex_wallet_address = wallet_address.hex_address if wallet_address else ""

row = [
Expand Down
166 changes: 116 additions & 50 deletions pretix_eth/management/commands/confirm_payments.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,21 @@
)
from django_scopes import scope

from pretix.base.models.event import (
Event,
)
from web3 import Web3
from web3.providers.auto import load_provider_from_uri
from web3.exceptions import TransactionNotFound

from pretix_eth.models import (
WalletAddress,
)
from pretix_eth.network.tokens import IToken, all_token_and_network_ids_to_tokens
from pretix.base.models import OrderPayment
from pretix.base.models.event import Event

from pretix_eth.network.tokens import IToken, all_token_and_network_ids_to_tokens, TOKEN_ABI

logger = logging.getLogger(__name__)


SAFETY_BLOCK_COUNT = 5
Comment thread
mikulas-mrva marked this conversation as resolved.
Outdated


class Command(BaseCommand):
help = (
"Verify pending orders from on-chain payments. Performs a dry run by default."
Expand All @@ -33,64 +36,127 @@ def add_arguments(self, parser):

def handle(self, *args, **options):
no_dry_run = options["no_dry_run"]
log_verbosity = int(options.get('verbosity', 0))

with scope(organizer=None):
# todo change to events where pending payments are expected only?
events = Event.objects.all()

for event in events:
self.confirm_payments_for_event(event, no_dry_run)
self.confirm_payments_for_event(event, no_dry_run, log_verbosity)

def confirm_payments_for_event(self, event: Event, no_dry_run):
def confirm_payments_for_event(self, event: Event, no_dry_run, log_verbosity=0):
logger.info(f"Event name - {event.name}")

unconfirmed_addresses = (
WalletAddress.objects.all().for_event(event).unconfirmed_orders()
)

for wallet_address in unconfirmed_addresses:
hex_address = wallet_address.hex_address

order_payment = wallet_address.order_payment
rpc_urls = json.loads(
order_payment.payment_provider.settings.NETWORK_RPC_URL
with scope(organizer=event.organizer):
unconfirmed_order_payments = OrderPayment.objects.filter(
order__event=event,
state__in=(
OrderPayment.PAYMENT_STATE_CREATED,
OrderPayment.PAYMENT_STATE_PENDING,
OrderPayment.PAYMENT_STATE_CANCELED,
)
)
full_id = order_payment.full_id

info = order_payment.info_data
token: IToken = all_token_and_network_ids_to_tokens[info["currency_type"]]
expected_network_id = token.NETWORK_IDENTIFIER
expected_network_rpc_url_key = f"{expected_network_id}_RPC_URL"
network_rpc_url = None

if expected_network_rpc_url_key in rpc_urls:
network_rpc_url = rpc_urls[expected_network_rpc_url_key]
else:
logger.warning(
f"No RPC URL configured for {expected_network_id}. Skipping..."
if log_verbosity > 0:
logger.info(f" * Found {unconfirmed_order_payments.count()} unconfirmed order payments")

for order_payment in unconfirmed_order_payments:
if log_verbosity > 0:
logger.info(f" * trying to confirm payment: {order_payment} (has {order_payment.signed_messages.all().count()} signed messages)")
# it is tempting to put .filter(invalid=False) here, but remember
# there is still a chance that low-gas txs are mined later on.
for signed_message in order_payment.signed_messages.all():
rpc_urls = json.loads(
order_payment.payment_provider.settings.NETWORK_RPC_URL
)
continue

expected_amount = info["amount"]
full_id = order_payment.full_id

# Get balance.
balance = token.get_balance_of_address(hex_address, network_rpc_url)
info = order_payment.info_data
token: IToken = all_token_and_network_ids_to_tokens[info["currency_type"]]
expected_network_id = token.NETWORK_IDENTIFIER
expected_network_rpc_url_key = f"{expected_network_id}_RPC_URL"

if balance > 0:
logger.info(f"Payments found for {full_id} at {hex_address}:")
if balance < expected_amount:
if expected_network_rpc_url_key in rpc_urls:
network_rpc_url = rpc_urls[expected_network_rpc_url_key]
else:
logger.warning(
f" * Expected payment of at least {expected_amount} {token.TOKEN_SYMBOL}"
f"No RPC URL configured for {expected_network_id}. Skipping..."
)
continue

expected_amount = info["amount"]

# Get balance
w3 = Web3(load_provider_from_uri(network_rpc_url))
if log_verbosity > 0:
logger.info(f" * Looking for a receip for a transaction with hash={signed_message.transaction_hash}")
try:
receipt = w3.eth.getTransactionReceipt(signed_message.transaction_hash)
except TransactionNotFound:
if log_verbosity > 0:
logger.info(f" * Transaction hash={signed_message.transaction_hash} not found, skipping.")
if signed_message.age > 30*60:
signed_message.invalidate()
continue

if receipt.status == 0:
if log_verbosity > 0:
logger.info(f" * Transaction hash={signed_message.transaction_hash} was has status=0, invalidating.")
signed_message.invalidate()
continue

block_number = receipt.blockNumber

if block_number is None or block_number + SAFETY_BLOCK_COUNT > w3.eth.get_block_number():
logger.warning(f" * Transfer found in a block that is too young, waiting until at least {SAFETY_BLOCK_COUNT} more blocks are confirmed.")
continue

if token.IS_NATIVE_ASSET:
# ETH
payment_amount = w3.eth.getTransaction(signed_message.transaction_hash).value
receipt_reciever = receipt.to.lower()
correct_recipient = receipt_reciever == signed_message.recipient_address.lower()

else:
# DAI
contract = w3.eth.contract(address=token.ADDRESS, abi=TOKEN_ABI)
transaction_details = contract.events.Transfer().processReceipt(receipt)[0].args
payment_amount = transaction_details.value
# check that the payment happened on the right contract address
correct_contract = token.ADDRESS.lower() == receipt.to.lower()
# take recipient address from the topics, not from the "from" field, as that's the contract address
receipt_reciever = receipt.logs[0].topics[2][12:].hex().lower()
correct_recipient = correct_contract and (receipt_reciever == signed_message.recipient_address.lower())


receipt_sender = getattr(receipt, 'from').lower()
correct_sender = receipt_sender == signed_message.sender_address.lower()

if not (correct_sender and correct_recipient):
logger.warning(
f" * Given payment was {balance} {token.TOKEN_SYMBOL}"
f" * Transaction hash provided does not match correct sender and recipient"
)
logger.warning(f" * Skipping") # noqa: F541
if log_verbosity > 0:
logger.info(f"receipt sender={receipt_sender}, expected sender={signed_message.sender_address.lower()}")
logger.info(f"receipt recipient={receipt_reciever}, expected recipient={signed_message.recipient_address.lower()}")
continue
if no_dry_run:
logger.info(f" * Confirming order payment {full_id}")
with scope(organizer=None):
order_payment.confirm()

if payment_amount > 0:
logger.info(f"Payments found for {full_id} at {signed_message.sender_address}:")
if payment_amount < expected_amount:
logger.warning(
f" * Expected payment of at least {expected_amount} {token.TOKEN_SYMBOL}"
)
logger.warning(
f" * Given payment was {payment_amount} {token.TOKEN_SYMBOL}"
)
logger.warning(f" * Skipping") # noqa: F541
continue
if no_dry_run:
logger.info(f" * Confirming order payment {full_id}")
with scope(organizer=None):
order_payment.confirm()
else:
logger.info(f" * DRY RUN: Would confirm order payment {full_id}")
else:
logger.info(f" * DRY RUN: Would confirm order payment {full_id}")
else:
logger.info(f"No payments found for {full_id}")
logger.info(f"No payments found for {full_id}")
88 changes: 0 additions & 88 deletions pretix_eth/management/commands/confirm_refunds.py

This file was deleted.

30 changes: 30 additions & 0 deletions pretix_eth/migrations/0002_auto_20220529_2332.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Generated by Django 3.2.12 on 2022-05-29 23:32

from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('pretixbase', '0208_auto_20220214_1632'),
('pretix_eth', '0001_initial'),
]

operations = [
migrations.CreateModel(
name='SignedMessage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False)),
('signature', models.CharField(max_length=132)),
('raw_message', models.CharField(max_length=256)),
('sender_address', models.CharField(max_length=42)),
('recipient_address', models.CharField(max_length=42)),
('chain_id', models.SmallIntegerField()),
('order_payment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='signed_messages', to='pretixbase.orderpayment')),
],
),
migrations.DeleteModel(
name='WalletAddress',
),
]
18 changes: 18 additions & 0 deletions pretix_eth/migrations/0003_signedmessage_transaction_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2022-07-03 14:50

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('pretix_eth', '0002_auto_20220529_2332'),
]

operations = [
migrations.AddField(
model_name='signedmessage',
name='transaction_hash',
field=models.CharField(max_length=66, null=True),
),
]
18 changes: 18 additions & 0 deletions pretix_eth/migrations/0004_alter_signedmessage_raw_message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.2.12 on 2022-07-08 20:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('pretix_eth', '0003_signedmessage_transaction_hash'),
]

operations = [
migrations.AlterField(
model_name='signedmessage',
name='raw_message',
field=models.TextField(),
),
]
Loading