summaryrefslogtreecommitdiff
path: root/design-documents/047-stefan.rst
blob: 4cef18650cdd0808c9062c24b70f8dfc47012744 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
DD 47: STEFAN
#############

Summary
=======

The Standardized Template for Effective-Fee Approximation Numbers (STEFAN)
is a feature to ensure customers see consistent fees for equivalent
purchases (largely) independent of the specific coins selected. It will also
make it easier for merchants to configure their systems to pay all reasonable
fees.


Motivation
==========

Taler has a logarithmic fee structure for good reasons (to compete in
different market segments with reasonable profit margins). However, the
logarithmic fee structure inherently implies that the specific coin selection
made by the wallet can result in very different fees being applicable for the
same amount at the same merchant merely due to different coins being available
in the wallet. To minimize support costs, it is important that customers do
not need to be aware of the concept of coins and are instead shown consistent
fees for equivalent transactions.


Requirements
============

  - keep the logarithmic nature of the fees (proportionally high fees
    for tiny amounts, medium fees for medium amounts, low fees for large amounts)
  - same purchase, same perceived price; prices are predictable for users
  - enable merchants to easily cover all fees in most cases


Proposed Solution
=================

The proposal is for the exchange to advertise three STEFAN-parameters that
encode a fee curve of the form ``stefan_abs + stefan_log * log P +
stefan_lin * P`` where P represents the gross price to be paid.
Here, the numerical value for P is to be computed by
dividing the actual gross price by the smallest denomination
offered by the exchange.

.. note::

   This calculation is already done using floating point (!) as we want the
   STEFAN-curve to be smooth and not a step-function. This is also needed so
   that we can invert the computation and calculate gross amounts from net
   amounts and actually get a nice invertible computation where both
   directions always match.  Note that the computation itself is nevertheless
   non-trivial involving Newton's method to solve ``f(x)=0`` using a
   well-estimated starting point for the iteration to avoid divergence issues.
   Finally, while we do the STEFAN-curve computations using doubles, we should
   then convert the final amounts back into "human-friendly" numbers rounding
   towards the nearest value that can be represented using the canonical
   number of decimal places at the exchange.  libtalerexchange (C) has a
   reference implementation of the gross-net conversion functions (in both
   directions) and of the final rounding logic.  This exception to the general
   rule of no floating-points for amounts is acceptable as it is not actually
   done at the protocol level but only in internal computations of the wallet
   and merchant backend as part of the STEFAN cost estimation logic, which by
   definition is an estimate and does not need to be exact.  (And even if
   wallet and merchant backend were to (slightly) disagree about the
   computations due to different floating point implementations, everything
   would still be fine, and even a significant disagreement would not cause
   anything but resurface the UI issue the STEFAN-curves addresses.)

The fee curve will be the displayed fee, except in cases where the coin
selection is exceptionally bad (which should happen in substantially less than
1% of all cases).  The fee curve will also be used as the maximum fee a
merchant will cover unless the merchant overrides the mechanism.

In the most common case, where the STEFAN-curve fee is at or below what the
merchant covers, no fees are displayed, except in the exceptionally rare
case where the actual fees (due to unfortunate coin selection) are above
both the exchange's STEFAN-curve and the what the merchant covers. In this
last case the fees shown will be the actual fees minus what the merchant
covers and here the fees may vary even for equivalent transactions.

In the uncommon case where a merchant does not cover any fees or only covers
parts of the fee according to the STEFAN-curve, the displayed fee will be the
value on the STEFAN-curve minus the amount covered by the merchant. If the
actual fees paid end up being below the approximation by the STEFAN-curve, the
delta is (by default) *hidden* from the balance of the user to simulate a
consistent fee.

However, only the total available balance is marked down based on the
STEFAN-curve value.  Thus, the wallet will contains coins with a potentially
higher balance value than what is shown to the user. This difference is
reconciled annually by adding a special transaction that increases the wallet
balance to eliminate the difference without any actual network interaction.
The entry in the transaction history that boosts the balance links to an
explanation.  We may consider suggesting the user to donate the windfall.

In developer mode, the user should probably be given the choice to see the
delta, to disable the feature, and/or to force the windfall transaction to
happen immediately.


Computing the curve
-------------------

Typically, the ``stefan_abs`` value should represent a single wire transfer
fee. The ``stefan_log`` value should be computed to approximate the deposit
(and if applicable) refresh and withdraw fees for a coin, to be multiplied by
the number of coins.  In a canonical setup, ``stefan_lin`` would be zero.
However, if an exchange is configured to use a linear fee structure, then
``stefan_lin`` would become applicable.

The taler-wallet-cli should have an option to compute the STEFAN-values
given a denomination fee structure. This computation could probably be done
either analytically (if the fee structure is systematic) or by simulation.

Modifications to the merchant
-----------------------------

Instead of having (just) a "default fee", merchants should have an option to
use the STEFAN-curve when computing the fees they would be willing to cover.

Modifications to the exchange
-----------------------------

The STEFAN-curve can be configured using three simple configuration values
in the ``[exchange]`` section. The resulting values should be shared as
part of the ``/keys`` response, without digital signature.

Modifications to the wallets
----------------------------

The STEFAN-curves will be useful as an easy approximate way to compare
exchange fee structures. However, wallets may not want to just trust an
exchange to honestly report STEFAN-curve values but could possibly use
a simulation to check that the given STEFAN-curve matches the actual fees.

Wallets will need to keep the hidden STEFAN-balance and add the annual
internal reconcilliation transaction.

Wallets will need to compute both the STEFAN-fee for display and still
do their own internal actual coin selection to minimize fees.



Definition of Done
==================

  - exchange modified (DONE)
  - merchant understands STEFAN curve in backend (DONE)
  - merchant SPA has configuration option to enable use of STEFAN-curves
  - wallet-core uses STEFAN-curves to compute display fees
  - wallet-core supports annual reconcilliation transaction
  - wallet GUIs use STEFAN-curves when comparing exchange fee structures


Alternatives
============

Refresh fees could additionally be waived if the refresh operation yields coins
of a lower denomination than the original coin.  We should check if this allows
us to define tighter STEFAN-curves.


Drawbacks
=========



Discussion / Q&A
================

(This should be filled in with results from discussions on mailing lists / personal communication.)