taler-docs

Documentation for GNU Taler components, APIs and protocols
Log | Files | Refs | README | LICENSE

041-wallet-balance-amount-definitions.rst (13529B)


      1 DD 41: Wallet Balance and Amount Definitions
      2 ############################################
      3 
      4 :Design status: Draft
      5 :Implementation status: Partial
      6 :DD shepherd: TBD
      7 :Historical contributors: Sebastian, Christian Grothoff, Florian Dold, Marc Stibane
      8 :First published: 2023-03-30
      9 :Last substantive change: 2026-09-05
     10 :Normative references: ``wallet/wallet-core.md``
     11 
     12 Summary
     13 =======
     14 
     15 This design document discusses terminology and concepts used in the wallet
     16 for balances and amounts.
     17 
     18 Motivation
     19 ==========
     20 
     21 There are many different types of balances and amounts, and they need
     22 to have a clear definition.
     23 
     24 Furthermore, the user in some situations needs to know/decide whether
     25 an amount that the user chooses includes fees or not.
     26 
     27 
     28 Proposed Solution
     29 =================
     30 
     31 Amounts
     32 -------
     33 
     34 * ``effective``: An effective amount always represents the direct effect on the
     35   wallet's balance of the same currency.
     36 * ``raw``: The raw amount always refers to the amount with fees applied.
     37   The exact interpretation of that depends on the transaction type.
     38 * ``instructed``: An instructed amount always refers to the amount that
     39   a user has explicitly specified as an input.  It is not directly a property
     40   of transactions, but might be added as metadata to transactions for
     41   informational purposes.  How the instructed amount is interpreted
     42   differs based on the "instructed amount mode" that is specified
     43   together with the amount.
     44 * ``counterparty-effective``: An amount that **estimates** the effect
     45   of the transaction of the balance (either wallet or bank account) of the other
     46   party. This is usually a conservative estimate, i.e. when sending money,
     47   this is the lower bound for the funds that the other party will obtain
     48   *after* fees.
     49 
     50 Confirmed and final transaction amounts
     51 ---------------------------------------
     52 
     53 The wallet API's ``amountEffective`` preserves the amount shown when the user
     54 confirmed a transaction. ``amountRaw`` retains its existing meaning.
     55 ``amountEffectiveFinal`` reports the actual wallet balance effect after the
     56 transaction and its change/recovery operations have settled. It is a
     57 nonnegative amount; the transaction type determines debit or credit.
     58 
     59 The final amount is absent while settlement is pending or suspended, and when
     60 historical evidence cannot establish an exact amount. A failed recovery counts
     61 only completed outputs. Aborting before funds are consumed has a zero final
     62 cost; fees and unrecovered funds otherwise remain a debit. For incoming
     63 transactions, only coins actually issued count as the final credit.
     64 
     65 Ordinary merchant refunds remain separate credits and do not reduce a
     66 successful payment's final cost. Abort refunds are included in the original
     67 unsuccessful payment's final cost; their informational refund records carry
     68 ``isAbortRecovery`` and must not be counted a second time. Associated refreshes
     69 have zero independent effect; standalone refreshes report their own loss.
     70 Settled amounts survive deletion of supporting records. Explicitly reopening
     71 an operation invalidates its final amount until it settles again.
     72 
     73 
     74 Instructed Amount Modes
     75 -----------------------
     76 
     77 * ``raw-mode``: The instructed amount represents the raw amount. This is the default.
     78 * ``effective-mode``: The instructed amount represents the effective amount, i.e.
     79   the direct change to the balance.
     80 * ``counterparty-effective-mode``: The instructed amount represents the effective
     81   amount seen from the counterparty. In other words, it is the direct
     82   difference to the counterparty's balance.
     83 
     84 Balances
     85 --------
     86 
     87 The following types of balances are defined:
     88 
     89 - ``available``: Balance that the wallet believes will certainly be available
     90   for spending, modulo any failures of the exchange or double spending issues.
     91   This includes available coins *not* allocated to any
     92   spending/refresh/... operation. Pending withdrawals are *not* counted
     93   towards this balance, because they are not certain to succeed.
     94   Pending refreshes *are* counted towards this balance.
     95   This balance type is nice to show to the user, because it does not
     96   temporarily decrease after payment when we are waiting for refreshes
     97 
     98 - ``material``: Balance that the wallet believes it could spend *right now*,
     99   without waiting for any operations to complete.
    100   This balance type is important when showing "insufficient balance" error messages.
    101 
    102 - ``age-acceptable``: Subset of the material balance that can be spent
    103   with age restrictions applied.
    104 
    105 - ``counterparty-acceptable``: Subset of the material balance that can be spent with a particular
    106   merchant or peer-to-peer transfer.
    107 
    108 - ``counterparty-depositable``: Subset of the receiver-acceptable balance that the counterpary
    109   can accept via their supported wire methods.
    110 
    111 - ``exchange-depositable``: Subset of the receiver-acceptable balance that the counterpary
    112   can accept via their supported wire methods.
    113 
    114 While not quite a balance, we also define the following balance-related term:
    115 
    116 - ``max-merchant-effective-deposit-amount``: Estimated maximum amount that the
    117   wallet could pay for, under the assumption that the merchant pays absolutely
    118   no fees.
    119 
    120 
    121 
    122 
    123 Raw Amount Definitions
    124 ----------------------
    125 Raw amount is…
    126 
    127 Manual Withdrawal
    128   … the money that is wired from the user's bank account to the
    129   exchange's bank account for the withdrawal.
    130 
    131 Deposit
    132   … the amount leaving the exchange account without the wire fees.
    133  
    134 Peer Pull Credit
    135   … the amount that is withdrawn from the purse (via merge reserve) that
    136   the counterparty fills.
    137 
    138 Peer Push Debit
    139   … what the counterparty expects in the purse.
    140 
    141 Peer Push Credit
    142   … the purse_value in the exchange to be withdrawn.
    143 
    144 Merchant Payment
    145   … the amount the merchant should get if is not doing aggregated transaction.
    146   Wire fees are ignored.
    147 
    148 Peer Pull Debit
    149   … the net value of the invoice without fees.
    150 
    151 Refund
    152   … the amount that the merchant refunded.
    153   
    154 
    155 
    156 Details per Transaction Type
    157 ----------------------------
    158 
    159 .. warning::
    160 
    161    This section is possibly incomplete / out of date.
    162 
    163 Manual Withdrawal
    164 ~~~~~~~~~~~~~~~~~
    165 
    166 .. code:: none
    167 
    168   if instructed_amount mode = raw
    169     raw_amount = instructed_amount
    170 
    171     effective_amount = instructed_amount - coins.withdrawal_fee
    172 
    173   if instructed_amount mode = effective
    174     raw_amount = instructed_amount + coins.withdrawal_fee
    175 
    176     effective_amount = instructed_amount
    177 
    178 Deposit
    179 ~~~~~~~
    180 
    181 .. code:: none
    182 
    183   if instructed_amount mode = raw
    184     raw_amount = instructed_amount
    185 
    186     effective_amount = instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee
    187 
    188   if instructed_amount mode = effective
    189     raw_amount = instructed_amount - coins.deposit_fee - coins.refresh_fee - wire.transfer_fee
    190 
    191     effective_amount = instructed_amount
    192 
    193 Peer Pull Credit
    194 ~~~~~~~~~~~~~~~~
    195 
    196 .. code:: none
    197 
    198   if instructed_amount mode = raw
    199     raw_amount = instructed_amount
    200 
    201     effective_amount = instructed_amount - coins.withdrawal_fee - purse_fee
    202 
    203   if instructed_amount mode = effective
    204     raw_amount = instructed_amount + coins.withdrawal_fee + purse_fee
    205 
    206     effective_amount = instructed_amount
    207 
    208   if instructed_amount mode = counterparty
    209     raw_amount = instructed_amount - coins.counterparty_deposit_fee
    210 
    211     effective_amount = instructed_amount - coins.counterparty_deposit_fee - coins.withdrawal_fee - purse_fee
    212 
    213   counterparty_raw_amount = raw_amount
    214 
    215   counterparty_effective_amount = raw_amount + coins.counterparty_deposit_fee
    216 
    217   .. note::
    218 
    219     counterparty_effective_amount is an estimation since refresh fee is not included.
    220     Refresh fee can't be calculated because depends on the coins available in the wallet
    221     of the counterparty
    222 
    223   .. note::
    224     coins.counterparty_deposit_fee is the minimum deposit_fee that can be calculated for the
    225     given exchange. Counter-party may pay more if it have different preferences doing the coin
    226     selection.
    227 
    228 
    229 Peer Push Debit
    230 ~~~~~~~~~~~~~~~
    231 
    232 .. code:: none
    233 
    234   coins = select-coin-for-operation(debit, mode, instructed_amount)
    235 
    236   if instructed_amount mode = raw
    237     raw_amount = instructed_amount
    238 
    239     effective_amount = instructed_amount + coins.deposit_fee + purse_fee
    240 
    241   if instructed_amount mode = effective
    242     raw_amount = instructed_amount - coins.deposit_fee - purse_fee
    243 
    244     effective_amount = instructed_amount
    245 
    246   if instructed_amount mode = counterparty
    247     raw_amount = instructed_amount + coins.counterparty_withdraw_fee
    248 
    249     effective_amount = instructed_amount - coins.counterparty_withdraw_fee - coins.withdrawal_fee - purse_fee
    250 
    251   counterparty_raw_amount = raw_amount
    252 
    253   counterparty_effective_amount = raw_amount - coins.counterparty_withdraw_fee
    254 
    255   .. note::
    256     ``coins.counterparty_withdraw_fee`` is the minimum withdraw_fee that can be calculated for the
    257     given exchange. Counter-party may pay more if it have different preferences doing the coin
    258     selection.
    259 
    260 
    261 .. note ::
    262   how much wire_fee the merchant is willing to pay
    263 
    264   merchant_wire_fee = min(wire.transfer_fee / contractTerms.amortization_factor, contractTerms.max_wire_fee)
    265 
    266   merchant_deposit_fee = min(contractTerms.max_fee, contract_wire_fee)
    267 
    268 
    269 Merchant Payment
    270 ~~~~~~~~~~~~~~~~
    271 
    272 .. code:: none
    273 
    274 
    275   instructed_amount = contractTerms.amount
    276 
    277   coins = select-coin-for-operation(debit, mode, raw_amount)
    278 
    279   raw_amount = instructed_amount - merchant_deposit_fee
    280 
    281   effective_amount = instructed_amount + coins.deposit_fee + coins.refresh_fee + (wire.transfer_fee - merchant_wire_fee)
    282 
    283   .. note::
    284     The current coin-selection algorithm the order_price is neither raw_amount nor effective_amount.
    285     We can calculate the raw_amount of the payment as (contractTerms.amount - max_merchant_fee) and then this
    286     operation becomes equivalent than a deposit (in terms of fee calculation).
    287 
    288 Peer Push Credit
    289 ~~~~~~~~~~~~~~~~
    290 
    291 
    292   ``instructed_amount`` = p2pContract.amount
    293 
    294   ``coins`` = select-coin-for-operation(credit, mode, raw_amount)
    295 
    296   ``raw_amount`` = instructed_amount
    297 
    298   ``effective_amount`` = instructed_amount - coins.withdrawal_fee
    299 
    300   .. note::
    301     In the case that the withdrawal_fee of the coin selection for the push-credit amount
    302     is higher than the wire_fee of the exchange, can the wallet ask the exchange to make
    303     a wire transfer of the purse instead of proceeding?
    304 
    305 Peer Pull Debit
    306 ~~~~~~~~~~~~~~~
    307 
    308 
    309   ``instructed_amount`` = p2pContract.amount
    310 
    311   ``coins`` = select-coin-for-operation(debit, mode, raw_amount)
    312 
    313   ``raw_amount`` = instructed_amount
    314 
    315   ``effective_amount`` = instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee
    316 
    317 Refund
    318 ~~~~~~
    319 
    320 
    321   ``instructed_amount`` = refund.amount
    322 
    323   ``raw_amount`` = instructed_amount
    324 
    325   ``effective_amount`` = instructed_amount - refund_fee - refresh_fee
    326 
    327   .. note::
    328     There may be the case that the merchant should refund all the value of the purchase
    329     and that may include paying for the refund_fee.
    330 
    331     Is there a way that the merchant can initiate a refund of purchase + refund_fee so
    332     the wallet will get the same effective_amount?
    333 
    334 
    335 
    336 Illustrative Example
    337 --------------------
    338 
    339 .. warning::
    340 
    341    This section is possibly incomplete / out of date.
    342 
    343 To explain the differences between raw, effective and instructed amounts, consider the following scenario: Alice wants to send money
    344 to Bob via a P2P push payment.
    345 
    346 Example 1:
    347 
    348 * Alice starts a withdrawal of ``KUDOS:10`` from her bank's web interface into her Taler
    349   wallet. The instructed amount is ``KUDOS:10`` and (by default for bank-integrated withdrawals),
    350   the mode is ``raw-mode``.  After fees, ``KUDOS:9.8`` arrive in her Taler wallet.
    351 
    352 Example 3:
    353 
    354 * Alice wants to pay for a ``KUDOS:10`` monthly magazine subscription. Her Taler wallet is empty though.
    355 * She starts withdrawal through her Android wallet app, where she selects ``KUDOS:10`` as the instructed
    356   amount with ``mode=effective-mode``. This translates to ``amountEffective=KUDOS:10`` and ``amountRaw=KUDOS:10.10``.
    357 * Alice is redirected to her banking app where she transfers ``KUDOS:10.10`` to the exchange.
    358 * Her Taler wallet balance will be ``KUDOS:10.10`` after the withdrawal completes.
    359 
    360 Note that on the amount she chooses and the fees / denom structure of the exchange, the ``amountEffective`` might be *higher*
    361 than the instructed amount.
    362 
    363 FIXME(dold): That flow does not work if withdrawal starts in the bank. Maybe there needs to be a mechanism
    364 where the wallet tells the bank the adjusted amount that needs to be transferred? That would be a new
    365 feature in the bank integration API.
    366 
    367 Example 4:
    368 
    369 * Alice has ``KUDOS:10`` in her wallet.
    370 * Alice wants to initiate a peer-push payment with ``amountInstructed=KUDOS:8``
    371   and ``mode=effective-mode``. That means that after the payment, she expects
    372   exactly ``KUDOS:2`` to remain in her wallet.
    373 * Due to the fee configuration, her wallet computes ``amountRaw=KUDOS:7.5`` and ``amountEffective=KUDOS:7.8``.
    374   The effective amount in this case does **not** equal the instructed amount, despite the ``mode=effective-mode``.
    375   That's because there no amount that can be spend so that the spend amount with resulting refresh
    376   fees equal ``KUDOS:8``.
    377 * Alice confirms the peer-push payment initiation, and exactly ``KUDOS:7.5`` are credited
    378   to the purse that her wallet creates.
    379 * Bob merges the purse into his reserve. Bob's wallet automatically withdraws
    380   from the reserve, and his wallet balance increases by ``KUDOS:7.1``, since
    381   withdrawal fees are deducted.
    382 
    383 
    384 Discussion / Q&A
    385 ================
    386 
    387 (This should be filled in with results from discussions on mailing lists / personal communication.)