summaryrefslogtreecommitdiff
path: root/design-documents/041-wallet-balance-amount-definitions.rst
blob: 48deeab56089a2485f6707ba4ac2b15633589b99 (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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
DD 41: Wallet Balance and Amount Definitions
############################################

Summary
=======

This design document discusses terminology and concepts used in the wallet
for balances and amounts.

Motivation
==========

There are many different types of balances and amounts, and they need
to have a clear definition.

Furthermore, the user in some situations needs to know/decide whether
an amount that the user chooses includes fees or not.


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

Amounts
-------

* "effective amount": An effective amount always represents the direct effect on the
  wallet's balance of the same currency.
* "raw amount": The raw amount always refers to the amount with fees applied.
  The exact interpretation of that depends on the transaction type.
* "instructed amount": An instructed amount always refers to the amount that
  a user has explicitly specified as an input.  It is not directly a property
  of transactions, but might be added as metadata to transactions for
  informational purposes.  How the instructed amount is interpreted
  differs based on the "instructed amount mode" that is specified
  together with the amount.  By default, the instructed amount
  is assumed to translate to the raw amount of the corresponding transaction.
* "counter-party effective amount": An amount that **estimates** the effect
  of the transaction of the balance (either wallet or bank account) of the other
  party. This is usually a conservative estimate, i.e. when sending money,
  this is the lower bound for the funds that the other party will obtain
  *after* fees.


Transaction types
----------

WITHDRAW
  raw amount is the total to be wired in exchange bank account

  ``coins`` = select-coin(withdraw, mode, instructed_amount)
  
  if instructed_amount mode = raw
    ``raw_amount`` = instructed_amount
    
    ``effective_amount`` = instructed_amount - coins.withdrawal_fee

  if instructed_amount mode = effective
    ``raw_amount`` = instructed_amount + coins.withdrawal_fee
    
    ``effective_amount`` = instructed_amount

DEPOSIT
  raw amount is the total wire transfer in the bank account

  ``coins`` = select-coin(deposit, mode, instructed_amount)

  if instructed_amount mode = raw
    ``raw_amount`` = instructed_amount

    ``effective_amount`` = instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee 

  if instructed_amount mode = effective
    ``raw_amount`` = instructed_amount - coins.deposit_fee - coins.refresh_fee - wire.transfer_fee

    ``effective_amount`` = instructed_amount

PULL CREDIT
  raw amount is the purse_value in the exchange that counter-party need will pay

  ``coins`` = select-coin(pull, mode, instructed_amount)

  if instructed_amount mode = raw
    ``raw_amount`` = instructed_amount

    ``effective_amount`` = instructed_amount - coins.withdrawal_fee - purse_fee

  if instructed_amount mode = effective
    ``raw_amount`` = instructed_amount + coins.withdrawal_fee + purse_fee

    ``effective_amount`` = instructed_amount
  
  if instructed_amount mode = counter-party
    ``raw_amount`` = instructed_amount - coins.counter-party_deposit_fee  

    ``effective_amount`` = instructed_amount - coins.counter-party_deposit_fee - coins.withdrawal_fee - purse_fee

  ``counter-party_raw_amount`` = raw_amount 
  
  ``counter-party_effective_amount`` = raw_amount + coins.counter-party_deposit_fee 

  .. note::

    counter-party_effective_amount is an estimation since refresh fee is not included. 
    Refresh fee can't be calculated because depends on the coins available in the wallet 
    of the counter-party

  .. note::
    coins.counter-party_deposit_fee is the minimum deposit_fee that can be calculated for the
    given exchange. Counter-party may pay more if it have different preferences doing the coin 
    selection.


PUSH DEBIT
  raw amount is the purse_value in the exchange to be withdrawn that counter-party will withdraw

  ``coins`` = select-coin(push, mode, instructed_amount)

  if instructed_amount mode = raw
    ``raw_amount`` = instructed_amount

    ``effective_amount`` = instructed_amount + coins.deposit_fee + purse_fee

  if instructed_amount mode = effective
    ``raw_amount`` = instructed_amount - coins.deposit_fee - purse_fee

    ``effective_amount`` = instructed_amount
 
  if instructed_amount mode = counter-party
    ``raw_amount`` = instructed_amount + coins.counter-party_withdraw_fee  

    ``effective_amount`` = instructed_amount - coins.counter-party_withdraw_fee - coins.withdrawal_fee - purse_fee
 
  ``counter-party_raw_amount`` = raw_amount 

  ``counter-party_effective_amount`` = raw_amount - coins.counter-party_withdraw_fee

  .. note::
    coins.counter-party_withdraw_fee is the minimum withdraw_fee that can be calculated for the
    given exchange. Counter-party may pay more if it have different preferences doing the coin 
    selection.


------------

.. note::
  Next transaction types are not initiated in the wallet so instructed amount is not defined by the wallet user.

  It may be helpful to calculate effective_amount to check if the wallet is able to perform the operation 

``contract_wire_fee`` = min(wire.transfer_fee / contractTerms.amortization_factor, contractTerms.max_wire_fee)

``max_merchant_fee`` = min(contractTerms.max_fee, contract_wire_fee)

PAYMENT
  raw amount is the net value of the order without fees 

  ``instructed_amount`` = contractTerms.amount

  ``coins`` = select-coin(deposit, mode, raw_amount)

  ``raw_amount`` = instructed_amount - max_merchant_fee

  ``effective_amount`` = instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee - max_merchant_fee

  .. note::
    The current coin-selection algorithm the order_price is neither raw_amount nor effective_amount.
    We can calculate the raw_amount of the payment as (contractTerms.amount - max_merchant_fee) and then this
    operation becomes equivalent than a deposit (in terms of fee calculation).

PUSH CREDIT
  raw amount is the purse_value in the exchange to be withdrawn

  ``instructed_amount`` = p2pContract.amount

  ``coins`` = select-coin(withdraw, mode, raw_amount)

  ``raw_amount`` = instructed_amount

  ``effective_amount`` = instructed_amount - coins.withdrawal_fee

  .. note::
    In the case that the withdrawal_fee of the coin selection for the push-credit amount
    is higher than the wire_fee of the exchange, can the wallet ask the exchange to make
    a wire transfer of the purse instead of proceeding? 

PULL DEBIT
  raw amount is the net value of the invoice without fees 

  ``instructed_amount`` = p2pContract.amount

  ``coins`` = select-coin(deposit, mode, raw_amount)

  ``raw_amount`` = instructed_amount

  ``effective_amount`` = instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee

REFUND
  raw amount is the amount that the merchant refunded

  ``instructed_amount`` = refund.amount

  ``raw_amount`` = instructed_amount 

  ``effective_amount`` = instructed_amount - refund_fee - refresh_fee

  .. note::
    There may be the case that the merchant should refund all the value of the purchase
    and that may include paying for the refund_fee.

    Is there a way that the merchant can initiate a refund of purchase + refund_fee so 
    the wallet will get the same effective_amount?

TIP
  raw amount is the amount that the merchant send as tip

  ``instructed_amount`` = tip.amount

  ``raw_amount`` = instructed_amount + withdrawal_fee

  ``effective_amount`` = instructed_amount 

  .. note:: 
    We should not show fee for tips in the wallet since the merchant is the one choosing
    the exchange and we can assume that those tips are paid by the merchant. 
    So the wallet only care about the effective.

Coin selection algorithm
----------

Is an optimization algorithm that will choose coins given a denomination list until amount is reached
and the coins selected minimize the fee spent.

``select-coin`` will receive 3 parameters:
  * operation type: tell for which fee is doing the optimization (can be withdraw, deposit, push, pull)
  * amount: how much value value the coins need to sum up
  * mode: the interpretation of the amount parameter and affect the denom selection

    - effective: the amount is the expected balance change
    - raw: the amount include fee of the operation paid by this wallet
    - counter-party: only valid for push and pull, the amount include fee of the operation and fee of the counter-party operation

Given the operation type, the fees taking into account when doing the calculation

  withdraw: 
    - withdrawal_fee of denominations until amount

  deposit: 
    - deposit_fee for every coin until total
    - refresh_fee of (total-amount)
    - wire_transfer for every exchange

  pull:
    - withdraw_fee of the amount
    - purse_fee of the exchange
    - if the mode is counter-party it should also take into account the deposit_fee of selected exchange 

  push:
    - deposit_fee for every coin until total
    - refresh_fee of (total - amount)
    - purse_fee of the exchange
    - if the mode is counter-party it should also take into account the withdrawal_fee of selected exchange 

.. note::

  select-coin must be predictable (select the same coins for the same parameters) and if
  the difference between two amounts are the fee for a given operation:

    operation: withdrawal

    withdrawal_fee = amount1 - amount2

  then the wallet should select the same coins using the correct mode 
  
    select-coin(withdraw, raw, amount1) == select-coin(withdraw, effective, amount2)


Instructed Amount Modes
-----------------------

The interpretation and possible choices of the instructed amount mode
depends on which transaction is initiated.

For withdrawal:

* ``raw-mode`` (default): instructed amount is what is subtracted from the reserve balance (i.e. it's the raw amount)
* ``effective-mode``: instructed amount is what the user wants to have as material balance in the wallet

FIXME(dold): However, that does not really cover the user case where the merchant charges fees and the user has to pay for that. So in theory we could have a mode that withdraws enough to pay for some particular claimed order, but IMHO that's overkill.

For deposits (where there is no contract that already specifies an amount):

* ``max-mode``: The instructed amount is ignored (can be zero in the request), and all coins are spent (note that the calculation should be made available when the user is asked to specify an amount when using a template)
* ``raw-mode`` (default): The instructed amount is what will be paid to the "merchant" (or the customer's bank account), ignoring wire fees
* ``effective-mode``: The instructed amount is how the wallet's balance will be affected. Difficult to compute accurately because refresh is involved. Note that the calculation should ideally again be made available when the user is asked to specify an amount when using a template.

  
For peer-push-debit:

* ``raw-mode`` (default): The instructed amount is what will be paid, deposit fees are covered by the sender, withdrawal fees from the reserve by the receiver
* ``effective-mode``: Instructed amount is the effect on the material balance. Difficult to compute accurately because refresh is involved.
* ``counter-party-effective-mode``: Instructed amount is the effect on the counterparty's wallet balance. Difficult to compute accurately because coin selection by receiver may not match our expectations.

FIXME(dold): Should we also have a mode where withdrawal fees are covered by the side that does peer-push-debit? However, that assumes the other party has the same withdrawal coin selection algorithm. FIXME(grothoff): isn't this the counterparty-effective-mode you described above, and that would seem to exactly have this issue?

For peer-pull-credit:

* ``raw-mode`` (default): Amount that the other party has to put in the reserve. The payer has to pay any deposit fees on top. The receiver balance is increased by the specified amount minus any withdraw fees.
* ``effective-mode``: Amount by which the initiator's balance is increased. Difficult to compute as the receiver has to simulate different coin selections and their effect on withdraw fees to arrive at the minimum total amount that must be deposited into the reserve.



Illustrative Example
--------------------

To explain the differences between raw, effective and instructed amounts, consider the following scenario: Alice wants to send money
to Bob via a P2P push payment.

Example 1:

* Alice starts a withdrawal of ``KUDOS:10`` from her bank's web interface into her Taler
  wallet. The instructed amount is ``KUDOS:10`` and (by default for bank-integrated withdrawals),
  the mode is ``raw-mode``.  After fees, ``KUDOS:9.8`` arrive in her Taler wallet.

Example 3:

* Alice wants to pay for a ``KUDOS:10`` monthly magazine subscription. Her Taler wallet is empty though.
* She starts withdrawal through her Android wallet app, where she selects ``KUDOS:10`` as the instructed
  amount with ``mode=effective-mode``. This translates to ``amountEffective=KUDOS:10`` and ``amountRaw=KUDOS:10.10``.
* Alice is redirected to her banking app where she transfers ``KUDOS:10.10`` to the exchange.
* Her Taler wallet balance will be ``KUDOS:10.10`` after the withdrawal completes.

Note that on the amount she chooses and the fees / denom structure of the exchange, the ``amountEffective`` might be *higher*
than the instructed amount.

FIXME(dold): That flow does not work if withdrawal starts in the bank. Maybe there needs to be a mechanism
where the wallet tells the bank the adjusted amount that needs to be transferred? That would be a new
feature in the bank integration API.

Example 4:

* Alice has ``KUDOS:10`` in her wallet.
* Alice wants to initiate a peer-push payment with ``amountInstructed=KUDOS:8``
  and ``mode=effective-mode``. That means that after the payment, she expects
  exactly ``KUDOS:2`` to remain in her wallet.
* Due to the fee configuration, her wallet computes ``amountRaw=KUDOS:7.5`` and ``amountEffective=KUDOS:7.8``.
  The effective amount in this case does **not** equal the instructed amount, despite the ``mode=effective-mode``.
  That's because there no amount that can be spend so that the spend amount with resulting refresh
  fees equal ``KUDOS:8``.
* Alice confirms the peer-push payment initiation, and exactly ``KUDOS:7.5`` are credited
  to the purse that her wallet creates.
* Bob merges the purse into his reserve. Bob's wallet automatically withdraws
  from the reserve, and his wallet balance increases by ``KUDOS:7.1``, since
  withdrawal fees are deducted.

Balances
--------

The following types of balances are defined:

- ``available``: Balance that the wallet believes will certainly be available
  for spending, modulo any failures of the exchange or double spending issues.
  This includes available coins *not* allocated to any
  spending/refresh/... operation. Pending withdrawals are *not* counted
  towards this balance, because they are not certain to succeed.
  Pending refreshes *are* counted towards this balance.
  This balance type is nice to show to the user, because it does not
  temporarily decrease after payment when we are waiting for refreshes

- ``material``: Balance that the wallet believes it could spend *right now*,
  without waiting for any operations to complete.
  This balance type is important when showing "insufficient balance" error messages.

- ``age-acceptable``: Subset of the material balance that can be spent
  with age restrictions applied.

- ``merchant-acceptable``: Subset of the material balance that can be spent with a particular
  merchant (restricted via min age, exchange, auditor, wire_method).

- ``merchant-depositable``: Subset of the merchant-acceptable balance that the merchant
  can accept via their supported wire methods.

Balance Mismatch
----------------

The wallet uses the following terminology when an operation can't succeed
because the balance is too low, even though the instructed amount 

- "fee-gap-estimate": Additional (material) balance that the wallet estimates it
  still needs for the operation to succeed.

  - This value is an estimated, because newly withdrawn coins might have different fees.
  - This value is specified *per exchange*, because each exchange has different fees.

FIXME(dold): Should we specify an upper-bound fee-gap-estimate to simplify it for the UIs?


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

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