This design document discusses terminology and concepts used in the wallet for balances and amounts.
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.
raw amount is the total to be wired in exchange bank account
coins
= select-coin(withdraw, mode, instructed_amount)
raw_amount
= instructed_amount
effective_amount
= instructed_amount - coins.withdrawal_fee
raw_amount
= instructed_amount + coins.withdrawal_fee
effective_amount
= instructed_amount
raw amount is the total wire transfer in the bank account
coins
= select-coin(deposit, mode, instructed_amount)
raw_amount
= instructed_amount
effective_amount
= instructed_amount + coins.deposit_fee + coins.refresh_fee + wire.transfer_fee
raw_amount
= instructed_amount - coins.deposit_fee - coins.refresh_fee - wire.transfer_fee
effective_amount
= instructed_amount
raw amount is the purse_value in the exchange that counter-party need will pay
coins
= select-coin(pull, mode, instructed_amount)
raw_amount
= instructed_amount
effective_amount
= instructed_amount - coins.withdrawal_fee - purse_fee
raw_amount
= instructed_amount + coins.withdrawal_fee + purse_fee
effective_amount
= instructed_amount
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.
raw amount is the purse_value in the exchange to be withdrawn that counter-party will withdraw
coins
= select-coin(push, mode, instructed_amount)
raw_amount
= instructed_amount
effective_amount
= instructed_amount + coins.deposit_fee + purse_fee
raw_amount
= instructed_amount - coins.deposit_fee - purse_fee
effective_amount
= instructed_amount
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)
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).
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?
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
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?
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.
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: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)
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 walletFIXME(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 feeseffective-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 receivereffective-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.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:
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:
KUDOS:10
monthly magazine subscription. Her Taler wallet is empty though.KUDOS:10
as the instructed
amount with mode=effective-mode
. This translates to amountEffective=KUDOS:10
and amountRaw=KUDOS:10.10
.KUDOS:10.10
to the exchange.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:
KUDOS:10
in her wallet.amountInstructed=KUDOS:8
and mode=effective-mode
. That means that after the payment, she expects
exactly KUDOS:2
to remain in her wallet.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
.KUDOS:7.5
are credited
to the purse that her wallet creates.KUDOS:7.1
, since
withdrawal fees are deducted.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 refreshesmaterial
: 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.The wallet uses the following terminology when an operation can’t succeed because the balance is too low, even though the instructed amount
FIXME(dold): Should we specify an upper-bound fee-gap-estimate to simplify it for the UIs?
(This should be filled in with results from discussions on mailing lists / personal communication.)