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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -822,6 +822,10 @@ export enum CoinStatus {
* Coin fully paid back. * Coin fully paid back.
*/ */
PaybackDone, 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. * 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. * Possible results for confirmPay.
@ -1695,3 +1702,30 @@ export interface PurchaseRecord {
refundsPending: { [refundSig: string]: RefundPermission }; refundsPending: { [refundSig: string]: RefundPermission };
refundsDone: { [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 { return {
coin: { coin: {
blindingKey: "(mock)", blindingKey: "(mock)",
@ -64,89 +64,89 @@ function fakeCwd(current: string, value: string, feeDeposit: string): wallet.Coi
test("coin selection 1", (t) => { 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.1"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"), 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) { if (!res) {
t.fail(); t.fail();
return; return;
} }
t.true(res.length === 2); t.true(res.cds.length === 2);
t.pass(); t.pass();
}); });
test("coin selection 2", (t) => { 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.5"),
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"), fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"),
// Merchant covers the fee, this one shouldn't be used // Merchant covers the fee, this one shouldn't be used
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"), 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) { if (!res) {
t.fail(); t.fail();
return; return;
} }
t.true(res.length === 2); t.true(res.cds.length === 2);
t.pass(); t.pass();
}); });
test("coin selection 3", (t) => { 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"),
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 // this coin should be selected instead of previous one with fee
fakeCwd("EUR:1.0", "EUR:1.0", "EUR:0.0"), 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) { if (!res) {
t.fail(); t.fail();
return; return;
} }
t.true(res.length === 2); t.true(res.cds.length === 2);
t.pass(); t.pass();
}); });
test("coin selection 4", (t) => { 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"), 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) { if (!res) {
t.fail(); t.fail();
return; return;
} }
t.true(res.length === 3); t.true(res.cds.length === 3);
t.pass(); t.pass();
}); });
test("coin selection 5", (t) => { 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"), 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.true(!res);
t.pass(); t.pass();
}); });
test("coin selection 6", (t) => { 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"),
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.true(!res);
t.pass(); t.pass();
}); });

View File

