compute full fees for refresh and spending

This commit is contained in:
Florian Dold 2017-08-30 17:08:54 +02:00
parent 24e021fef3
commit 008926b184
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
18 changed files with 478 additions and 382 deletions

View File

@ -26,6 +26,7 @@
import {
AmountJson,
CoinRecord,
CoinWithDenom,
ContractTerms,
DenominationRecord,
PayCoinInfo,
@ -36,10 +37,6 @@ import {
WireFee,
} from "../types";
import {
CoinWithDenom,
} from "../wallet";
import * as timer from "../timer";
import { startWorker } from "./startWorker";

View File

@ -28,6 +28,7 @@ import {
CoinPaySig,
CoinRecord,
CoinStatus,
CoinWithDenom,
ContractTerms,
DenominationRecord,
PayCoinInfo,
@ -41,9 +42,6 @@ import {
import {
canonicalJson,
} from "../helpers";
import {
CoinWithDenom,
} from "../wallet";
import {
Amount,

View File

@ -27,28 +27,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/webex/pages/confirm-contract.tsx:69
#: src/webex/pages/confirm-contract.tsx:70
#, c-format
msgid "show more details\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:83
#: src/webex/pages/confirm-contract.tsx:84
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:88
#: src/webex/pages/confirm-contract.tsx:89
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:154
#: src/webex/pages/confirm-contract.tsx:156
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr ""
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:156
#: src/webex/pages/confirm-contract.tsx:158
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -56,67 +56,77 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:213
#: src/webex/pages/confirm-contract.tsx:214
#, c-format
msgid "The merchant%1$s offers you to purchase:\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:232
#, fuzzy, c-format
msgid "Confirm payment"
msgstr "Bezahlung bestätigen"
#: src/webex/pages/confirm-create-reserve.tsx:179
#, fuzzy, c-format
msgid "Withdrawal fees:"
msgstr "Abheben bei %1$s"
#: src/webex/pages/confirm-create-reserve.tsx:214
#: src/webex/pages/confirm-create-reserve.tsx:180
#, c-format
msgid "Rounding loss:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:215
#: src/webex/pages/confirm-create-reserve.tsx:181
#, c-format
msgid "Earliest expiration (for deposit): %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:220
#: src/webex/pages/confirm-create-reserve.tsx:186
#, c-format
msgid "# Coins"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:221
#: src/webex/pages/confirm-create-reserve.tsx:187
#, c-format
msgid "Value"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#: src/webex/pages/confirm-create-reserve.tsx:188
#, fuzzy, c-format
msgid "Withdraw Fee"
msgstr "Abheben bei %1$s"
#: src/webex/pages/confirm-create-reserve.tsx:223
#: src/webex/pages/confirm-create-reserve.tsx:189
#, c-format
msgid "Refresh Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:224
#: src/webex/pages/confirm-create-reserve.tsx:190
#, c-format
msgid "Deposit Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:278
#: src/webex/pages/confirm-create-reserve.tsx:244
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:294
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:362
#: src/webex/pages/confirm-create-reserve.tsx:328
#, c-format
msgid "The exchange is trusted by the wallet.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:368
#: src/webex/pages/confirm-create-reserve.tsx:334
#, c-format
msgid "The exchange is audited by a trusted auditor.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:374
#: src/webex/pages/confirm-create-reserve.tsx:340
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
@ -124,7 +134,7 @@ msgid ""
"If you withdraw from this exchange, it will be trusted in the future.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:383
#: src/webex/pages/confirm-create-reserve.tsx:349
#, c-format
msgid ""
"Using exchange provider%1$s.\n"
@ -132,63 +142,58 @@ msgid ""
" %2$s in fees.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:397
#: src/webex/pages/confirm-create-reserve.tsx:363
#, c-format
msgid ""
"Waiting for a response from\n"
" %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:408
#, c-format
msgid "A problem occured, see below. %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:414
#: src/webex/pages/confirm-create-reserve.tsx:380
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:457
#: src/webex/pages/confirm-create-reserve.tsx:423
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:462
#: src/webex/pages/confirm-create-reserve.tsx:428
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:519
#: src/webex/pages/confirm-create-reserve.tsx:485
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:607
#: src/webex/pages/confirm-create-reserve.tsx:570
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:616
#: src/webex/pages/confirm-create-reserve.tsx:579
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:630
#: src/webex/pages/confirm-create-reserve.tsx:593
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:637
#: src/webex/pages/confirm-create-reserve.tsx:600
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:663
#: src/webex/pages/confirm-create-reserve.tsx:626
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
@ -311,19 +316,6 @@ msgstr "Bezahlung bestätigen"
msgid "Cancel"
msgstr "Saldo"
#: src/webex/renderHtml.tsx:51
#, fuzzy, c-format
msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
msgstr ""
"%1$s\n"
" möchte einen Vertrag über %2$s\n"
" mit Ihnen abschließen."
#: src/webex/renderHtml.tsx:56
#, fuzzy, c-format
msgid "You are about to purchase:"
msgstr "Sie sind dabei, Folgendes zu kaufen:"
#: src/wire.ts:38
#, c-format
msgid "Invalid Wire"
@ -344,6 +336,17 @@ msgstr ""
msgid "Unknown Wire Detail"
msgstr ""
#, fuzzy
#~ msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
#~ msgstr ""
#~ "%1$s\n"
#~ " möchte einen Vertrag über %2$s\n"
#~ " mit Ihnen abschließen."
#, fuzzy
#~ msgid "You are about to purchase:"
#~ msgstr "Sie sind dabei, Folgendes zu kaufen:"
#, fuzzy
#~ msgid "Withdrawal fees: %1$s"
#~ msgstr "Abheben bei %1$s"

View File

@ -27,28 +27,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/webex/pages/confirm-contract.tsx:69
#: src/webex/pages/confirm-contract.tsx:70
#, c-format
msgid "show more details\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:83
#: src/webex/pages/confirm-contract.tsx:84
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:88
#: src/webex/pages/confirm-contract.tsx:89
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:154
#: src/webex/pages/confirm-contract.tsx:156
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr ""
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:156
#: src/webex/pages/confirm-contract.tsx:158
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -56,67 +56,77 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:213
#: src/webex/pages/confirm-contract.tsx:214
#, c-format
msgid "The merchant%1$s offers you to purchase:\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:232
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:179
#, c-format
msgid "Withdrawal fees:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:214
#: src/webex/pages/confirm-create-reserve.tsx:180
#, c-format
msgid "Rounding loss:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:215
#: src/webex/pages/confirm-create-reserve.tsx:181
#, c-format
msgid "Earliest expiration (for deposit): %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:220
#: src/webex/pages/confirm-create-reserve.tsx:186
#, c-format
msgid "# Coins"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:221
#: src/webex/pages/confirm-create-reserve.tsx:187
#, c-format
msgid "Value"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#: src/webex/pages/confirm-create-reserve.tsx:188
#, c-format
msgid "Withdraw Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:223
#: src/webex/pages/confirm-create-reserve.tsx:189
#, c-format
msgid "Refresh Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:224
#: src/webex/pages/confirm-create-reserve.tsx:190
#, c-format
msgid "Deposit Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:278
#: src/webex/pages/confirm-create-reserve.tsx:244
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:294
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:362
#: src/webex/pages/confirm-create-reserve.tsx:328
#, c-format
msgid "The exchange is trusted by the wallet.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:368
#: src/webex/pages/confirm-create-reserve.tsx:334
#, c-format
msgid "The exchange is audited by a trusted auditor.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:374
#: src/webex/pages/confirm-create-reserve.tsx:340
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
@ -124,7 +134,7 @@ msgid ""
"If you withdraw from this exchange, it will be trusted in the future.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:383
#: src/webex/pages/confirm-create-reserve.tsx:349
#, c-format
msgid ""
"Using exchange provider%1$s.\n"
@ -132,63 +142,58 @@ msgid ""
" %2$s in fees.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:397
#: src/webex/pages/confirm-create-reserve.tsx:363
#, c-format
msgid ""
"Waiting for a response from\n"
" %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:408
#, c-format
msgid "A problem occured, see below. %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:414
#: src/webex/pages/confirm-create-reserve.tsx:380
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:457
#: src/webex/pages/confirm-create-reserve.tsx:423
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:462
#: src/webex/pages/confirm-create-reserve.tsx:428
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:519
#: src/webex/pages/confirm-create-reserve.tsx:485
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:607
#: src/webex/pages/confirm-create-reserve.tsx:570
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:616
#: src/webex/pages/confirm-create-reserve.tsx:579
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:630
#: src/webex/pages/confirm-create-reserve.tsx:593
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:637
#: src/webex/pages/confirm-create-reserve.tsx:600
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:663
#: src/webex/pages/confirm-create-reserve.tsx:626
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
@ -311,16 +316,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: src/webex/renderHtml.tsx:51
#, c-format
msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
msgstr ""
#: src/webex/renderHtml.tsx:56
#, c-format
msgid "You are about to purchase:"
msgstr ""
#: src/wire.ts:38
#, c-format
msgid "Invalid Wire"

View File

@ -27,28 +27,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/webex/pages/confirm-contract.tsx:69
#: src/webex/pages/confirm-contract.tsx:70
#, c-format
msgid "show more details\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:83
#: src/webex/pages/confirm-contract.tsx:84
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:88
#: src/webex/pages/confirm-contract.tsx:89
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:154
#: src/webex/pages/confirm-contract.tsx:156
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr ""
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:156
#: src/webex/pages/confirm-contract.tsx:158
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -56,67 +56,77 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:213
#: src/webex/pages/confirm-contract.tsx:214
#, c-format
msgid "The merchant%1$s offers you to purchase:\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:232
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:179
#, c-format
msgid "Withdrawal fees:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:214
#: src/webex/pages/confirm-create-reserve.tsx:180
#, c-format
msgid "Rounding loss:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:215
#: src/webex/pages/confirm-create-reserve.tsx:181
#, c-format
msgid "Earliest expiration (for deposit): %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:220
#: src/webex/pages/confirm-create-reserve.tsx:186
#, c-format
msgid "# Coins"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:221
#: src/webex/pages/confirm-create-reserve.tsx:187
#, c-format
msgid "Value"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#: src/webex/pages/confirm-create-reserve.tsx:188
#, c-format
msgid "Withdraw Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:223
#: src/webex/pages/confirm-create-reserve.tsx:189
#, c-format
msgid "Refresh Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:224
#: src/webex/pages/confirm-create-reserve.tsx:190
#, c-format
msgid "Deposit Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:278
#: src/webex/pages/confirm-create-reserve.tsx:244
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:294
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:362
#: src/webex/pages/confirm-create-reserve.tsx:328
#, c-format
msgid "The exchange is trusted by the wallet.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:368
#: src/webex/pages/confirm-create-reserve.tsx:334
#, c-format
msgid "The exchange is audited by a trusted auditor.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:374
#: src/webex/pages/confirm-create-reserve.tsx:340
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
@ -124,7 +134,7 @@ msgid ""
"If you withdraw from this exchange, it will be trusted in the future.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:383
#: src/webex/pages/confirm-create-reserve.tsx:349
#, c-format
msgid ""
"Using exchange provider%1$s.\n"
@ -132,63 +142,58 @@ msgid ""
" %2$s in fees.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:397
#: src/webex/pages/confirm-create-reserve.tsx:363
#, c-format
msgid ""
"Waiting for a response from\n"
" %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:408
#, c-format
msgid "A problem occured, see below. %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:414
#: src/webex/pages/confirm-create-reserve.tsx:380
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:457
#: src/webex/pages/confirm-create-reserve.tsx:423
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:462
#: src/webex/pages/confirm-create-reserve.tsx:428
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:519
#: src/webex/pages/confirm-create-reserve.tsx:485
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:607
#: src/webex/pages/confirm-create-reserve.tsx:570
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:616
#: src/webex/pages/confirm-create-reserve.tsx:579
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:630
#: src/webex/pages/confirm-create-reserve.tsx:593
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:637
#: src/webex/pages/confirm-create-reserve.tsx:600
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:663
#: src/webex/pages/confirm-create-reserve.tsx:626
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
@ -311,16 +316,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: src/webex/renderHtml.tsx:51
#, c-format
msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
msgstr ""
#: src/webex/renderHtml.tsx:56
#, c-format
msgid "You are about to purchase:"
msgstr ""
#: src/wire.ts:38
#, c-format
msgid "Invalid Wire"

View File

@ -27,28 +27,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/webex/pages/confirm-contract.tsx:69
#: src/webex/pages/confirm-contract.tsx:70
#, c-format
msgid "show more details\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:83
#: src/webex/pages/confirm-contract.tsx:84
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:88
#: src/webex/pages/confirm-contract.tsx:89
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:154
#: src/webex/pages/confirm-contract.tsx:156
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr ""
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:156
#: src/webex/pages/confirm-contract.tsx:158
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -56,67 +56,77 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:213
#: src/webex/pages/confirm-contract.tsx:214
#, c-format
msgid "The merchant%1$s offers you to purchase:\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:232
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:179
#, c-format
msgid "Withdrawal fees:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:214
#: src/webex/pages/confirm-create-reserve.tsx:180
#, c-format
msgid "Rounding loss:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:215
#: src/webex/pages/confirm-create-reserve.tsx:181
#, c-format
msgid "Earliest expiration (for deposit): %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:220
#: src/webex/pages/confirm-create-reserve.tsx:186
#, c-format
msgid "# Coins"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:221
#: src/webex/pages/confirm-create-reserve.tsx:187
#, c-format
msgid "Value"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#: src/webex/pages/confirm-create-reserve.tsx:188
#, c-format
msgid "Withdraw Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:223
#: src/webex/pages/confirm-create-reserve.tsx:189
#, c-format
msgid "Refresh Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:224
#: src/webex/pages/confirm-create-reserve.tsx:190
#, c-format
msgid "Deposit Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:278
#: src/webex/pages/confirm-create-reserve.tsx:244
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:294
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:362
#: src/webex/pages/confirm-create-reserve.tsx:328
#, c-format
msgid "The exchange is trusted by the wallet.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:368
#: src/webex/pages/confirm-create-reserve.tsx:334
#, c-format
msgid "The exchange is audited by a trusted auditor.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:374
#: src/webex/pages/confirm-create-reserve.tsx:340
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
@ -124,7 +134,7 @@ msgid ""
"If you withdraw from this exchange, it will be trusted in the future.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:383
#: src/webex/pages/confirm-create-reserve.tsx:349
#, c-format
msgid ""
"Using exchange provider%1$s.\n"
@ -132,63 +142,58 @@ msgid ""
" %2$s in fees.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:397
#: src/webex/pages/confirm-create-reserve.tsx:363
#, c-format
msgid ""
"Waiting for a response from\n"
" %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:408
#, c-format
msgid "A problem occured, see below. %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:414
#: src/webex/pages/confirm-create-reserve.tsx:380
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:457
#: src/webex/pages/confirm-create-reserve.tsx:423
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:462
#: src/webex/pages/confirm-create-reserve.tsx:428
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:519
#: src/webex/pages/confirm-create-reserve.tsx:485
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:607
#: src/webex/pages/confirm-create-reserve.tsx:570
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:616
#: src/webex/pages/confirm-create-reserve.tsx:579
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:630
#: src/webex/pages/confirm-create-reserve.tsx:593
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:637
#: src/webex/pages/confirm-create-reserve.tsx:600
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:663
#: src/webex/pages/confirm-create-reserve.tsx:626
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
@ -311,16 +316,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: src/webex/renderHtml.tsx:51
#, c-format
msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
msgstr ""
#: src/webex/renderHtml.tsx:56
#, c-format
msgid "You are about to purchase:"
msgstr ""
#: src/wire.ts:38
#, c-format
msgid "Invalid Wire"

View File

@ -39,6 +39,12 @@ strings['de'] = {
"You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.": [
""
],
"The merchant%1$s offers you to purchase:\n": [
""
],
"Confirm payment": [
"Bezahlung bestätigen"
],
"Withdrawal fees:": [
"Abheben bei %1$s"
],
@ -84,9 +90,6 @@ strings['de'] = {
"Waiting for a response from\n %1$s": [
""
],
"A problem occured, see below. %1$s": [
""
],
"Information about fees will be available when an exchange provider is selected.": [
""
],
@ -180,12 +183,6 @@ strings['de'] = {
"Cancel": [
"Saldo"
],
"The merchant%1$swants to enter a contract over%2$s with you.\n": [
"%1$s\n möchte einen Vertrag über %2$s\n mit Ihnen abschließen."
],
"You are about to purchase:": [
"Sie sind dabei, Folgendes zu kaufen:"
],
"Invalid Wire": [
""
],
@ -225,6 +222,12 @@ strings['en-US'] = {
"You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.": [
""
],
"The merchant%1$s offers you to purchase:\n": [
""
],
"Confirm payment": [
""
],
"Withdrawal fees:": [
""
],
@ -270,9 +273,6 @@ strings['en-US'] = {
"Waiting for a response from\n %1$s": [
""
],
"A problem occured, see below. %1$s": [
""
],
"Information about fees will be available when an exchange provider is selected.": [
""
],
@ -366,12 +366,6 @@ strings['en-US'] = {
"Cancel": [
""
],
"The merchant%1$swants to enter a contract over%2$s with you.\n": [
""
],
"You are about to purchase:": [
""
],
"Invalid Wire": [
""
],
@ -411,6 +405,12 @@ strings['fr'] = {
"You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.": [
""
],
"The merchant%1$s offers you to purchase:\n": [
""
],
"Confirm payment": [
""
],
"Withdrawal fees:": [
""
],
@ -456,9 +456,6 @@ strings['fr'] = {
"Waiting for a response from\n %1$s": [
""
],
"A problem occured, see below. %1$s": [
""
],
"Information about fees will be available when an exchange provider is selected.": [
""
],
@ -552,12 +549,6 @@ strings['fr'] = {
"Cancel": [
""
],
"The merchant%1$swants to enter a contract over%2$s with you.\n": [
""
],
"You are about to purchase:": [
""
],
"Invalid Wire": [
""
],
@ -597,6 +588,12 @@ strings['it'] = {
"You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.": [
""
],
"The merchant%1$s offers you to purchase:\n": [
""
],
"Confirm payment": [
""
],
"Withdrawal fees:": [
""
],
@ -642,9 +639,6 @@ strings['it'] = {
"Waiting for a response from\n %1$s": [
""
],
"A problem occured, see below. %1$s": [
""
],
"Information about fees will be available when an exchange provider is selected.": [
""
],
@ -738,12 +732,6 @@ strings['it'] = {
"Cancel": [
""
],
"The merchant%1$swants to enter a contract over%2$s with you.\n": [
""
],
"You are about to purchase:": [
""
],
"Invalid Wire": [
""
],

View File

@ -27,28 +27,28 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: src/webex/pages/confirm-contract.tsx:69
#: src/webex/pages/confirm-contract.tsx:70
#, c-format
msgid "show more details\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:83
#: src/webex/pages/confirm-contract.tsx:84
#, c-format
msgid "Accepted exchanges:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:88
#: src/webex/pages/confirm-contract.tsx:89
#, c-format
msgid "Exchanges in the wallet:"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:154
#: src/webex/pages/confirm-contract.tsx:156
#, c-format
msgid "You have insufficient funds of the requested currency in your wallet."
msgstr ""
#. tslint:disable-next-line:max-line-length
#: src/webex/pages/confirm-contract.tsx:156
#: src/webex/pages/confirm-contract.tsx:158
#, c-format
msgid ""
"You do not have any funds from an exchange that is accepted by this "
@ -56,67 +56,77 @@ msgid ""
"wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:213
#: src/webex/pages/confirm-contract.tsx:214
#, c-format
msgid "The merchant%1$s offers you to purchase:\n"
msgstr ""
#: src/webex/pages/confirm-contract.tsx:232
#, c-format
msgid "Confirm payment"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:179
#, c-format
msgid "Withdrawal fees:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:214
#: src/webex/pages/confirm-create-reserve.tsx:180
#, c-format
msgid "Rounding loss:"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:215
#: src/webex/pages/confirm-create-reserve.tsx:181
#, c-format
msgid "Earliest expiration (for deposit): %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:220
#: src/webex/pages/confirm-create-reserve.tsx:186
#, c-format
msgid "# Coins"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:221
#: src/webex/pages/confirm-create-reserve.tsx:187
#, c-format
msgid "Value"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:222
#: src/webex/pages/confirm-create-reserve.tsx:188
#, c-format
msgid "Withdraw Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:223
#: src/webex/pages/confirm-create-reserve.tsx:189
#, c-format
msgid "Refresh Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:224
#: src/webex/pages/confirm-create-reserve.tsx:190
#, c-format
msgid "Deposit Fee"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:278
#: src/webex/pages/confirm-create-reserve.tsx:244
#, c-format
msgid "Select"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:294
#: src/webex/pages/confirm-create-reserve.tsx:260
#, c-format
msgid "Error: URL may not be relative"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:362
#: src/webex/pages/confirm-create-reserve.tsx:328
#, c-format
msgid "The exchange is trusted by the wallet.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:368
#: src/webex/pages/confirm-create-reserve.tsx:334
#, c-format
msgid "The exchange is audited by a trusted auditor.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:374
#: src/webex/pages/confirm-create-reserve.tsx:340
#, c-format
msgid ""
"Warning: The exchange is neither directly trusted nor audited by a trusted "
@ -124,7 +134,7 @@ msgid ""
"If you withdraw from this exchange, it will be trusted in the future.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:383
#: src/webex/pages/confirm-create-reserve.tsx:349
#, c-format
msgid ""
"Using exchange provider%1$s.\n"
@ -132,63 +142,58 @@ msgid ""
" %2$s in fees.\n"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:397
#: src/webex/pages/confirm-create-reserve.tsx:363
#, c-format
msgid ""
"Waiting for a response from\n"
" %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:408
#, c-format
msgid "A problem occured, see below. %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:414
#: src/webex/pages/confirm-create-reserve.tsx:380
#, c-format
msgid ""
"Information about fees will be available when an exchange provider is "
"selected."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:457
#: src/webex/pages/confirm-create-reserve.tsx:423
#, c-format
msgid "Accept fees and withdraw"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:462
#: src/webex/pages/confirm-create-reserve.tsx:428
#, c-format
msgid "Change Exchange Provider"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:519
#: src/webex/pages/confirm-create-reserve.tsx:485
#, c-format
msgid "You are about to withdraw %1$s from your bank account into your wallet."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:607
#: src/webex/pages/confirm-create-reserve.tsx:570
#, c-format
msgid ""
"Oops, something went wrong. The wallet responded with error status (%1$s)."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:616
#: src/webex/pages/confirm-create-reserve.tsx:579
#, c-format
msgid "Checking URL, please wait ..."
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:630
#: src/webex/pages/confirm-create-reserve.tsx:593
#, c-format
msgid "Can't parse amount: %1$s"
msgstr ""
#: src/webex/pages/confirm-create-reserve.tsx:637
#: src/webex/pages/confirm-create-reserve.tsx:600
#, c-format
msgid "Can't parse wire_types: %1$s"
msgstr ""
#. TODO:generic error reporting function or component.
#: src/webex/pages/confirm-create-reserve.tsx:663
#: src/webex/pages/confirm-create-reserve.tsx:626
#, c-format
msgid "Fatal error: \"%1$s\"."
msgstr ""
@ -311,16 +316,6 @@ msgstr ""
msgid "Cancel"
msgstr ""
#: src/webex/renderHtml.tsx:51
#, c-format
msgid "The merchant%1$swants to enter a contract over%2$s with you.\n"
msgstr ""
#: src/webex/renderHtml.tsx:56
#, c-format
msgid "You are about to purchase:"
msgstr ""
#: src/wire.ts:38
#, c-format
msgid "Invalid Wire"

View File

@ -822,6 +822,10 @@ export enum CoinStatus {
* Coin fully paid back.
*/
PaybackDone,
/**
* Coin was dirty but can't be refreshed.
*/
Useless,
}
@ -1467,7 +1471,10 @@ export function mkAmount(value: number, fraction: number, currency: string): Amo
/**
* Possible results for checkPay.
*/
export type CheckPayResult = "paid" | "payment-possible" | "insufficient-balance";
export interface CheckPayResult {
status: "paid" | "payment-possible" | "insufficient-balance";
coinSelection?: CoinSelectionResult;
}
/**
* Possible results for confirmPay.
@ -1695,3 +1702,30 @@ export interface PurchaseRecord {
refundsPending: { [refundSig: string]: RefundPermission };
refundsDone: { [refundSig: string]: RefundPermission };
}
/**
* Result of selecting coins, contains the exchange, and selected
* coins with their denomination.
*/
export interface CoinSelectionResult {
exchangeUrl: string;
cds: CoinWithDenom[];
totalFees: AmountJson;
}
/**
* Named tuple of coin and denomination.
*/
export interface CoinWithDenom {
/**
* A coin. Must have the same denomination public key as the associated
* denomination.
*/
coin: CoinRecord;
/**
* An associated denomination.
*/
denom: DenominationRecord;
}

View File

@ -29,7 +29,7 @@ function a(x: string): types.AmountJson {
}
function fakeCwd(current: string, value: string, feeDeposit: string): wallet.CoinWithDenom {
function fakeCwd(current: string, value: string, feeDeposit: string): types.CoinWithDenom {
return {
coin: {
blindingKey: "(mock)",
@ -64,89 +64,89 @@ function fakeCwd(current: string, value: string, feeDeposit: string): wallet.Coi
test("coin selection 1", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.1"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.1"));
const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.1"));
if (!res) {
t.fail();
return;
}
t.true(res.length === 2);
t.true(res.cds.length === 2);
t.pass();
});
test("coin selection 2", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
// Merchant covers the fee, this one shouldn't be used
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.5"));
if (!res) {
t.fail();
return;
}
t.true(res.length === 2);
t.true(res.cds.length === 2);
t.pass();
});
test("coin selection 3", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
// this coin should be selected instead of previous one with fee
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
];
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.5"));
const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.5"));
if (!res) {
t.fail();
return;
}
t.true(res.length === 2);
t.true(res.cds.length === 2);
t.pass();
});
test("coin selection 4", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.2"));
if (!res) {
t.fail();
return;
}
t.true(res.length === 3);
t.true(res.cds.length === 3);
t.pass();
});
test("coin selection 5", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
const res = wallet.selectPayCoins(cds, a("EUR:4.0"), a("EUR:0.2"));
const res = wallet.selectPayCoins([], cds, a("EUR:4.0"), a("EUR:0.2"));
t.true(!res);
t.pass();
});
test("coin selection 6", (t) => {
const cds: wallet.CoinWithDenom[] = [
const cds: types.CoinWithDenom[] = [
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.5"),
];
const res = wallet.selectPayCoins(cds, a("EUR:2.0"), a("EUR:0.2"));
const res = wallet.selectPayCoins([], cds, a("EUR:2.0"), a("EUR:0.2"));
t.true(!res);
t.pass();
});

View File

@ -51,7 +51,9 @@ import {
CheckPayResult,
CoinPaySig,
CoinRecord,
CoinSelectionResult,
CoinStatus,
CoinWithDenom,
ConfirmPayResult,
ConfirmReserveRequest,
ContractTerms,
@ -72,8 +74,10 @@ import {
PaybackConfirmation,
PreCoinRecord,
ProposalRecord,
PurchaseRecord,
QueryPaymentResult,
RefreshSessionRecord,
RefundPermission,
ReserveCreationInfo,
ReserveRecord,
ReturnCoinsRequest,
@ -82,27 +86,10 @@ import {
WalletBalanceEntry,
WireFee,
WireInfo,
RefundPermission,
PurchaseRecord,
} from "./types";
import URI = require("urijs");
/**
* Named tuple of coin and denomination.
*/
export interface CoinWithDenom {
/**
* A coin. Must have the same denomination public key as the associated
* denomination.
*/
coin: CoinRecord;
/**
* An associated denomination.
*/
denom: DenominationRecord;
}
/**
* Element of the payback list that the
@ -370,30 +357,62 @@ function isWithdrawableDenom(d: DenominationRecord) {
}
function strcmp(s1: string, s2: string): number {
if (s1 < s2) {
return -1;
}
if (s1 > s2) {
return 1;
}
return 0;
}
interface SelectPayCoinsResult {
cds: CoinWithDenom[];
totalFees: AmountJson;
}
/**
* Result of selecting coins, contains the exchange, and selected
* coins with their denomination.
* Get the amount that we lose when refreshing a coin of the given denomination
* with a certain amount left.
*
* If the amount left is zero, then the refresh cost
* is also considered to be zero. If a refresh isn't possible (e.g. due to lack of
* the right denominations), then the cost is the full amount left.
*
* Considers refresh fees, withdrawal fees after refresh and amounts too small
* to refresh.
*/
export type CoinSelectionResult = {exchangeUrl: string, cds: CoinWithDenom[]}|undefined;
export function getTotalRefreshCost(denoms: DenominationRecord[], refreshedDenom: DenominationRecord, amountLeft: AmountJson): AmountJson {
const withdrawAmount = Amounts.sub(amountLeft, refreshedDenom.feeRefresh).amount;
const withdrawDenoms = getWithdrawDenomList(withdrawAmount, denoms);
const resultingAmount = Amounts.add(Amounts.getZero(withdrawAmount.currency), ...withdrawDenoms.map((d) => d.value)).amount;
const totalCost = Amounts.sub(amountLeft, resultingAmount).amount;
console.log("total refresh cost for", amountToPretty(amountLeft), "is", amountToPretty(totalCost));
return totalCost;
}
/**
* Select coins for a payment under the merchant's constraints.
*
* @param denoms all available denoms, used to compute refresh fees
*/
export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
depositFeeLimit: AmountJson): CoinWithDenom[]|undefined {
export function selectPayCoins(denoms: DenominationRecord[], cds: CoinWithDenom[], paymentAmount: AmountJson,
depositFeeLimit: AmountJson): SelectPayCoinsResult|undefined {
if (cds.length === 0) {
return undefined;
}
// Sort by ascending deposit fee
cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit,
o2.denom.feeDeposit));
// Sort by ascending deposit fee and denomPub if deposit fee is the same
// (to guarantee deterministic results)
cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit, o2.denom.feeDeposit) || strcmp(o1.denom.denomPub, o2.denom.denomPub));
const currency = cds[0].denom.value.currency;
const cdsResult: CoinWithDenom[] = [];
let accFee: AmountJson = Amounts.getZero(currency);
let accDepositFee: AmountJson = Amounts.getZero(currency);
let accAmount: AmountJson = Amounts.getZero(currency);
let isBelowFee = false;
let coversAmount = false;
let coversAmountWithFee = false;
for (const {coin, denom} of cds) {
if (coin.suspended) {
continue;
@ -405,18 +424,30 @@ export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
continue;
}
cdsResult.push({coin, denom});
accFee = Amounts.add(denom.feeDeposit, accFee).amount;
accDepositFee = Amounts.add(denom.feeDeposit, accDepositFee).amount;
let leftAmount = Amounts.sub(coin.currentAmount, Amounts.sub(paymentAmount, accAmount).amount).amount;
accAmount = Amounts.add(coin.currentAmount, accAmount).amount;
coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
coversAmountWithFee = Amounts.cmp(accAmount,
const coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
const coversAmountWithFee = Amounts.cmp(accAmount,
Amounts.add(paymentAmount,
denom.feeDeposit).amount) >= 0;
isBelowFee = Amounts.cmp(accFee, depositFeeLimit) <= 0;
const isBelowFee = Amounts.cmp(accDepositFee, depositFeeLimit) <= 0;
console.log("coin selection", { coversAmount, isBelowFee, accFee, accAmount, paymentAmount });
console.log("coin selection", { coversAmount, isBelowFee, accDepositFee, accAmount, paymentAmount });
if ((coversAmount && isBelowFee) || coversAmountWithFee) {
return cdsResult;
let depositFeeToCover = Amounts.sub(accDepositFee, depositFeeLimit).amount;
leftAmount = Amounts.sub(leftAmount, depositFeeToCover).amount;
console.log("deposit fee to cover", amountToPretty(depositFeeToCover));
let totalFees: AmountJson = Amounts.getZero(currency);
if (coversAmountWithFee && !isBelowFee) {
// these are the fees the customer has to pay
// because the merchant doesn't cover them
totalFees = Amounts.sub(depositFeeLimit, accDepositFee).amount;
}
totalFees = Amounts.add(totalFees, getTotalRefreshCost(denoms, denom, leftAmount)).amount;
return { cds: cdsResult, totalFees };
}
}
return undefined;
@ -729,6 +760,8 @@ export class Wallet {
return [];
}
const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray();
// Denomination of the first coin, we assume that all other
// coins have the same currency
const firstDenom = await this.q().get(Stores.denominations,
@ -763,7 +796,11 @@ export class Wallet {
console.log("coin return: selecting from possible coins", { cds, amount } );
return selectPayCoins(cds, amount, amount);
const res = selectPayCoins(denoms, cds, amount, amount);
if (res) {
return res.cds;
}
return undefined
}
@ -771,7 +808,7 @@ export class Wallet {
* Get exchanges and associated coins that are still spendable,
* but only if the sum the coins' remaining value exceeds the payment amount.
*/
private async getCoinsForPayment(args: CoinsForPaymentArgs): Promise<CoinSelectionResult> {
private async getCoinsForPayment(args: CoinsForPaymentArgs): Promise<CoinSelectionResult|undefined> {
const {
allowedAuditors,
allowedExchanges,
@ -821,6 +858,7 @@ export class Wallet {
.iterIndex(Stores.coins.exchangeBaseUrlIndex,
exchange.baseUrl)
.toArray();
const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray();
if (!coins || coins.length === 0) {
continue;
}
@ -862,6 +900,7 @@ export class Wallet {
continue;
}
let totalFees = Amounts.getZero(currency);
let wireFee: AmountJson|undefined;
for (const fee of (fees.feesForType[wireMethod] || [])) {
if (fee.startStamp >= wireFeeTime && fee.endStamp <= wireFeeTime) {
@ -873,15 +912,18 @@ export class Wallet {
if (wireFee) {
const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
totalFees = Amounts.add(amortizedWireFee, totalFees).amount;
remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount;
}
}
const res = selectPayCoins(cds, remainingAmount, depositFeeLimit);
const res = selectPayCoins(denoms, cds, remainingAmount, depositFeeLimit);
if (res) {
totalFees = Amounts.add(totalFees, res.totalFees).amount;
return {
cds: res,
cds: res.cds,
exchangeUrl: exchange.baseUrl,
totalFees,
};
}
}
@ -1014,7 +1056,7 @@ export class Wallet {
// First check if we already payed for it.
const purchase = await this.q().get(Stores.purchases, proposal.contractTermsHash);
if (purchase) {
return "paid";
return { status: "paid" };
}
// If not already payed, check if we could pay for it.
@ -1031,9 +1073,9 @@ export class Wallet {
if (!res) {
console.log("not confirming payment, insufficient coins");
return "insufficient-balance";
return { status: "insufficient-balance" };
}
return "payment-possible";
return { status: "payment-possible", coinSelection: res };
}
@ -1653,6 +1695,7 @@ export class Wallet {
console.log("suspending coin", c);
c.suspended = true;
q.put(Stores.coins, c);
this.notifier.notify();
});
await q.finish();
}
@ -1840,11 +1883,14 @@ export class Wallet {
if (c.suspended) {
return balance;
}
if (!(c.status === CoinStatus.Fresh)) {
if (c.status === CoinStatus.Fresh) {
addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
return balance;
}
if (c.status === CoinStatus.Dirty) {
addTo(balance, "pendingIncoming", c.currentAmount, c.exchangeBaseUrl);
return balance;
}
console.log("collecting balance");
addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
return balance;
}
@ -1978,6 +2024,9 @@ export class Wallet {
if (newCoinDenoms.length === 0) {
console.log(`not refreshing, available amount ${amountToPretty(availableAmount)} too small`);
coin.status = CoinStatus.Useless;
await this.q().put(Stores.coins, coin);
this.notifier.notify();
return undefined;
}
@ -2007,6 +2056,7 @@ export class Wallet {
query.put(Stores.refresh, refreshSession, "refreshKey")
.mutate(Stores.coins, coin.coinPub, mutateCoin);
await query.finish();
this.notifier.notify();
const key = query.key("refreshKey");
if (!key || typeof key !== "number") {
@ -2026,7 +2076,15 @@ export class Wallet {
console.log("got old session for", oldCoinPub, session);
this.continueRefreshSession(session);
}
let refreshSession = await this.createRefreshSession(oldCoinPub);
const coin = await this.q().get(Stores.coins, oldCoinPub);
if (!coin) {
console.warn("can't refresh, coin not in database");
return;
}
if (coin.status === CoinStatus.Useless || coin.status === CoinStatus.Fresh) {
return;
}
const refreshSession = await this.createRefreshSession(oldCoinPub);
if (!refreshSession) {
// refreshing not necessary
console.log("not refreshing", oldCoinPub);
@ -2106,6 +2164,7 @@ export class Wallet {
refreshSession.norevealIndex = norevealIndex;
await this.q().put(Stores.refresh, refreshSession).finish();
this.notifier.notify();
}
@ -2186,6 +2245,7 @@ export class Wallet {
.putAll(Stores.coins, coins)
.put(Stores.refresh, refreshSession)
.finish();
this.notifier.notify();
}
@ -2344,6 +2404,7 @@ export class Wallet {
// from the reserve for the payback request.
reserve.hasPayback = true;
await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve);
this.notifier.notify();
const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
const reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl);
@ -2361,6 +2422,7 @@ export class Wallet {
}
coin.status = CoinStatus.PaybackDone;
await this.q().put(Stores.coins, coin);
this.notifier.notify();
await this.updateReserve(reservePub!);
}
@ -2502,6 +2564,7 @@ export class Wallet {
.put(Stores.coinsReturns, coinsReturnRecord)
.putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
.finish();
this.notifier.notify();
this.depositReturnedCoins(coinsReturnRecord);
}
@ -2558,6 +2621,7 @@ export class Wallet {
}
}
await this.q().put(Stores.coinsReturns, currentCrr);
this.notifier.notify();
}
}
@ -2666,4 +2730,34 @@ export class Wallet {
async getPurchase(contractTermsHash: string): Promise<PurchaseRecord|undefined> {
return this.q().get(Stores.purchases, contractTermsHash);
}
async getFullRefundFees(refundPermissions: RefundPermission[]): Promise<AmountJson> {
if (refundPermissions.length === 0) {
throw Error("no refunds given");
}
const coin0 = await this.q().get(Stores.coins, refundPermissions[0].coin_pub)
if (!coin0) {
throw Error("coin not found");
}
let feeAcc = Amounts.getZero(refundPermissions[0].refund_amount.currency);
const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, coin0.exchangeBaseUrl).toArray();
for (const rp of refundPermissions) {
const coin = await this.q().get(Stores.coins, rp.coin_pub);
if (!coin) {
throw Error("coin not found");
}
const denom = await this.q().get(Stores.denominations, [coin0.exchangeBaseUrl, coin.denomPub]);
if (!denom) {
throw Error(`denom not found (${coin.denomPub})`);
}
// FIXME: this assumes that the refund already happened.
// When it hasn't, the refresh cost is inaccurate. To fix this,
// we need introduce a flag to tell if a coin was refunded or
// refreshed normally (and what about incremental refunds?)
const refreshCost = getTotalRefreshCost(denoms, denom, Amounts.sub(rp.refund_amount, rp.refund_fee).amount);
feeAcc = Amounts.add(feeAcc, refreshCost, rp.refund_fee).amount;
}
return feeAcc;
}
}

View File

@ -191,7 +191,11 @@ export interface MessageMap {
"get-purchase": {
request: any;
response: void;
}
};
"get-full-refund-fees": {
request: { refundPermissions: types.RefundPermission[] };
response: void;
};
}
/**

View File

@ -5,6 +5,7 @@
<meta charset="UTF-8">
<title>Taler Wallet: Confirm Reserve Creation</title>
<link rel="stylesheet" type="text/css" href="../style/pure.css">
<link rel="stylesheet" type="text/css" href="../style/wallet.css">
<link rel="icon" href="/img/icon.png">

View File

@ -25,12 +25,13 @@
*/
import * as i18n from "../../i18n";
import {
CheckPayResult,
ContractTerms,
ExchangeRecord,
ProposalRecord,
} from "../../types";
import { renderContractTerms } from "../renderHtml";
import { renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi";
import * as React from "react";
@ -113,6 +114,7 @@ interface ContractPromptState {
* when pressing pay.
*/
holdCheck: boolean;
payStatus?: CheckPayResult;
}
class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
@ -150,7 +152,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
return;
}
const payStatus = await wxApi.checkPay(this.props.proposalId);
if (payStatus === "insufficient-balance") {
if (payStatus.status === "insufficient-balance") {
const msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`;
// tslint:disable-next-line:max-line-length
const msgNoMatch = i18n.str`You do not have any funds from an exchange that is accepted by this merchant. None of the exchanges accepted by the merchant is known to your wallet.`;
@ -166,10 +168,10 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
this.setState({error: msgInsufficient});
}
this.setState({payDisabled: true});
} else if (payStatus === "paid") {
this.setState({alreadyPaid: true, payDisabled: false, error: null});
} else if (payStatus.status === "paid") {
this.setState({alreadyPaid: true, payDisabled: false, error: null, payStatus});
} else {
this.setState({payDisabled: false, error: null});
this.setState({payDisabled: false, error: null, payStatus});
}
}
@ -189,7 +191,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
document.location.href = proposal.contractTerms.fulfillment_url;
break;
}
this.setState({holdCheck: false});
this.setState({holdCheck: true});
}
@ -198,15 +200,36 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
return <span>...</span>;
}
const c = this.state.proposal.contractTerms;
let merchantName;
if (c.merchant && c.merchant.name) {
merchantName = <strong>{c.merchant.name}</strong>;
} else {
merchantName = <strong>(pub: {c.merchant_pub})</strong>;
}
const amount = <strong>{renderAmount(c.amount)}</strong>;
console.log("payStatus", this.state.payStatus);
return (
<div>
<div>
{renderContractTerms(c)}
<i18n.Translate wrap="p">
The merchant <span>{merchantName}</span> {" "}
offers you to purchase:
</i18n.Translate>
<ul>
{c.products.map(
(p: any, i: number) => (<li key={i}>{p.description}: {renderAmount(p.price)}</li>))
}
</ul>
{(this.state.payStatus && this.state.payStatus.coinSelection) ?
<p>The total price is <span>{amount}</span> (plus <span>{renderAmount(this.state.payStatus.coinSelection.totalFees)}</span> fees).</p>
:
<p>The total price is <span>{amount}</span>.</p>
}
</div>
<button onClick={() => this.doPayment()}
<button className="pure-button button-success"
disabled={this.state.payDisabled}
className="accept">
Confirm payment
onClick={() => this.doPayment()}>
{i18n.str`Confirm payment`}
</button>
<div>
{(this.state.alreadyPaid ? <p className="okaybox">You already paid for this, clicking "Confirm payment" will not cost money again.</p> : <p />)}

View File

@ -37,11 +37,12 @@ interface RefundStatusViewProps {
interface RefundStatusViewState {
purchase?: types.PurchaseRecord;
refundFees?: types.AmountJson;
gotResult: boolean;
}
const RefundDetail = ({purchase}: {purchase: types.PurchaseRecord}) => {
const RefundDetail = ({purchase, fullRefundFees}: {purchase: types.PurchaseRecord, fullRefundFees: types.AmountJson}) => {
const pendingKeys = Object.keys(purchase.refundsPending);
const doneKeys = Object.keys(purchase.refundsDone);
if (pendingKeys.length == 0 && doneKeys.length == 0) {
@ -54,22 +55,18 @@ const RefundDetail = ({purchase}: {purchase: types.PurchaseRecord}) => {
}
let amountPending = types.Amounts.getZero(currency);
let feesPending = types.Amounts.getZero(currency)
for (let k of pendingKeys) {
amountPending = types.Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount;
feesPending = types.Amounts.add(feesPending, purchase.refundsPending[k].refund_fee).amount;
}
let amountDone = types.Amounts.getZero(currency);
let feesDone = types.Amounts.getZero(currency);
for (let k of doneKeys) {
amountDone = types.Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
feesDone = types.Amounts.add(feesDone, purchase.refundsDone[k].refund_fee).amount;
}
return (
<div>
<p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund fees: <AmountDisplay amount={feesDone} />)</p>
<p>Refund incoming: <AmountDisplay amount={amountPending} /> (refund fees: <AmountDisplay amount={feesPending} />)</p>
<p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund fees: <AmountDisplay amount={fullRefundFees} />)</p>
<p>Refund incoming: <AmountDisplay amount={amountPending} /></p>
</div>
);
};
@ -108,7 +105,7 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
<h1>Refund Status</h1>
<p>Status of purchase <strong>{summary}</strong> from merchant <strong>{merchantName}</strong> (order id {purchase.contractTerms.order_id}).</p>
<p>Total amount: <AmountDisplay amount={purchase.contractTerms.amount} /></p>
{purchase.finished ? <RefundDetail purchase={purchase} /> : <p>Purchase not completed.</p>}
{purchase.finished ? <RefundDetail purchase={purchase} fullRefundFees={this.state.refundFees!} /> : <p>Purchase not completed.</p>}
</div>
);
}
@ -116,7 +113,9 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
async update() {
const purchase = await wxApi.getPurchase(this.props.contractTermsHash);
console.log("got purchase", purchase);
this.setState({ purchase, gotResult: true });
const refundsDone = Object.keys(purchase.refundsDone).map((x) => purchase.refundsDone[x]);
const refundFees = await wxApi.getFullRefundFees( {refundPermissions: refundsDone });
this.setState({ purchase, gotResult: true, refundFees });
}
}

View File

@ -24,45 +24,13 @@
/**
* Imports.
*/
import { amountToPretty } from "../helpers";
import * as i18n from "../i18n";
import {
AmountJson,
Amounts,
ContractTerms,
} from "../types";
import * as React from "react";
/**
* Render contract terms for the end user to view.
*/
export function renderContractTerms(contractTerms: ContractTerms): JSX.Element {
let merchantName;
if (contractTerms.merchant && contractTerms.merchant.name) {
merchantName = <strong>{contractTerms.merchant.name}</strong>;
} else {
merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
}
const amount = <strong>{amountToPretty(contractTerms.amount)}</strong>;
return (
<div>
<i18n.Translate wrap="p">
The merchant <span>{merchantName}</span>
wants to enter a contract over <span>{amount}</span>{" "}
with you.
</i18n.Translate>
<p>{i18n.str`You are about to purchase:`}</p>
<ul>
{contractTerms.products.map(
(p: any, i: number) => (<li key={i}>{`${p.description}: ${amountToPretty(p.price)}`}</li>))
}
</ul>
</div>
);
}
/**
* Render amount as HTML, which non-breaking space between

View File

@ -33,6 +33,7 @@ import {
PreCoinRecord,
PurchaseRecord,
QueryPaymentResult,
RefundPermission,
ReserveCreationInfo,
ReserveRecord,
SenderWireInfos,
@ -345,3 +346,7 @@ export function acceptRefund(refundData: any): Promise<number> {
export function getPurchase(contractTermsHash: string): Promise<PurchaseRecord> {
return callBackend("get-purchase", { contractTermsHash });
}
export function getFullRefundFees(args: { refundPermissions: RefundPermission[] }): Promise<AmountJson> {
return callBackend("get-full-refund-fees", { refundPermissions: args.refundPermissions });
}

View File

@ -323,6 +323,8 @@ function handleMessage(sender: MessageSender,
throw Error("contractTermsHash missing");
}
return needsWallet().getPurchase(contractTermsHash);
case "get-full-refund-fees":
return needsWallet().getFullRefundFees(detail.refundPermissions);
default:
// Exhaustiveness check.
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html