efdevcon / pretix-eth-payment-plugin Goto Github PK
View Code? Open in Web Editor NEWAn etheruem payment provider plugin for pretix
License: Other
An etheruem payment provider plugin for pretix
License: Other
currently there is only a section "Development setup" - at some point we should add a section production setup
currently our web3connect option offers walletconnect and metamask as options.
The new web3connect version offers more options - so we should update web3connect
When the user is presented with options to pay, the pretix-eth-payment-plugin displays "Ethereum Plugin".
I do not see where this can be changed in the Admin Settings for the plugin so it must be what is defined here in payment.py:
I am thinking that it should read: "ETH or DAI" or perhaps "Pay directly with ETH or DAI".
Let me know if there is any impact of changing this to something more comprehensible to a purchasing user.
Alice loads the order page when ETH is worth $100 each. The backend queries for the proper exchange rate and stores it in her session. This determines how much ETH she must pay for a ticket.
Alice now waits 12 hours, during which the price of ETH drops 10%. She is now able to purchase a ticket at a 10% discount since the backend will use the stored exchange rate from time she loaded the order page.
Enforce an expiration on exchange rates for volatile currencies to ensure that there are bounds on how long someone can short the currency to purchase their devcon ticket. Something like 30 minutes should be sufficient to account for network congestion or even sending funds from an exchange.
In the code, you are checking the value with this endpoint https://blockscout.com/eth/mainnet/api?module=transaction&action=gettxinfo&txhash=0xc74c19b163b627bb3f82771efd87aa072bc996557418d08573b4f8494bf9fe28
At this line in the code https://github.com/esPass/pretix-eth-payment-plugin/blob/master/pretix_eth/providers.py#L88
In this case, that txhash, is from a tx performed by a Gnosis Safe Wallet https://safe.gnosis.io/
You can see in etherscan an internal transaction https://etherscan.io/address/0x6a1517622feb74a242e68a26f423ae38e020a0b1#internaltx for Devcon tickets.
It needs to check the traces... not only the main value.
for easy payment via phone - cc @pedrouid
We should setup https://rollbar.com/ for aggregated logging.
I just installed the plugin - before pretix was working fine - but afterwards I am getting this:
Watching for file changes with StatReloader
INFO 2019-05-17 18:42:51,423 django.utils.autoreload autoreload Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
Exception in thread django-main-thread:
Traceback (most recent call last):
File "/usr/lib/python3.5/threading.py", line 914, in _bootstrap_inner
self.run()
File "/usr/lib/python3.5/threading.py", line 862, in run
self._target(*self._args, **self._kwargs)
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/utils/autoreload.py", line 54, in wrapper
fn(*args, **kwargs)
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/core/management/commands/runserver.py", line 120, in inner_run
self.check_migrations()
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/core/management/base.py", line 453, in check_migrations
executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/executor.py", line 18, in __init__
self.loader = MigrationLoader(self.connection)
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/loader.py", line 49, in __init__
self.build_graph()
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/loader.py", line 274, in build_graph
raise exc
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/loader.py", line 248, in build_graph
self.graph.validate_consistency()
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/graph.py", line 195, in validate_consistency
[n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/graph.py", line 195, in <listcomp>
[n.raise_error() for n in self.node_map.values() if isinstance(n, DummyNode)]
File "/home/ligi/git/pretix/src/env/lib/python3.5/site-packages/django/db/migrations/graph.py", line 58, in raise_error
raise NodeNotFoundError(self.error_message, self.key, origin=self.origin)
django.db.migrations.exceptions.NodeNotFoundError: Migration pretix_eth.0001_initial dependencies reference nonexistent parent node ('pretixbase', '0109_auto_20190215_1700')
Make users aware if they do not want to associate their account with a conference - they need to use a mixer (and ideally later a checkbox [ ] use mixer)
the transactions should be better verified. Currently single services are queried. One thing to mitigate could be to query more services or in an ideal world even use a system like INCUBED to verify incoming transactions
In this process we might also want to use web3.py
DAI values obtained from blockscout by the token provider are in DAI "wei" units (e.g. 2 DAI = 2 x 10^18 DAI wei). However, rate quotes are in whole DAI units.
The rate quote functionality should ensure that its amount values are scaled appropriately for comparison with the token provider amount values.
this is wrong - we cannot ask for the transaction hash before the payment method is selected or the order is placed.
This bad flow was introduced with the security fixes for #8 - the problem is when fixing the problem there where obstacles to get an input field in the.
I do not think the user should enter the TX-Hash at all
I propose the following:
the user just selects the currency type:
then we show a QR (ERC-681) and a Web3Connect link (or js call) to trigger the payment
important is that the data-field gets a mandatory payment object ID - so we can prevent #8 and the sniping attack @pipermerriam pointed out
but this means in the beginning we can only take ETH and xDAI no DAI yet.
Later on (but IMHO not in wave 1 for DevCon) we should create a smart contract that accepts ETH and DAI with a function
EDIT: we can also take DAI by using the least significant bits of the value to encode the payment object ID (less than 5000WEI for DevCon - so in comparison to gas usage negligible)
Currently payment provider appears to use floats for prices. These should be converted to use either decimal.Decimal
or a plain int
.
this is a follow up for #49 :
we should inline (host statically via the plugin) the code/site for web3connect - currently it is using https://checkout.web3connect.com which works but adds another point of failure / attack vector
This likely relates to the Ethereum Plugin, or possibly in pretix itself. This is tested with eth-payment-plugin 1.0.1.
I am able to view orders, but viewing pending orders from the Ethereum Plugin causes an error. This is from the pretix log at /var/pretix/data/logs/pretix.log:
Traceback (most recent call last):
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/control/permissions.py", line 27, in wrapper
return function(request, *args, **kw)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/generic/base.py", line 71, in view
return self.dispatch(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/generic/base.py", line 97, in dispatch
return handler(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/generic/detail.py", line 107, in get
context = self.get_context_data(object=self.object)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/control/views/orders.py", line 203, in get_context_data
p.html_info = (p.payment_provider.payment_control_render(self.request, p) or "").strip()
File "/var/pretix/venv/lib/python3.7/site-packages/pretix_eth/payment.py", line 213, in payment_control_render
'coin': request.session['payment_ethereum_fm_currency']}
File "/var/pretix/venv/lib/python3.7/site-packages/django/contrib/sessions/backends/base.py", line 54, in __getitem__
return self._session[key]
KeyError: 'payment_ethereum_fm_currency'```
payment_prepare
does not call _get_rates_checkout
. Therefore, payment_ethereum_time
and payment_ethereum_amount
are not set on the session and execute_payment
. checkout_prepare
is correct, so the bug will only occur in the "secondary" payment flow, so only if a failed payment is retried later or if a payment method is switched.
This is to confirm that orders in ETH are not finalized, and persist in "pending". This is tested with eth-payment-plugin 1.0.1.
Order ID: G0DDF
Ethereum Address: 0xf355ad58e038d779731d9df833694b2e5d7a05b9
Txn ID: https://etherscan.io/tx/0x3d19188f6f2f2a094658540abe1a252aebd19f45172af33c2e3d1fd7cf61e7a7
had a pending payment. wanted to change from DAI to ETH.. decided to do it later, clicked cancel.
Request Method: | GET
-- | --
2.2.2
KeyError
'currency_type'
/var/pretix/venv/lib/python3.7/site-packages/pretix_eth/payment.py in payment_pending_render, line 221
/var/pretix/venv/bin/python3
3.7.3
['/var/pretix', '/var/pretix', '/var/pretix/venv/bin', '/usr/local/lib/python37.zip', '/usr/local/lib/python3.7', '/usr/local/lib/python3.7/lib-dynload', '/var/pretix/venv/lib/python3.7/site-packages']
Thu, 18 Jul 2019 15:36:14 +0000
@pipermerriam @davesque can you have a look? This one would be quite urgent - but I can't look at it in the next 4h
to be able to pay with more tokens
unfortunately this is only possible for native tokens like ETH, xDAI and such - it is not possible for ERC-20
The code here that we use to get token transfer information from ERC20 contract transactions is potentially unsafe. It will fail if an ERC20 token contract does not throw an exception when a sender has insufficient funds for a transfer. In that case, an attacker could make a transfer for the correct amount without having adequate token funds and the resulting transaction will pass our verification checks. We've confirmed that the DAI contract will throw an exception when a sender doesn't have adequate funds so this issue doesn't currently affect our supported payment tokens (which are only DAI).
If a token contract does not raise for insufficient funds, we'll need to check for log events, in payment transactions, that indicate a successful transfer.
I used this checkout to buy Devcon5 tickets and when presented with an option to pay to an Ethereum address, the amount shown was in wei rather than ETH. Most wallets default to ETH, or even only allow sending in ETH denominations, and so I request that the amount shown on the checkout page also be converted from wei to ETH for ease of use.
this is a meta-issue for an alternative approach to #64
the reason for suggesting #64 was mainly that in a praxis dogfooding test it showed users ignored warnings that where written in bold - it looked like this:
The idea was now to use HD wallets to prevent this user error.
But there might actually be another way.
For people that did this mistake: offer an option to sign a message with their account to prove the tx is from them and correlate this way.
This should solve most of the problems.
The rest (e.g. using an exchange that rounds) can even be small enough that manual support is feasible.
Advantage is that is is less complexity and cheaper.
means one address per payment in via one HD wallet.
reasons:
When making a payment in ETH
this API call is executed:
The ethplorer API imposes a limit of 10 results when using a free API key:
https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API#freekey-limits
If a user's transaction is not in the ten most recent payments then their payment will end up failing.
Currently, the code which would accept DAI
based payments doesn't verify that the appropriate amount was sent.
With the lines above commented out, a user could send any amount of DAI
and successfully purchase a ticket.
it would be great to support a testnet like goerli for testing the plugin without spending fees and spamming the main network
We should add somewhat verbose logging statements into the payments module.
It appears that Ethplorer doesn't reliably give the value
parameter back when querying their API.
See this transaction:
{"timestamp":1562560887,"from":"0x9bb37d6564500b01135887f94ce86515cfaa5f80","to":"0x0000000000000000000000000000000000000000","hash":"0x50fcadf54f2e7f0473dfd2083ab55ac58fd6cbd6e2a30e7967e9686d28b1fa2d","value":1.0e-24,"input":"...","success":true}`
It shows a value
of 1.0e-24
Here's the same tx on etherscan:
https://etherscan.io/tx/0x50fcadf54f2e7f0473dfd2083ab55ac58fd6cbd6e2a30e7967e9686d28b1fa2d
It's not possible to recover an accurate amount from the data returned by ethplorer.
All automatic confirmation code was removed for security reasons in #49 to be able to run the first 2 waves. But as now in wave2 the dust settled and the current system seems to be stable enough so we can add complexity again. Here an outline on how to get automatic confirmations going.
The following plan:
This should already remove most of the current manual work. After this we can get #74 and #75 going to care for the orders with user errors.
For better and intense testing I suggest we do #5 before
as suggested by @pipermerriam
the old maintainer is unresponsive and this will become the new canonical plugin. In the end not much of the original will exist anyway - so let's break the linkage.
here is how:
currently DAI points to SAI as the change of MakerDAO is not reflected
currently the documentation only describes the "Development setup" - would be good to have documentation on how to use this plugin in a production setup
via 0ETH tx to an address that is generated from static order-info
needed as 681 itself it is only unidirectional
so a tx that ends up on chain would be the needed "back channel"
It will already be better after #64 as we do not need these weird sums or mentioning the whole "exactly" thing
But perhaps hiding the QR-Code and refreshing the link with a Web3Connec/WalletConnect logo could be nice
just skimming the code (as a non python dev) I could imagine the following attack:
it would be great if one could pay via xDAI for better UX and lower fees.
I cannot paste the error info here as it contains user-revealing info. However this error affects several users.
KeyError at ..URL..
'currency_type'
Zendesk Case: 4525
Rollbar: https://rollbar.com/ethereum/Devcon5-pretix-Production/items/4/
We are using a free API key for ethplorer:
The rate limit for free keys is 1 request every 2 seconds:
https://github.com/EverexIO/Ethplorer/wiki/Ethplorer-API#freekey-limits
The code which makes this request has no mechanism for detecting the HTTP error that requests
will emit resulting in the application throwing a 500 level error.
We need to call raise_for_status
on the response object before calling json()
on it to emit any HTTP based errors in the 4xx and 5xx range:
Then these need to be caught, and a friendly error pushed back to the user saying something like: "Server too busy, try counting to 10 and re-submitting"
No validation being done on the transaction hash
Need to validate that the submitted transaction hash is a 0x prefixed hex representation of a 32-byte value.
happened when switching from DAI to ETH payment
Environment:
Request Method: POST
Request URL: http://pretix.staging-8392.devcon.org/devcon/5/order/3N7KD/ivlnbh2kiegzl3bw/pay/change
Django Version: 2.2.2
Python Version: 3.7.3
Installed Applications:
['django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pretix.base',
'pretix.control',
'pretix.presale',
'pretix.multidomain',
'pretix.api',
'pretix.helpers',
'rest_framework',
'django_filters',
'compressor',
'bootstrap3',
'djangoformsetjs',
'pretix.plugins.banktransfer',
'pretix.plugins.stripe',
'pretix.plugins.paypal',
'pretix.plugins.ticketoutputpdf',
'pretix.plugins.sendmail',
'pretix.plugins.statistics',
'pretix.plugins.reports',
'pretix.plugins.checkinlists',
'pretix.plugins.pretixdroid',
'pretix.plugins.badges',
'pretix.plugins.manualpayment',
'django_markup',
'django_otp',
'django_otp.plugins.otp_totp',
'django_otp.plugins.otp_static',
'statici18n',
'django_countries',
'hijack',
'compat',
'oauth2_provider',
'pretix_eth',
'pretix_espass',
'pretix_bitpay']
Installed Middleware:
['pretix.api.middleware.IdempotencyMiddleware',
'django.middleware.common.CommonMiddleware',
'pretix.multidomain.middlewares.MultiDomainMiddleware',
'pretix.multidomain.middlewares.SessionMiddleware',
'pretix.multidomain.middlewares.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'pretix.control.middleware.PermissionMiddleware',
'pretix.control.middleware.AuditLogMiddleware',
'pretix.base.middleware.LocaleMiddleware',
'pretix.base.middleware.SecurityMiddleware',
'pretix.presale.middleware.EventMiddleware',
'pretix.api.middleware.ApiScopeMiddleware']
Traceback:
File "/var/pretix/venv/lib/python3.7/site-packages/redis/connection.py" in send_packed_command
600. self._sock.sendall(item)
During handling of the above exception ([Errno 104] Connection reset by peer), another exception occurred:
File "/var/pretix/venv/lib/python3.7/site-packages/kombu/connection.py" in _reraise_as_library_errors
431. yield
File "/var/pretix/venv/lib/python3.7/site-packages/celery/app/base.py" in send_task
755. self.backend.on_task_call(P, task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in on_task_call
294. self.result_consumer.consume_from(task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in consume_from
136. self._consume_from(task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in _consume_from
142. self._pubsub.subscribe(key)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in subscribe
3096. ret_val = self.execute_command('SUBSCRIBE', *iterkeys(new_channels))
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in execute_command
3009. self._execute(connection, connection.send_command, *args)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in _execute
3013. return command(*args)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/connection.py" in send_command
620. self.send_packed_command(self.pack_command(*args))
File "/var/pretix/venv/lib/python3.7/site-packages/redis/connection.py" in send_packed_command
613. (errno, errmsg))
During handling of the above exception (Error 104 while writing to socket. Connection reset by peer.), another exception occurred:
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
34. response = get_response(request)
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
115. response = self.process_exception_by_middleware(e, request)
File "/var/pretix/venv/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
113. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/generic/base.py" in view
71. return self.dispatch(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/utils/decorators.py" in _wrapper
45. return bound_method(*args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/decorators/clickjacking.py" in wrapped_view
50. resp = view_func(*args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/presale/views/order.py" in dispatch
459. return super().dispatch(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/presale/views/robots.py" in dispatch
7. resp = super().dispatch(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/django/views/generic/base.py" in dispatch
97. return handler(request, *args, **kwargs)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/presale/views/order.py" in post
542. for p in self.provider_forms:
File "/var/pretix/venv/lib/python3.7/site-packages/django/utils/functional.py" in __get__
80. res = instance.__dict__[self.name] = self.func(instance)
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/presale/views/order.py" in provider_forms
526. form = provider.payment_form_render(self.request, abs(pending_sum + fee - current_fee))
File "/var/pretix/venv/lib/python3.7/site-packages/pretix/plugins/stripe/payment.py" in payment_form_render
576. stripe_verify_domain.apply_async(args=(self.event.pk, request.host))
File "/var/pretix/venv/lib/python3.7/site-packages/celery/app/task.py" in apply_async
570. **options
File "/var/pretix/venv/lib/python3.7/site-packages/celery/app/base.py" in send_task
756. amqp.send_task_message(P, name, message, **options)
File "/usr/local/lib/python3.7/contextlib.py" in __exit__
130. self.gen.throw(type, value, traceback)
File "/var/pretix/venv/lib/python3.7/site-packages/kombu/connection.py" in _reraise_as_library_errors
436. sys.exc_info()[2])
File "/var/pretix/venv/lib/python3.7/site-packages/vine/five.py" in reraise
194. raise value.with_traceback(tb)
File "/var/pretix/venv/lib/python3.7/site-packages/kombu/connection.py" in _reraise_as_library_errors
431. yield
File "/var/pretix/venv/lib/python3.7/site-packages/celery/app/base.py" in send_task
755. self.backend.on_task_call(P, task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in on_task_call
294. self.result_consumer.consume_from(task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in consume_from
136. self._consume_from(task_id)
File "/var/pretix/venv/lib/python3.7/site-packages/celery/backends/redis.py" in _consume_from
142. self._pubsub.subscribe(key)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in subscribe
3096. ret_val = self.execute_command('SUBSCRIBE', *iterkeys(new_channels))
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in execute_command
3009. self._execute(connection, connection.send_command, *args)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/client.py" in _execute
3013. return command(*args)
File "/var/pretix/venv/lib/python3.7/site-packages/redis/connection.py" in send_command
620. self.send_packed_command(self.pack_command(*args))
File "/var/pretix/venv/lib/python3.7/site-packages/redis/connection.py" in send_packed_command
613. (errno, errmsg))
Exception Type: OperationalError at /devcon/5/order/3N7KD/ivlnbh2kiegzl3bw/pay/change
Exception Value: Error 104 while writing to socket. Connection reset by peer.
this
rate = requests.get('https://api.bitfinex.com/v1/pubticker/' + request.session['fm_currency'] + 'usd')
could be replaced with a query of the MakerDAO price oracle - perhaps even via INCUBED.
Otherwise it is easy for bitfinex to get cheap tickets as far as I see ,-)
Currently when the pretix wallet is on the wrong chain - the UX is quite bad
Ideally when the wallet is on the wrong chain -> the user should be asked to switch the chain
this way it feels more natural
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.