diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/crypto/cryptoApi.ts | 5 | ||||
-rw-r--r-- | src/crypto/cryptoWorker.ts | 4 | ||||
-rw-r--r-- | src/i18n/de.po | 97 | ||||
-rw-r--r-- | src/i18n/en-US.po | 83 | ||||
-rw-r--r-- | src/i18n/fr.po | 83 | ||||
-rw-r--r-- | src/i18n/it.po | 83 | ||||
-rw-r--r-- | src/i18n/strings.ts | 60 | ||||
-rw-r--r-- | src/i18n/taler-wallet-webex.pot | 83 | ||||
-rw-r--r-- | src/types.ts | 36 | ||||
-rw-r--r-- | src/wallet-test.ts | 34 | ||||
-rw-r--r-- | src/wallet.ts | 186 | ||||
-rw-r--r-- | src/webex/messages.ts | 6 | ||||
-rw-r--r-- | src/webex/pages/confirm-contract.html | 1 | ||||
-rw-r--r-- | src/webex/pages/confirm-contract.tsx | 43 | ||||
-rw-r--r-- | src/webex/pages/refund.tsx | 17 | ||||
-rw-r--r-- | src/webex/renderHtml.tsx | 32 | ||||
-rw-r--r-- | src/webex/wxApi.ts | 5 | ||||
-rw-r--r-- | src/webex/wxBackend.ts | 2 |
18 files changed, 478 insertions, 382 deletions
diff --git a/src/crypto/cryptoApi.ts b/src/crypto/cryptoApi.ts index 227c3d346..00013f0d3 100644 --- a/src/crypto/cryptoApi.ts +++ b/src/crypto/cryptoApi.ts @@ -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"; diff --git a/src/crypto/cryptoWorker.ts b/src/crypto/cryptoWorker.ts index 1db6e62d5..92b766e9c 100644 --- a/src/crypto/cryptoWorker.ts +++ b/src/crypto/cryptoWorker.ts @@ -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, diff --git a/src/i18n/de.po b/src/i18n/de.po index cb57219c5..38ce00c45 100644 --- a/src/i18n/de.po +++ b/src/i18n/de.po @@ -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" @@ -345,6 +337,17 @@ 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" diff --git a/src/i18n/en-US.po b/src/i18n/en-US.po index a39c8331a..66d4bd118 100644 --- a/src/i18n/en-US.po +++ b/src/i18n/en-US.po @@ -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" diff --git a/src/i18n/fr.po b/src/i18n/fr.po index dc271a749..d804bd204 100644 --- a/src/i18n/fr.po +++ b/src/i18n/fr.po @@ -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" diff --git a/src/i18n/it.po b/src/i18n/it.po index dc271a749..d804bd204 100644 --- a/src/i18n/it.po +++ b/src/i18n/it.po @@ -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" diff --git a/src/i18n/strings.ts b/src/i18n/strings.ts index 75672cbd3..41c8d72a2 100644 --- a/src/i18n/strings.ts +++ b/src/i18n/strings.ts @@ -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": [ "" ], diff --git a/src/i18n/taler-wallet-webex.pot b/src/i18n/taler-wallet-webex.pot index dc271a749..d804bd204 100644 --- a/src/i18n/taler-wallet-webex.pot +++ b/src/i18n/taler-wallet-webex.pot @@ -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" diff --git a/src/types.ts b/src/types.ts index d016b7fea..90fe7cf9c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -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; +} diff --git a/src/wallet-test.ts b/src/wallet-test.ts index acd776d67..037cc7592 100644 --- a/src/wallet-test.ts +++ b/src/wallet-test.ts @@ -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(); }); diff --git a/src/wallet.ts b/src/wallet.ts index 3d095fc06..f194755e8 100644 --- a/src/wallet.ts +++ b/src/wallet.ts @@ -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; + } } diff --git a/src/webex/messages.ts b/src/webex/messages.ts index 7de28b9e9..122bd8fe2 100644 --- a/src/webex/messages.ts +++ b/src/webex/messages.ts @@ -191,7 +191,11 @@ export interface MessageMap { "get-purchase": { request: any; response: void; - } + }; + "get-full-refund-fees": { + request: { refundPermissions: types.RefundPermission[] }; + response: void; + }; } /** diff --git a/src/webex/pages/confirm-contract.html b/src/webex/pages/confirm-contract.html index 394de582a..223d413d8 100644 --- a/src/webex/pages/confirm-contract.html +++ b/src/webex/pages/confirm-contract.html @@ -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"> diff --git a/src/webex/pages/confirm-contract.tsx b/src/webex/pages/confirm-contract.tsx index fa71b1028..5436cb5a6 100644 --- a/src/webex/pages/confirm-contract.tsx +++ b/src/webex/pages/confirm-contract.tsx @@ -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 />)} diff --git a/src/webex/pages/refund.tsx b/src/webex/pages/refund.tsx index b9506bf29..d2c21c2f4 100644 --- a/src/webex/pages/refund.tsx +++ b/src/webex/pages/refund.tsx @@ -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 }); } } diff --git a/src/webex/renderHtml.tsx b/src/webex/renderHtml.tsx index 2a5b50533..d26f726af 100644 --- a/src/webex/renderHtml.tsx +++ b/src/webex/renderHtml.tsx @@ -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 diff --git a/src/webex/wxApi.ts b/src/webex/wxApi.ts index 1423da53b..096d855e0 100644 --- a/src/webex/wxApi.ts +++ b/src/webex/wxApi.ts @@ -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 }); +} diff --git a/src/webex/wxBackend.ts b/src/webex/wxBackend.ts index db2ffbfbd..16da3d97d 100644 --- a/src/webex/wxBackend.ts +++ b/src/webex/wxBackend.ts @@ -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 |