@ -51,7 +51,9 @@ import {
CheckPayResult, CheckPayResult,
CoinPaySig, CoinPaySig,
CoinRecord, CoinRecord,
CoinSelectionResult,
CoinStatus, CoinStatus,
CoinWithDenom,
ConfirmPayResult, ConfirmPayResult,
ConfirmReserveRequest, ConfirmReserveRequest,
ContractTerms, ContractTerms,
@ -72,8 +74,10 @@ import {
PaybackConfirmation, PaybackConfirmation,
PreCoinRecord, PreCoinRecord,
ProposalRecord, ProposalRecord,
PurchaseRecord,
QueryPaymentResult, QueryPaymentResult,
RefreshSessionRecord, RefreshSessionRecord,
RefundPermission,
ReserveCreationInfo, ReserveCreationInfo,
ReserveRecord, ReserveRecord,
ReturnCoinsRequest, ReturnCoinsRequest,
@ -82,27 +86,10 @@ import {
WalletBalanceEntry, WalletBalanceEntry,
WireFee, WireFee,
WireInfo, WireInfo,
RefundPermission,
PurchaseRecord,
} from "./types"; } from "./types";
import URI = require("urijs"); 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 * 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 * Get the amount that we lose when refreshing a coin of the given denomination
* coins with their 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. * 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, export function selectPayCoins(denoms: DenominationRecord[], cds: CoinWithDenom[], paymentAmount: AmountJson,
depositFeeLimit: AmountJson): CoinWithDenom[]|undefined { depositFeeLimit: AmountJson): SelectPayCoinsResult|undefined {
if (cds.length === 0) { if (cds.length === 0) {
return undefined; return undefined;
} }
// Sort by ascending deposit fee // Sort by ascending deposit fee and denomPub if deposit fee is the same
cds.sort((o1, o2) => Amounts.cmp(o1.denom.feeDeposit, // (to guarantee deterministic results)
o2.denom.feeDeposit)); 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 currency = cds[0].denom.value.currency;
const cdsResult: CoinWithDenom[] = []; const cdsResult: CoinWithDenom[] = [];
let accFee: AmountJson = Amounts.getZero(currency); let accDepositFee: AmountJson = Amounts.getZero(currency);
let accAmount: 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) { for (const {coin, denom} of cds) {
if (coin.suspended) { if (coin.suspended) {
continue; continue;
@ -405,18 +424,30 @@ export function selectPayCoins(cds: CoinWithDenom[], paymentAmount: AmountJson,
continue; continue;
} }
cdsResult.push({coin, denom}); 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; accAmount = Amounts.add(coin.currentAmount, accAmount).amount;
coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0; const coversAmount = Amounts.cmp(accAmount, paymentAmount) >= 0;
coversAmountWithFee = Amounts.cmp(accAmount, const coversAmountWithFee = Amounts.cmp(accAmount,
Amounts.add(paymentAmount, Amounts.add(paymentAmount,
denom.feeDeposit).amount) >= 0; 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) { 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; return undefined;
@ -729,6 +760,8 @@ export class Wallet {
return []; return [];
} }
const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray();
// Denomination of the first coin, we assume that all other // Denomination of the first coin, we assume that all other
// coins have the same currency // coins have the same currency
const firstDenom = await this.q().get(Stores.denominations, 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 } ); 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, * Get exchanges and associated coins that are still spendable,
* but only if the sum the coins' remaining value exceeds the payment amount. * 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 { const {
allowedAuditors, allowedAuditors,
allowedExchanges, allowedExchanges,
@ -821,6 +858,7 @@ export class Wallet {
.iterIndex(Stores.coins.exchangeBaseUrlIndex, .iterIndex(Stores.coins.exchangeBaseUrlIndex,
exchange.baseUrl) exchange.baseUrl)
.toArray(); .toArray();
const denoms = await this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex, exchange.baseUrl).toArray();
if (!coins || coins.length === 0) { if (!coins || coins.length === 0) {
continue; continue;
} }
@ -862,6 +900,7 @@ export class Wallet {
continue; continue;
} }
let totalFees = Amounts.getZero(currency);
let wireFee: AmountJson|undefined; let wireFee: AmountJson|undefined;
for (const fee of (fees.feesForType[wireMethod] || [])) { for (const fee of (fees.feesForType[wireMethod] || [])) {
if (fee.startStamp >= wireFeeTime && fee.endStamp <= wireFeeTime) { if (fee.startStamp >= wireFeeTime && fee.endStamp <= wireFeeTime) {
@ -873,15 +912,18 @@ export class Wallet {
if (wireFee) { if (wireFee) {
const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization); const amortizedWireFee = Amounts.divide(wireFee, wireFeeAmortization);
if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) { if (Amounts.cmp(wireFeeLimit, amortizedWireFee) < 0) {
totalFees = Amounts.add(amortizedWireFee, totalFees).amount;
remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount; remainingAmount = Amounts.add(amortizedWireFee, remainingAmount).amount;
} }
} }
const res = selectPayCoins(cds, remainingAmount, depositFeeLimit); const res = selectPayCoins(denoms, cds, remainingAmount, depositFeeLimit);
if (res) { if (res) {
totalFees = Amounts.add(totalFees, res.totalFees).amount;
return { return {
cds: res, cds: res.cds,
exchangeUrl: exchange.baseUrl, exchangeUrl: exchange.baseUrl,
totalFees,
}; };
} }
} }
@ -1014,7 +1056,7 @@ export class Wallet {
// First check if we already payed for it. // First check if we already payed for it.
const purchase = await this.q().get(Stores.purchases, proposal.contractTermsHash); const purchase = await this.q().get(Stores.purchases, proposal.contractTermsHash);
if (purchase) { if (purchase) {
return "paid"; return { status: "paid" };
} }
// If not already payed, check if we could pay for it. // If not already payed, check if we could pay for it.
@ -1031,9 +1073,9 @@ export class Wallet {
if (!res) { if (!res) {
console.log("not confirming payment, insufficient coins"); 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); console.log("suspending coin", c);
c.suspended = true; c.suspended = true;
q.put(Stores.coins, c); q.put(Stores.coins, c);
this.notifier.notify();
}); });
await q.finish(); await q.finish();
} }
@ -1840,11 +1883,14 @@ export class Wallet {
if (c.suspended) { if (c.suspended) {
return balance; 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; return balance;
} }
console.log("collecting balance");
addTo(balance, "available", c.currentAmount, c.exchangeBaseUrl);
return balance; return balance;
} }
@ -1978,6 +2024,9 @@ export class Wallet {
if (newCoinDenoms.length === 0) { if (newCoinDenoms.length === 0) {
console.log(`not refreshing, available amount ${amountToPretty(availableAmount)} too small`); 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; return undefined;
} }
@ -2007,6 +2056,7 @@ export class Wallet {
query.put(Stores.refresh, refreshSession, "refreshKey") query.put(Stores.refresh, refreshSession, "refreshKey")
.mutate(Stores.coins, coin.coinPub, mutateCoin); .mutate(Stores.coins, coin.coinPub, mutateCoin);
await query.finish(); await query.finish();
this.notifier.notify();
const key = query.key("refreshKey"); const key = query.key("refreshKey");
if (!key || typeof key !== "number") { if (!key || typeof key !== "number") {
@ -2026,7 +2076,15 @@ export class Wallet {
console.log("got old session for", oldCoinPub, session); console.log("got old session for", oldCoinPub, session);
this.continueRefreshSession(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) { if (!refreshSession) {
// refreshing not necessary // refreshing not necessary
console.log("not refreshing", oldCoinPub); console.log("not refreshing", oldCoinPub);
@ -2106,6 +2164,7 @@ export class Wallet {
refreshSession.norevealIndex = norevealIndex; refreshSession.norevealIndex = norevealIndex;
await this.q().put(Stores.refresh, refreshSession).finish(); await this.q().put(Stores.refresh, refreshSession).finish();
this.notifier.notify();
} }
@ -2186,6 +2245,7 @@ export class Wallet {
.putAll(Stores.coins, coins) .putAll(Stores.coins, coins)
.put(Stores.refresh, refreshSession) .put(Stores.refresh, refreshSession)
.finish(); .finish();
this.notifier.notify();
} }
@ -2344,6 +2404,7 @@ export class Wallet {
// from the reserve for the payback request. // from the reserve for the payback request.
reserve.hasPayback = true; reserve.hasPayback = true;
await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve); await this.q().put(Stores.coins, coin).put(Stores.reserves, reserve);
this.notifier.notify();
const paybackRequest = await this.cryptoApi.createPaybackRequest(coin); const paybackRequest = await this.cryptoApi.createPaybackRequest(coin);
const reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl); const reqUrl = new URI("payback").absoluteTo(coin.exchangeBaseUrl);
@ -2361,6 +2422,7 @@ export class Wallet {
} }
coin.status = CoinStatus.PaybackDone; coin.status = CoinStatus.PaybackDone;
await this.q().put(Stores.coins, coin); await this.q().put(Stores.coins, coin);
this.notifier.notify();
await this.updateReserve(reservePub!); await this.updateReserve(reservePub!);
} }
@ -2502,6 +2564,7 @@ export class Wallet {
.put(Stores.coinsReturns, coinsReturnRecord) .put(Stores.coinsReturns, coinsReturnRecord)
.putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin)) .putAll(Stores.coins, payCoinInfo.map((pci) => pci.updatedCoin))
.finish(); .finish();
this.notifier.notify();
this.depositReturnedCoins(coinsReturnRecord); this.depositReturnedCoins(coinsReturnRecord);
} }
@ -2558,6 +2621,7 @@ export class Wallet {
} }
} }
await this.q().put(Stores.coinsReturns, currentCrr); await this.q().put(Stores.coinsReturns, currentCrr);
this.notifier.notify();
} }
} }
@ -2666,4 +2730,34 @@ export class Wallet {
async getPurchase(contractTermsHash: string): Promise<PurchaseRecord|undefined> { async getPurchase(contractTermsHash: string): Promise<PurchaseRecord|undefined> {
return this.q().get(Stores.purchases, contractTermsHash); 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": { "get-purchase": {
request: any; request: any;
response: void; response: void;
} };
"get-full-refund-fees": {
request: { refundPermissions: types.RefundPermission[] };
response: void;
};
} }
/** /**

View File

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

View File

@ -25,12 +25,13 @@
*/ */
import * as i18n from "../../i18n"; import * as i18n from "../../i18n";
import { import {
CheckPayResult,
ContractTerms, ContractTerms,
ExchangeRecord, ExchangeRecord,
ProposalRecord, ProposalRecord,
} from "../../types"; } from "../../types";
import { renderContractTerms } from "../renderHtml"; import { renderAmount } from "../renderHtml";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
import * as React from "react"; import * as React from "react";
@ -113,6 +114,7 @@ interface ContractPromptState {
* when pressing pay. * when pressing pay.
*/ */
holdCheck: boolean; holdCheck: boolean;
payStatus?: CheckPayResult;
} }
class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> { class ContractPrompt extends React.Component<ContractPromptProps, ContractPromptState> {
@ -150,7 +152,7 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
return; return;
} }
const payStatus = await wxApi.checkPay(this.props.proposalId); 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.`; const msgInsufficient = i18n.str`You have insufficient funds of the requested currency in your wallet.`;
// tslint:disable-next-line:max-line-length // 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.`; 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({error: msgInsufficient});
} }
this.setState({payDisabled: true}); this.setState({payDisabled: true});
} else if (payStatus === "paid") { } else if (payStatus.status === "paid") {
this.setState({alreadyPaid: true, payDisabled: false, error: null}); this.setState({alreadyPaid: true, payDisabled: false, error: null, payStatus});
} else { } 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; document.location.href = proposal.contractTerms.fulfillment_url;
break; break;
} }
this.setState({holdCheck: false}); this.setState({holdCheck: true});
} }
@ -198,15 +200,36 @@ class ContractPrompt extends React.Component<ContractPromptProps, ContractPrompt
return <span>...</span>; return <span>...</span>;
} }
const c = this.state.proposal.contractTerms; 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 ( return (
<div> <div>
<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> </div>
<button onClick={() => this.doPayment()} <button className="pure-button button-success"
disabled={this.state.payDisabled} disabled={this.state.payDisabled}
className="accept"> onClick={() => this.doPayment()}>
Confirm payment {i18n.str`Confirm payment`}
</button> </button>
<div> <div>
{(this.state.alreadyPaid ? <p className="okaybox">You already paid for this, clicking "Confirm payment" will not cost money again.</p> : <p />)} {(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 { interface RefundStatusViewState {
purchase?: types.PurchaseRecord; purchase?: types.PurchaseRecord;
refundFees?: types.AmountJson;
gotResult: boolean; 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 pendingKeys = Object.keys(purchase.refundsPending);
const doneKeys = Object.keys(purchase.refundsDone); const doneKeys = Object.keys(purchase.refundsDone);
if (pendingKeys.length == 0 && doneKeys.length == 0) { if (pendingKeys.length == 0 && doneKeys.length == 0) {
@ -54,22 +55,18 @@ const RefundDetail = ({purchase}: {purchase: types.PurchaseRecord}) => {
} }
let amountPending = types.Amounts.getZero(currency); let amountPending = types.Amounts.getZero(currency);
let feesPending = types.Amounts.getZero(currency)
for (let k of pendingKeys) { for (let k of pendingKeys) {
amountPending = types.Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount; 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 amountDone = types.Amounts.getZero(currency);
let feesDone = types.Amounts.getZero(currency);
for (let k of doneKeys) { for (let k of doneKeys) {
amountDone = types.Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount; amountDone = types.Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
feesDone = types.Amounts.add(feesDone, purchase.refundsDone[k].refund_fee).amount;
} }
return ( return (
<div> <div>
<p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund fees: <AmountDisplay amount={feesDone} />)</p> <p>Refund fully received: <AmountDisplay amount={amountDone} /> (refund fees: <AmountDisplay amount={fullRefundFees} />)</p>
<p>Refund incoming: <AmountDisplay amount={amountPending} /> (refund fees: <AmountDisplay amount={feesPending} />)</p> <p>Refund incoming: <AmountDisplay amount={amountPending} /></p>
</div> </div>
); );
}; };
@ -108,7 +105,7 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
<h1>Refund Status</h1> <h1>Refund Status</h1>
<p>Status of purchase <strong>{summary}</strong> from merchant <strong>{merchantName}</strong> (order id {purchase.contractTerms.order_id}).</p> <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> <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> </div>
); );
} }
@ -116,7 +113,9 @@ class RefundStatusView extends React.Component<RefundStatusViewProps, RefundStat
async update() { async update() {
const purchase = await wxApi.getPurchase(this.props.contractTermsHash); const purchase = await wxApi.getPurchase(this.props.contractTermsHash);
console.log("got purchase", purchase); 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. * Imports.
*/ */
import { amountToPretty } from "../helpers";
import * as i18n from "../i18n";
import { import {
AmountJson, AmountJson,
Amounts, Amounts,
ContractTerms,
} from "../types"; } from "../types";
import * as React from "react"; 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 * Render amount as HTML, which non-breaking space between

View File

@ -33,6 +33,7 @@ import {
PreCoinRecord, PreCoinRecord,
PurchaseRecord, PurchaseRecord,
QueryPaymentResult, QueryPaymentResult,
RefundPermission,
ReserveCreationInfo, ReserveCreationInfo,
ReserveRecord, ReserveRecord,
SenderWireInfos, SenderWireInfos,
@ -345,3 +346,7 @@ export function acceptRefund(refundData: any): Promise<number> {
export function getPurchase(contractTermsHash: string): Promise<PurchaseRecord> { export function getPurchase(contractTermsHash: string): Promise<PurchaseRecord> {
return callBackend("get-purchase", { contractTermsHash }); 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"); throw Error("contractTermsHash missing");
} }
return needsWallet().getPurchase(contractTermsHash); return needsWallet().getPurchase(contractTermsHash);
case "get-full-refund-fees":
return needsWallet().getFullRefundFees(detail.refundPermissions);
default: default:
// Exhaustiveness check. // Exhaustiveness check.
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html // See https://www.typescriptlang.org/docs/handbook/advanced-types.html