diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index 94a988c67..095266f22 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -571,6 +571,11 @@ export interface DepositInfo { export interface ExchangesListRespose { exchanges: ExchangeListItem[]; } + +export interface ExchangeDetailledListRespose { + exchanges: ExchangeFullDetailsListItem[]; +} + export interface WalletCoreVersion { hash: string | undefined; version: string; @@ -578,6 +583,7 @@ export interface WalletCoreVersion { merchant: string; bank: string; } + export interface KnownBankAccounts { accounts: { [payto: string]: PaytoUri }; } @@ -727,7 +733,7 @@ export interface DenominationInfo { stampExpireDeposit: TalerProtocolTimestamp; } -export interface ExchangeListItem { +export interface ExchangeFullDetailsListItem { exchangeBaseUrl: string; currency: string; paytoUris: string[]; @@ -737,6 +743,13 @@ export interface ExchangeListItem { denominations: DenominationInfo[]; } +export interface ExchangeListItem { + exchangeBaseUrl: string; + currency: string; + paytoUris: string[]; + tos: ExchangeTos; +} + const codecForAuditorDenomSig = (): Codec => buildCodecForObject() .property("denom_pub_h", codecForString()) @@ -758,20 +771,29 @@ const codecForExchangeTos = (): Codec => .property("content", codecOptional(codecForString())) .build("ExchangeTos"); +export const codecForExchangeFullDetailsListItem = + (): Codec => + buildCodecForObject() + .property("currency", codecForString()) + .property("exchangeBaseUrl", codecForString()) + .property("paytoUris", codecForList(codecForString())) + .property("tos", codecForExchangeTos()) + .property("auditors", codecForList(codecForExchangeAuditor())) + .property("wireInfo", codecForWireInfo()) + .property("denominations", codecForList(codecForDenominationInfo())) + .build("ExchangeListItem"); + export const codecForExchangeListItem = (): Codec => buildCodecForObject() .property("currency", codecForString()) .property("exchangeBaseUrl", codecForString()) .property("paytoUris", codecForList(codecForString())) .property("tos", codecForExchangeTos()) - .property("auditors", codecForList(codecForExchangeAuditor())) - .property("wireInfo", codecForWireInfo()) - .property("denominations", codecForList(codecForDenominationInfo())) .build("ExchangeListItem"); export const codecForExchangesListResponse = (): Codec => buildCodecForObject() - .property("exchanges", codecForList(codecForExchangeListItem())) + .property("exchanges", codecForList(codecForExchangeFullDetailsListItem())) .build("ExchangesListRespose"); export interface AcceptManualWithdrawalResult { diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 9678258f0..2b981e117 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -1436,9 +1436,6 @@ export async function getWithdrawalDetailsForUri( content: details.termsOfServiceText, }, paytoUris: details.wireInfo.accounts.map((x) => x.payto_uri), - auditors: details.auditors, - wireInfo: details.wireInfo, - denominations: denominations, }); } } diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts index 688985521..a7a39c5a9 100644 --- a/packages/taler-wallet-core/src/wallet.ts +++ b/packages/taler-wallet-core/src/wallet.ts @@ -72,7 +72,7 @@ import { Duration, durationFromSpec, durationMin, - ExchangeListItem, + ExchangeFullDetailsListItem, ExchangesListRespose, GetExchangeTosResult, j2s, @@ -87,6 +87,7 @@ import { URL, WalletNotification, WalletCoreVersion, + ExchangeListItem, } from "@gnu-taler/taler-util"; import { TalerCryptoInterface } from "./crypto/cryptoImplementation.js"; import { @@ -207,7 +208,11 @@ import { } from "./util/promiseUtils.js"; import { DbAccess, GetReadWriteAccess } from "./util/query.js"; import { TimerAPI, TimerGroup } from "./util/timer.js"; -import { WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, WALLET_EXCHANGE_PROTOCOL_VERSION, WALLET_MERCHANT_PROTOCOL_VERSION } from "./versions.js"; +import { + WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, + WALLET_EXCHANGE_PROTOCOL_VERSION, + WALLET_MERCHANT_PROTOCOL_VERSION, +} from "./versions.js"; import { WalletCoreApiClient } from "./wallet-api-types.js"; const builtinAuditors: AuditorTrustRecord[] = [ @@ -592,6 +597,53 @@ async function getExchanges( continue; } + exchanges.push({ + exchangeBaseUrl: r.baseUrl, + currency, + tos: { + acceptedVersion: exchangeDetails.termsOfServiceAcceptedEtag, + currentVersion: exchangeDetails.termsOfServiceLastEtag, + contentType: exchangeDetails.termsOfServiceContentType, + content: exchangeDetails.termsOfServiceText, + }, + paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri), + }); + } + }); + return { exchanges }; +} + +async function getExchangesDetailled( + ws: InternalWalletState, +): Promise { + const exchanges: ExchangeFullDetailsListItem[] = []; + await ws.db + .mktx((x) => ({ + exchanges: x.exchanges, + exchangeDetails: x.exchangeDetails, + denominations: x.denominations, + })) + .runReadOnly(async (tx) => { + const exchangeRecords = await tx.exchanges.iter().toArray(); + for (const r of exchangeRecords) { + const dp = r.detailsPointer; + if (!dp) { + continue; + } + const { currency } = dp; + const exchangeDetails = await getExchangeDetails(tx, r.baseUrl); + if (!exchangeDetails) { + continue; + } + + const denominations = await tx.denominations.indexes.byExchangeBaseUrl + .iter(r.baseUrl) + .toArray(); + + if (!denominations) { + continue; + } + exchanges.push({ exchangeBaseUrl: r.baseUrl, currency, @@ -782,6 +834,9 @@ async function dispatchRequestInternal( case "listExchanges": { return await getExchanges(ws); } + case "listExchangesDetailled": { + return await getExchangesDetailled(ws); + } case "listKnownBankAccounts": { const req = codecForListKnownBankAccounts().decode(payload); return await listKnownBankAccounts(ws, req.currency); @@ -790,6 +845,7 @@ async function dispatchRequestInternal( const req = codecForGetWithdrawalDetailsForUri().decode(payload); return await getWithdrawalDetailsForUri(ws, req.talerWithdrawUri); } + case "getExchangeWithdrawalInfo": { const req = codecForGetExchangeWithdrawalInfo().decode(payload); return await getExchangeWithdrawalInfo( @@ -1078,7 +1134,7 @@ async function dispatchRequestInternal( exchange: WALLET_EXCHANGE_PROTOCOL_VERSION, merchant: WALLET_MERCHANT_PROTOCOL_VERSION, bank: WALLET_BANK_INTEGRATION_PROTOCOL_VERSION, - } + }; return version; } } diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts index 804d216eb..bb0f0d1af 100644 --- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts +++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts @@ -30,7 +30,7 @@ export function useComponentState( const [subject, setSubject] = useState(""); const [talerUri, setTalerUri] = useState("") - const hook = useAsyncAsHook(api.listExchanges); + const hook = useAsyncAsHook(api.listExchangesDetailled); const [exchangeIdx, setExchangeIdx] = useState("0") const [operationError, setOperationError] = useState(undefined) diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts index 3b138e74d..58d3cfedf 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts @@ -30,7 +30,7 @@ export function useComponentStateFromParams( const [ageRestricted, setAgeRestricted] = useState(0); - const exchangeHook = useAsyncAsHook(api.listExchanges); + const exchangeHook = useAsyncAsHook(api.listExchangesDetailled); const exchangeHookDep = !exchangeHook || exchangeHook.hasError || !exchangeHook.response diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts index c72f906e5..6c9e0c3ba 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts +++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts @@ -21,6 +21,7 @@ import { Amounts, + ExchangeFullDetailsListItem, ExchangeListItem, GetExchangeTosResult, } from "@gnu-taler/taler-util"; @@ -29,7 +30,7 @@ import { expect } from "chai"; import { mountHook } from "../../test-utils.js"; import { useComponentStateFromURI } from "./state.js"; -const exchanges: ExchangeListItem[] = [ +const exchanges: ExchangeFullDetailsListItem[] = [ { currency: "ARS", exchangeBaseUrl: "http://exchange.demo.taler.net", diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx index fa1a606f7..b63bbacaf 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx @@ -171,7 +171,7 @@ export function SelectCurrency({ }): VNode { const { i18n } = useTranslationContext(); - const hook = useAsyncAsHook(wxApi.listExchanges); + const hook = useAsyncAsHook(wxApi.listExchangesDetailled); if (!hook) { return ; diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx index 3406b04ef..8ba92a8f2 100644 --- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx @@ -45,7 +45,7 @@ export function DeveloperPage(): VNode { const response = useAsyncAsHook(async () => { const op = await wxApi.getPendingOperations(); const c = await wxApi.dumpCoins(); - const ex = await wxApi.listExchanges(); + const ex = await wxApi.listExchangesDetailled(); return { operations: op.pendingOperations, coins: c.coins, diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx index 859a7f86b..8ba5fb7e9 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeAddPage.tsx @@ -36,7 +36,7 @@ export function ExchangeAddPage({ currency, onBack }: Props): VNode { { url: string; config: TalerConfigResponse } | undefined >(undefined); - const knownExchangesResponse = useAsyncAsHook(wxApi.listExchanges); + const knownExchangesResponse = useAsyncAsHook(wxApi.listExchangesDetailled); const knownExchanges = !knownExchangesResponse ? [] : knownExchangesResponse.hasError diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/example.ts b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/example.ts index 2fbe40407..bdbd36596 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/example.ts +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/example.ts @@ -14,14 +14,17 @@ GNU Taler; see the file COPYING. If not, see */ -import { ExchangeListItem } from "@gnu-taler/taler-util"; +import { + ExchangeFullDetailsListItem, + ExchangeListItem, +} from "@gnu-taler/taler-util"; /** * * @author Sebastian Javier Marchano (sebasjm) */ -export const bitcoinExchanges: ExchangeListItem[] = [ +export const bitcoinExchanges: ExchangeFullDetailsListItem[] = [ { exchangeBaseUrl: "https://bitcoin1.ice.bfh.ch/", currency: "BITCOINBTC", @@ -11778,7 +11781,7 @@ export const bitcoinExchanges: ExchangeListItem[] = [ }, ] as any; -export const kudosExchanges: ExchangeListItem[] = [ +export const kudosExchanges: ExchangeFullDetailsListItem[] = [ { exchangeBaseUrl: "https://exchange1.demo.taler.net/", currency: "KUDOS", diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts index 37fdc8f5e..0dec00b09 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/index.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see */ -import { AbsoluteTime, AmountJson, ExchangeListItem } from "@gnu-taler/taler-util"; +import { AbsoluteTime, AmountJson, ExchangeFullDetailsListItem } from "@gnu-taler/taler-util"; import { Loading } from "../../components/Loading.js"; import { HookError } from "../../hooks/useAsyncAsHook.js"; import { ButtonHandler, SelectFieldHandler } from "../../mui/handlers.js"; @@ -52,7 +52,7 @@ export namespace State { export interface BaseInfo { exchanges: SelectFieldHandler; - selected: ExchangeListItem; + selected: ExchangeFullDetailsListItem; nextFeeUpdate: AbsoluteTime; error: undefined; } diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts index 70e8487d8..eda755e96 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/state.ts @@ -25,7 +25,7 @@ export function useComponentState( { onCancel, onSelection, currency }: Props, api: typeof wxApi, ): State { - const hook = useAsyncAsHook(api.listExchanges); + const hook = useAsyncAsHook(api.listExchangesDetailled); const initialValue = 0 const [value, setValue] = useState(String(initialValue)); diff --git a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx index c4e631b45..094087517 100644 --- a/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ExchangeSelection/stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ExchangeListItem } from "@gnu-taler/taler-util"; +import { ExchangeFullDetailsListItem, ExchangeListItem } from "@gnu-taler/taler-util"; import { createExample } from "../../test-utils.js"; import { bitcoinExchanges, kudosExchanges } from "./example.js"; import { FeeDescription, FeeDescriptionPair, OperationMap } from "./index.js"; @@ -34,7 +34,7 @@ export default { }; function timelineForExchange( - ex: ExchangeListItem, + ex: ExchangeFullDetailsListItem, ): OperationMap { return { deposit: createDenominationTimeline( @@ -61,8 +61,8 @@ function timelineForExchange( } function timelinePairForExchange( - ex1: ExchangeListItem, - ex2: ExchangeListItem, + ex1: ExchangeFullDetailsListItem, + ex2: ExchangeFullDetailsListItem, ): OperationMap { const om1 = timelineForExchange(ex1); const om2 = timelineForExchange(ex2); diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx index a292914fb..5a82c96ed 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx @@ -50,7 +50,7 @@ export function ManualWithdrawPage({ amount, onCancel }: Props): VNode { >(undefined); const [error, setError] = useState(undefined); - const state = useAsyncAsHook(wxApi.listExchanges); + const state = useAsyncAsHook(wxApi.listExchangesDetailled); useEffect(() => { return wxApi.onUpdateNotification([NotificationType.ExchangeAdded], () => { state?.retry(); diff --git a/packages/taler-wallet-webextension/src/wallet/Settings.tsx b/packages/taler-wallet-webextension/src/wallet/Settings.tsx index 4a520c3bb..36a356fba 100644 --- a/packages/taler-wallet-webextension/src/wallet/Settings.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Settings.tsx @@ -49,7 +49,7 @@ export function SettingsPage(): VNode { const webex = platform.getWalletWebExVersion(); const exchangesHook = useAsyncAsHook(async () => { - const list = await wxApi.listExchanges(); + const list = await wxApi.listExchangesDetailled(); const version = await wxApi.getVersion(); return { exchanges: list.exchanges, version }; }); diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 074dbbfb0..1b26ea214 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -67,6 +67,7 @@ import { WalletDiagnostics, WalletCoreVersion, WithdrawUriInfoResponse, + ExchangeDetailledListRespose, } from "@gnu-taler/taler-util"; import { AddBackupProviderRequest, @@ -78,7 +79,11 @@ import { } from "@gnu-taler/taler-wallet-core"; import type { DepositGroupFees } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; import type { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw"; -import { platform, MessageFromBackend, WalletWebExVersion } from "./platform/api.js"; +import { + platform, + MessageFromBackend, + WalletWebExVersion, +} from "./platform/api.js"; /** * @@ -202,8 +207,9 @@ export function getBalance(): Promise { return callBackend("getBalances", {}); } - -export function getContractTermsDetails(proposalId: string): Promise { +export function getContractTermsDetails( + proposalId: string, +): Promise { return callBackend("getContractTermsDetails", { proposalId }); } @@ -250,9 +256,15 @@ export function listKnownCurrencies(): Promise { export function listExchanges(): Promise { return callBackend("listExchanges", {}); } + +export function listExchangesDetailled(): Promise { + return callBackend("listExchangesDetailled", {}); +} + export function getVersion(): Promise { return callBackend("getVersion", {}); } + export function listKnownBankAccounts( currency?: string, ): Promise { @@ -487,23 +499,33 @@ export function onUpdateNotification( return platform.listenToWalletBackground(onNewMessage); } -export function initiatePeerPushPayment(req: InitiatePeerPushPaymentRequest): Promise { +export function initiatePeerPushPayment( + req: InitiatePeerPushPaymentRequest, +): Promise { return callBackend("initiatePeerPushPayment", req); } -export function checkPeerPushPayment(req: CheckPeerPushPaymentRequest): Promise { +export function checkPeerPushPayment( + req: CheckPeerPushPaymentRequest, +): Promise { return callBackend("checkPeerPushPayment", req); } -export function acceptPeerPushPayment(req: AcceptPeerPushPaymentRequest): Promise { +export function acceptPeerPushPayment( + req: AcceptPeerPushPaymentRequest, +): Promise { return callBackend("acceptPeerPushPayment", req); } -export function initiatePeerPullPayment(req: InitiatePeerPullPaymentRequest): Promise { +export function initiatePeerPullPayment( + req: InitiatePeerPullPaymentRequest, +): Promise { return callBackend("initiatePeerPullPayment", req); } -export function checkPeerPullPayment(req: CheckPeerPullPaymentRequest): Promise { +export function checkPeerPullPayment( + req: CheckPeerPullPaymentRequest, +): Promise { return callBackend("checkPeerPullPayment", req); } -export function acceptPeerPullPayment(req: AcceptPeerPullPaymentRequest): Promise { +export function acceptPeerPullPayment( + req: AcceptPeerPullPaymentRequest, +): Promise { return callBackend("acceptPeerPullPayment", req); } - -