diff options
Diffstat (limited to 'packages/taler-util/src/wallet-types.ts')
-rw-r--r-- | packages/taler-util/src/wallet-types.ts | 3356 |
1 files changed, 3356 insertions, 0 deletions
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts new file mode 100644 index 000000000..a52b3e6ba --- /dev/null +++ b/packages/taler-util/src/wallet-types.ts @@ -0,0 +1,3356 @@ +/* + This file is part of GNU Taler + (C) 2015-2020 Taler Systems SA + + TALER is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Types used by clients of the wallet. + * + * These types are defined in a separate file make tree shaking easier, since + * some components use these types (via RPC) but do not depend on the wallet + * code directly. + * + * @author Florian Dold <dold@taler.net> + */ + +/** + * Imports. + */ +import { AmountJson, codecForAmountString } from "./amounts.js"; +import { BackupRecovery } from "./backup-types.js"; +import { + Codec, + Context, + DecodingError, + buildCodecForObject, + buildCodecForUnion, + codecForAny, + codecForBoolean, + codecForConstString, + codecForEither, + codecForList, + codecForMap, + codecForNumber, + codecForString, + codecOptional, + renderContext, +} from "./codec.js"; +import { + CurrencySpecification, + TemplateParams, + WithdrawalOperationStatus, +} from "./index.js"; +import { VersionMatchResult } from "./libtool-version.js"; +import { PaytoUri } from "./payto.js"; +import { AgeCommitmentProof } from "./taler-crypto.js"; +import { TalerErrorCode } from "./taler-error-codes.js"; +import { + AccountRestriction, + AmountString, + AuditorDenomSig, + CoinEnvelope, + DenomKeyType, + DenominationPubKey, + EddsaPrivateKeyString, + ExchangeAuditor, + ExchangeWireAccount, + InternationalizedString, + MerchantContractTerms, + MerchantInfo, + PeerContractTerms, + UnblindedSignature, + codecForExchangeWireAccount, + codecForMerchantContractTerms, + codecForPeerContractTerms, +} from "./taler-types.js"; +import { + AbsoluteTime, + TalerPreciseTimestamp, + TalerProtocolDuration, + TalerProtocolTimestamp, + codecForAbsoluteTime, + codecForPreciseTimestamp, + codecForTimestamp, +} from "./time.js"; +import { + OrderShortInfo, + TransactionState, + TransactionType, +} from "./transactions-types.js"; + +/** + * Identifier for a transaction in the wallet. + */ +declare const __txId: unique symbol; +export type TransactionIdStr = `txn:${string}:${string}` & { [__txId]: true }; + +/** + * Identifier for a pending task in the wallet. + */ +declare const __pndId: unique symbol; +export type PendingIdStr = `pnd:${string}:${string}` & { [__pndId]: true }; + +declare const __tmbId: unique symbol; +export type TombstoneIdStr = `tmb:${string}:${string}` & { [__tmbId]: true }; + +function codecForTransactionIdStr(): Codec<TransactionIdStr> { + return { + decode(x: any, c?: Context): TransactionIdStr { + if (typeof x === "string" && x.startsWith("txn:")) { + return x as TransactionIdStr; + } + throw new DecodingError( + `expected string starting with "txn:" at ${renderContext( + c, + )} but got ${x}`, + ); + }, + }; +} + +function codecForPendingIdStr(): Codec<PendingIdStr> { + return { + decode(x: any, c?: Context): PendingIdStr { + if (typeof x === "string" && x.startsWith("txn:")) { + return x as PendingIdStr; + } + throw new DecodingError( + `expected string starting with "txn:" at ${renderContext( + c, + )} but got ${x}`, + ); + }, + }; +} + +function codecForTombstoneIdStr(): Codec<TombstoneIdStr> { + return { + decode(x: any, c?: Context): TombstoneIdStr { + if (typeof x === "string" && x.startsWith("tmb:")) { + return x as TombstoneIdStr; + } + throw new DecodingError( + `expected string starting with "tmb:" at ${renderContext( + c, + )} but got ${x}`, + ); + }, + }; +} + +/** + * Response for the create reserve request to the wallet. + */ +export class CreateReserveResponse { + /** + * Exchange URL where the bank should create the reserve. + * The URL is canonicalized in the response. + */ + exchange: string; + + /** + * Reserve public key of the newly created reserve. + */ + reservePub: string; +} + +export interface GetBalanceDetailRequest { + currency: string; +} + +export const codecForGetBalanceDetailRequest = + (): Codec<GetBalanceDetailRequest> => + buildCodecForObject<GetBalanceDetailRequest>() + .property("currency", codecForString()) + .build("GetBalanceDetailRequest"); + +/** + * How the amount should be interpreted in a transaction + * Effective = how the balance is change + * Raw = effective amount without fee + * + * Depending on the transaction, raw can be higher than effective + */ +export enum TransactionAmountMode { + Effective = "effective", + Raw = "raw", +} + +export type GetPlanForOperationRequest = + | GetPlanForWithdrawRequest + | GetPlanForDepositRequest; +// | GetPlanForPushDebitRequest +// | GetPlanForPullCreditRequest +// | GetPlanForPaymentRequest +// | GetPlanForTipRequest +// | GetPlanForRefundRequest +// | GetPlanForPullDebitRequest +// | GetPlanForPushCreditRequest; + +interface GetPlanForWalletInitiatedOperation { + instructedAmount: AmountString; + mode: TransactionAmountMode; +} + +export interface ConvertAmountRequest { + amount: AmountString; + type: TransactionAmountMode; +} + +export const codecForConvertAmountRequest = + buildCodecForObject<ConvertAmountRequest>() + .property("amount", codecForAmountString()) + .property( + "type", + codecForEither( + codecForConstString(TransactionAmountMode.Raw), + codecForConstString(TransactionAmountMode.Effective), + ), + ) + .build("ConvertAmountRequest"); + +export interface GetAmountRequest { + currency: string; +} + +export const codecForGetAmountRequest = buildCodecForObject<GetAmountRequest>() + .property("currency", codecForString()) + .build("GetAmountRequest"); + +interface GetPlanToCompleteOperation { + instructedAmount: AmountString; +} + +const codecForGetPlanForWalletInitiatedOperation = < + T extends GetPlanForWalletInitiatedOperation, +>() => + buildCodecForObject<T>() + .property( + "mode", + codecForEither( + codecForConstString(TransactionAmountMode.Raw), + codecForConstString(TransactionAmountMode.Effective), + ), + ) + .property("instructedAmount", codecForAmountString()); + +interface GetPlanForWithdrawRequest extends GetPlanForWalletInitiatedOperation { + type: TransactionType.Withdrawal; + exchangeUrl?: string; +} +interface GetPlanForDepositRequest extends GetPlanForWalletInitiatedOperation { + type: TransactionType.Deposit; + account: string; //payto string +} +interface GetPlanForPushDebitRequest + extends GetPlanForWalletInitiatedOperation { + type: TransactionType.PeerPushDebit; +} + +interface GetPlanForPullCreditRequest + extends GetPlanForWalletInitiatedOperation { + type: TransactionType.PeerPullCredit; + exchangeUrl: string; +} + +const codecForGetPlanForWithdrawRequest = + codecForGetPlanForWalletInitiatedOperation<GetPlanForWithdrawRequest>() + .property("type", codecForConstString(TransactionType.Withdrawal)) + .property("exchangeUrl", codecOptional(codecForString())) + .build("GetPlanForWithdrawRequest"); + +const codecForGetPlanForDepositRequest = + codecForGetPlanForWalletInitiatedOperation<GetPlanForDepositRequest>() + .property("type", codecForConstString(TransactionType.Deposit)) + .property("account", codecForString()) + .build("GetPlanForDepositRequest"); + +const codecForGetPlanForPushDebitRequest = + codecForGetPlanForWalletInitiatedOperation<GetPlanForPushDebitRequest>() + .property("type", codecForConstString(TransactionType.PeerPushDebit)) + .build("GetPlanForPushDebitRequest"); + +const codecForGetPlanForPullCreditRequest = + codecForGetPlanForWalletInitiatedOperation<GetPlanForPullCreditRequest>() + .property("type", codecForConstString(TransactionType.PeerPullCredit)) + .property("exchangeUrl", codecForString()) + .build("GetPlanForPullCreditRequest"); + +interface GetPlanForPaymentRequest extends GetPlanToCompleteOperation { + type: TransactionType.Payment; + wireMethod: string; + ageRestriction: number; + maxDepositFee: AmountString; +} + +// interface GetPlanForTipRequest extends GetPlanForOperationBase { +// type: TransactionType.Tip; +// } +// interface GetPlanForRefundRequest extends GetPlanForOperationBase { +// type: TransactionType.Refund; +// } +interface GetPlanForPullDebitRequest extends GetPlanToCompleteOperation { + type: TransactionType.PeerPullDebit; +} +interface GetPlanForPushCreditRequest extends GetPlanToCompleteOperation { + type: TransactionType.PeerPushCredit; +} + +const codecForGetPlanForPaymentRequest = + buildCodecForObject<GetPlanForPaymentRequest>() + .property("type", codecForConstString(TransactionType.Payment)) + .property("maxDepositFee", codecForAmountString()) + .build("GetPlanForPaymentRequest"); + +const codecForGetPlanForPullDebitRequest = + buildCodecForObject<GetPlanForPullDebitRequest>() + .property("type", codecForConstString(TransactionType.PeerPullDebit)) + .build("GetPlanForPullDebitRequest"); + +const codecForGetPlanForPushCreditRequest = + buildCodecForObject<GetPlanForPushCreditRequest>() + .property("type", codecForConstString(TransactionType.PeerPushCredit)) + .build("GetPlanForPushCreditRequest"); + +export const codecForGetPlanForOperationRequest = + (): Codec<GetPlanForOperationRequest> => + buildCodecForUnion<GetPlanForOperationRequest>() + .discriminateOn("type") + .alternative( + TransactionType.Withdrawal, + codecForGetPlanForWithdrawRequest, + ) + .alternative(TransactionType.Deposit, codecForGetPlanForDepositRequest) + // .alternative( + // TransactionType.PeerPushDebit, + // codecForGetPlanForPushDebitRequest, + // ) + // .alternative( + // TransactionType.PeerPullCredit, + // codecForGetPlanForPullCreditRequest, + // ) + // .alternative(TransactionType.Payment, codecForGetPlanForPaymentRequest) + // .alternative( + // TransactionType.PeerPullDebit, + // codecForGetPlanForPullDebitRequest, + // ) + // .alternative( + // TransactionType.PeerPushCredit, + // codecForGetPlanForPushCreditRequest, + // ) + .build("GetPlanForOperationRequest"); + +export interface GetPlanForOperationResponse { + effectiveAmount: AmountString; + rawAmount: AmountString; + counterPartyAmount?: AmountString; + details: any; +} + +export const codecForGetPlanForOperationResponse = + (): Codec<GetPlanForOperationResponse> => + buildCodecForObject<GetPlanForOperationResponse>() + .property("effectiveAmount", codecForAmountString()) + .property("rawAmount", codecForAmountString()) + .property("details", codecForAny()) + .property("counterPartyAmount", codecOptional(codecForAmountString())) + .build("GetPlanForOperationResponse"); + +export interface AmountResponse { + effectiveAmount: AmountString; + rawAmount: AmountString; +} + +export const codecForAmountResponse = (): Codec<AmountResponse> => + buildCodecForObject<AmountResponse>() + .property("effectiveAmount", codecForAmountString()) + .property("rawAmount", codecForAmountString()) + .build("AmountResponse"); + +export enum BalanceFlag { + IncomingKyc = "incoming-kyc", + IncomingAml = "incoming-aml", + IncomingConfirmation = "incoming-confirmation", + OutgoingKyc = "outgoing-kyc", +} + +export interface WalletBalance { + scopeInfo: ScopeInfo; + available: AmountString; + pendingIncoming: AmountString; + pendingOutgoing: AmountString; + + /** + * Does the balance for this currency have a pending + * transaction? + * + * @deprecated use flags and pendingIncoming/pendingOutgoing instead + */ + hasPendingTransactions: boolean; + + /** + * Is there a transaction that requires user input? + * + * @deprecated use flags instead + */ + requiresUserInput: boolean; + + flags: BalanceFlag[]; +} + +export const codecForScopeInfoGlobal = (): Codec<ScopeInfoGlobal> => + buildCodecForObject<ScopeInfoGlobal>() + .property("currency", codecForString()) + .property("type", codecForConstString(ScopeType.Global)) + .build("ScopeInfoGlobal"); + +export const codecForScopeInfoExchange = (): Codec<ScopeInfoExchange> => + buildCodecForObject<ScopeInfoExchange>() + .property("currency", codecForString()) + .property("type", codecForConstString(ScopeType.Exchange)) + .property("url", codecForString()) + .build("ScopeInfoExchange"); + +export const codecForScopeInfoAuditor = (): Codec<ScopeInfoAuditor> => + buildCodecForObject<ScopeInfoAuditor>() + .property("currency", codecForString()) + .property("type", codecForConstString(ScopeType.Auditor)) + .property("url", codecForString()) + .build("ScopeInfoAuditor"); + +export const codecForScopeInfo = (): Codec<ScopeInfo> => + buildCodecForUnion<ScopeInfo>() + .discriminateOn("type") + .alternative(ScopeType.Global, codecForScopeInfoGlobal()) + .alternative(ScopeType.Exchange, codecForScopeInfoExchange()) + .alternative(ScopeType.Auditor, codecForScopeInfoAuditor()) + .build("ScopeInfo"); + +export interface GetCurrencySpecificationRequest { + scope: ScopeInfo; +} + +export const codecForGetCurrencyInfoRequest = + (): Codec<GetCurrencySpecificationRequest> => + buildCodecForObject<GetCurrencySpecificationRequest>() + .property("scope", codecForScopeInfo()) + .build("GetCurrencySpecificationRequest"); + +export interface ListExchangesForScopedCurrencyRequest { + scope: ScopeInfo; +} + +export const codecForListExchangesForScopedCurrencyRequest = + (): Codec<ListExchangesForScopedCurrencyRequest> => + buildCodecForObject<ListExchangesForScopedCurrencyRequest>() + .property("scope", codecForScopeInfo()) + .build("ListExchangesForScopedCurrencyRequest"); + +export interface GetCurrencySpecificationResponse { + currencySpecification: CurrencySpecification; +} + +export interface BuiltinExchange { + exchangeBaseUrl: string; + currencyHint: string; +} + +export interface PartialWalletRunConfig { + builtin?: Partial<WalletRunConfig["builtin"]>; + testing?: Partial<WalletRunConfig["testing"]>; + features?: Partial<WalletRunConfig["features"]>; +} + +export interface WalletRunConfig { + /** + * Initialization values useful for a complete startup. + * + * These are values may be overridden by different wallets + */ + builtin: { + exchanges: BuiltinExchange[]; + }; + + /** + * Unsafe options which it should only be used to create + * testing environment. + */ + testing: { + /** + * Allow withdrawal of denominations even though they are about to expire. + */ + denomselAllowLate: boolean; + devModeActive: boolean; + insecureTrustExchange: boolean; + preventThrottling: boolean; + skipDefaults: boolean; + emitObservabilityEvents?: boolean; + }; + + /** + * Configurations values that may be safe to show to the user + */ + features: { + allowHttp: boolean; + }; +} + +export interface InitRequest { + config?: PartialWalletRunConfig; +} + +export const codecForInitRequest = (): Codec<InitRequest> => + buildCodecForObject<InitRequest>() + .property("config", codecForAny()) + .build("InitRequest"); + +export interface InitResponse { + versionInfo: WalletCoreVersion; +} + +export enum ScopeType { + Global = "global", + Exchange = "exchange", + Auditor = "auditor", +} + +export type ScopeInfoGlobal = { type: ScopeType.Global; currency: string }; +export type ScopeInfoExchange = { + type: ScopeType.Exchange; + currency: string; + url: string; +}; +export type ScopeInfoAuditor = { + type: ScopeType.Auditor; + currency: string; + url: string; +}; + +export type ScopeInfo = ScopeInfoGlobal | ScopeInfoExchange | ScopeInfoAuditor; + +export interface BalancesResponse { + balances: WalletBalance[]; +} + +export const codecForBalance = (): Codec<WalletBalance> => + buildCodecForObject<WalletBalance>() + .property("scopeInfo", codecForAny()) // FIXME + .property("available", codecForAmountString()) + .property("hasPendingTransactions", codecForBoolean()) + .property("pendingIncoming", codecForAmountString()) + .property("pendingOutgoing", codecForAmountString()) + .property("requiresUserInput", codecForBoolean()) + .property("flags", codecForAny()) // FIXME + .build("Balance"); + +export const codecForBalancesResponse = (): Codec<BalancesResponse> => + buildCodecForObject<BalancesResponse>() + .property("balances", codecForList(codecForBalance())) + .build("BalancesResponse"); + +/** + * For terseness. + */ +export function mkAmount( + value: number, + fraction: number, + currency: string, +): AmountJson { + return { value, fraction, currency }; +} + +/** + * Status of a coin. + */ +export enum CoinStatus { + /** + * Withdrawn and never shown to anybody. + */ + Fresh = "fresh", + + /** + * Coin was lost as the denomination is not usable anymore. + */ + DenomLoss = "denom-loss", + + /** + * Fresh, but currently marked as "suspended", thus won't be used + * for spending. Used for testing. + */ + FreshSuspended = "fresh-suspended", + + /** + * A coin that has been spent and refreshed. + */ + Dormant = "dormant", +} + +/** + * Easy to process format for the public data of coins + * managed by the wallet. + */ +export interface CoinDumpJson { + coins: Array<{ + /** + * The coin's denomination's public key. + */ + denom_pub: DenominationPubKey; + /** + * Hash of denom_pub. + */ + denom_pub_hash: string; + /** + * Value of the denomination (without any fees). + */ + denom_value: string; + /** + * Public key of the coin. + */ + coin_pub: string; + /** + * Base URL of the exchange for the coin. + */ + exchange_base_url: string; + /** + * Public key of the parent coin. + * Only present if this coin was obtained via refreshing. + */ + refresh_parent_coin_pub: string | undefined; + /** + * Public key of the reserve for this coin. + * Only present if this coin was obtained via refreshing. + */ + withdrawal_reserve_pub: string | undefined; + coin_status: CoinStatus; + spend_allocation: + | { + id: string; + amount: AmountString; + } + | undefined; + /** + * Information about the age restriction + */ + ageCommitmentProof: AgeCommitmentProof | undefined; + }>; +} + +export enum ConfirmPayResultType { + Done = "done", + Pending = "pending", +} + +/** + * Result for confirmPay + */ +export interface ConfirmPayResultDone { + type: ConfirmPayResultType.Done; + contractTerms: MerchantContractTerms; + transactionId: TransactionIdStr; +} + +export interface ConfirmPayResultPending { + type: ConfirmPayResultType.Pending; + transactionId: TransactionIdStr; + lastError: TalerErrorDetail | undefined; +} + +export const codecForTalerErrorDetail = (): Codec<TalerErrorDetail> => + buildCodecForObject<TalerErrorDetail>() + .property("code", codecForNumber()) + .property("when", codecOptional(codecForAbsoluteTime)) + .property("hint", codecOptional(codecForString())) + .build("TalerErrorDetail"); + +export type ConfirmPayResult = ConfirmPayResultDone | ConfirmPayResultPending; + +export const codecForConfirmPayResultPending = + (): Codec<ConfirmPayResultPending> => + buildCodecForObject<ConfirmPayResultPending>() + .property("lastError", codecOptional(codecForTalerErrorDetail())) + .property("transactionId", codecForTransactionIdStr()) + .property("type", codecForConstString(ConfirmPayResultType.Pending)) + .build("ConfirmPayResultPending"); + +export const codecForConfirmPayResultDone = (): Codec<ConfirmPayResultDone> => + buildCodecForObject<ConfirmPayResultDone>() + .property("type", codecForConstString(ConfirmPayResultType.Done)) + .property("transactionId", codecForTransactionIdStr()) + .property("contractTerms", codecForMerchantContractTerms()) + .build("ConfirmPayResultDone"); + +export const codecForConfirmPayResult = (): Codec<ConfirmPayResult> => + buildCodecForUnion<ConfirmPayResult>() + .discriminateOn("type") + .alternative( + ConfirmPayResultType.Pending, + codecForConfirmPayResultPending(), + ) + .alternative(ConfirmPayResultType.Done, codecForConfirmPayResultDone()) + .build("ConfirmPayResult"); + +/** + * Information about all sender wire details known to the wallet, + * as well as exchanges that accept these wire types. + */ +export interface SenderWireInfos { + /** + * Mapping from exchange base url to list of accepted + * wire types. + */ + exchangeWireTypes: { [exchangeBaseUrl: string]: string[] }; + + /** + * Sender wire information stored in the wallet. + */ + senderWires: string[]; +} + +/** + * Request to mark a reserve as confirmed. + */ +export interface ConfirmReserveRequest { + /** + * Public key of then reserve that should be marked + * as confirmed. + */ + reservePub: string; +} + +export const codecForConfirmReserveRequest = (): Codec<ConfirmReserveRequest> => + buildCodecForObject<ConfirmReserveRequest>() + .property("reservePub", codecForString()) + .build("ConfirmReserveRequest"); + +export interface PrepareRefundResult { + proposalId: string; + + effectivePaid: AmountString; + gone: AmountString; + granted: AmountString; + pending: boolean; + awaiting: AmountString; + + info: OrderShortInfo; +} + +export interface PrepareTipResult { + /** + * Unique ID for the tip assigned by the wallet. + * Typically different from the merchant-generated tip ID. + * + * @deprecated use transactionId instead + */ + walletRewardId: string; + + /** + * Tip transaction ID. + */ + transactionId: TransactionIdStr; + + /** + * Has the tip already been accepted? + */ + accepted: boolean; + + /** + * Amount that the merchant gave. + */ + rewardAmountRaw: AmountString; + + /** + * Amount that arrived at the wallet. + * Might be lower than the raw amount due to fees. + */ + rewardAmountEffective: AmountString; + + /** + * Base URL of the merchant backend giving then tip. + */ + merchantBaseUrl: string; + + /** + * Base URL of the exchange that is used to withdraw the tip. + * Determined by the merchant, the wallet/user has no choice here. + */ + exchangeBaseUrl: string; + + /** + * Time when the tip will expire. After it expired, it can't be picked + * up anymore. + */ + expirationTimestamp: TalerProtocolTimestamp; +} + +export interface AcceptTipResponse { + transactionId: TransactionIdStr; + next_url?: string; +} + +export const codecForPrepareTipResult = (): Codec<PrepareTipResult> => + buildCodecForObject<PrepareTipResult>() + .property("accepted", codecForBoolean()) + .property("rewardAmountRaw", codecForAmountString()) + .property("rewardAmountEffective", codecForAmountString()) + .property("exchangeBaseUrl", codecForString()) + .property("merchantBaseUrl", codecForString()) + .property("expirationTimestamp", codecForTimestamp) + .property("walletRewardId", codecForString()) + .property("transactionId", codecForTransactionIdStr()) + .build("PrepareRewardResult"); + +export interface BenchmarkResult { + time: { [s: string]: number }; + repetitions: number; +} + +export enum PreparePayResultType { + PaymentPossible = "payment-possible", + InsufficientBalance = "insufficient-balance", + AlreadyConfirmed = "already-confirmed", +} + +export const codecForPreparePayResultPaymentPossible = + (): Codec<PreparePayResultPaymentPossible> => + buildCodecForObject<PreparePayResultPaymentPossible>() + .property("amountEffective", codecForAmountString()) + .property("amountRaw", codecForAmountString()) + .property("contractTerms", codecForMerchantContractTerms()) + .property("transactionId", codecForTransactionIdStr()) + .property("proposalId", codecForString()) + .property("contractTermsHash", codecForString()) + .property("talerUri", codecForString()) + .property( + "status", + codecForConstString(PreparePayResultType.PaymentPossible), + ) + .build("PreparePayResultPaymentPossible"); + +export interface BalanceDetails {} + +/** + * Detailed reason for why the wallet's balance is insufficient. + */ +export interface PaymentInsufficientBalanceDetails { + /** + * Amount requested by the merchant. + */ + amountRequested: AmountString; + + /** + * Balance of type "available" (see balance.ts for definition). + */ + balanceAvailable: AmountString; + + /** + * Balance of type "material" (see balance.ts for definition). + */ + balanceMaterial: AmountString; + + /** + * Balance of type "age-acceptable" (see balance.ts for definition). + */ + balanceAgeAcceptable: AmountString; + + /** + * Balance of type "merchant-acceptable" (see balance.ts for definition). + */ + balanceReceiverAcceptable: AmountString; + + /** + * Balance of type "merchant-depositable" (see balance.ts for definition). + */ + balanceReceiverDepositable: AmountString; + + balanceExchangeDepositable: AmountString; + + /** + * Maximum effective amount that the wallet can spend, + * when all fees are paid by the wallet. + */ + maxEffectiveSpendAmount: AmountString; + + perExchange: { + [url: string]: { + balanceAvailable: AmountString; + balanceMaterial: AmountString; + balanceExchangeDepositable: AmountString; + balanceAgeAcceptable: AmountString; + balanceReceiverAcceptable: AmountString; + balanceReceiverDepositable: AmountString; + maxEffectiveSpendAmount: AmountString; + /** + * Exchange doesn't have global fees configured for the relevant year, + * p2p payments aren't possible. + */ + missingGlobalFees: boolean; + }; + }; +} + +export const codecForPayMerchantInsufficientBalanceDetails = + (): Codec<PaymentInsufficientBalanceDetails> => + buildCodecForObject<PaymentInsufficientBalanceDetails>() + .property("amountRequested", codecForAmountString()) + .property("balanceAgeAcceptable", codecForAmountString()) + .property("balanceAvailable", codecForAmountString()) + .property("balanceMaterial", codecForAmountString()) + .property("balanceReceiverAcceptable", codecForAmountString()) + .property("balanceReceiverDepositable", codecForAmountString()) + .property("balanceExchangeDepositable", codecForAmountString()) + .property("perExchange", codecForAny()) + .property("maxEffectiveSpendAmount", codecForAmountString()) + .build("PayMerchantInsufficientBalanceDetails"); + +export const codecForPreparePayResultInsufficientBalance = + (): Codec<PreparePayResultInsufficientBalance> => + buildCodecForObject<PreparePayResultInsufficientBalance>() + .property("amountRaw", codecForAmountString()) + .property("contractTerms", codecForAny()) + .property("talerUri", codecForString()) + .property("proposalId", codecForString()) + .property("transactionId", codecForTransactionIdStr()) + .property( + "status", + codecForConstString(PreparePayResultType.InsufficientBalance), + ) + .property( + "balanceDetails", + codecForPayMerchantInsufficientBalanceDetails(), + ) + .build("PreparePayResultInsufficientBalance"); + +export const codecForPreparePayResultAlreadyConfirmed = + (): Codec<PreparePayResultAlreadyConfirmed> => + buildCodecForObject<PreparePayResultAlreadyConfirmed>() + .property( + "status", + codecForConstString(PreparePayResultType.AlreadyConfirmed), + ) + .property("amountEffective", codecOptional(codecForAmountString())) + .property("amountRaw", codecForAmountString()) + .property("paid", codecForBoolean()) + .property("talerUri", codecForString()) + .property("contractTerms", codecForAny()) + .property("contractTermsHash", codecForString()) + .property("transactionId", codecForTransactionIdStr()) + .property("proposalId", codecForString()) + .build("PreparePayResultAlreadyConfirmed"); + +export const codecForPreparePayResult = (): Codec<PreparePayResult> => + buildCodecForUnion<PreparePayResult>() + .discriminateOn("status") + .alternative( + PreparePayResultType.AlreadyConfirmed, + codecForPreparePayResultAlreadyConfirmed(), + ) + .alternative( + PreparePayResultType.InsufficientBalance, + codecForPreparePayResultInsufficientBalance(), + ) + .alternative( + PreparePayResultType.PaymentPossible, + codecForPreparePayResultPaymentPossible(), + ) + .build("PreparePayResult"); + +/** + * Result of a prepare pay operation. + */ +export type PreparePayResult = + | PreparePayResultInsufficientBalance + | PreparePayResultAlreadyConfirmed + | PreparePayResultPaymentPossible; + +/** + * Payment is possible. + */ +export interface PreparePayResultPaymentPossible { + status: PreparePayResultType.PaymentPossible; + transactionId: TransactionIdStr; + /** + * @deprecated use transactionId instead + */ + proposalId: string; + contractTerms: MerchantContractTerms; + contractTermsHash: string; + amountRaw: AmountString; + amountEffective: AmountString; + talerUri: string; +} + +export interface PreparePayResultInsufficientBalance { + status: PreparePayResultType.InsufficientBalance; + transactionId: TransactionIdStr; + /** + * @deprecated use transactionId + */ + proposalId: string; + contractTerms: MerchantContractTerms; + amountRaw: AmountString; + talerUri: string; + balanceDetails: PaymentInsufficientBalanceDetails; +} + +export interface PreparePayResultAlreadyConfirmed { + status: PreparePayResultType.AlreadyConfirmed; + transactionId: TransactionIdStr; + contractTerms: MerchantContractTerms; + paid: boolean; + amountRaw: AmountString; + amountEffective: AmountString | undefined; + contractTermsHash: string; + /** + * @deprecated use transactionId + */ + proposalId: string; + talerUri: string; +} + +export interface BankWithdrawDetails { + status: WithdrawalOperationStatus; + amount: AmountJson; + senderWire?: string; + suggestedExchange?: string; + confirmTransferUrl?: string; + wireTypes: string[]; + operationId: string; + apiBaseUrl: string; +} + +export interface AcceptWithdrawalResponse { + reservePub: string; + confirmTransferUrl?: string; + transactionId: TransactionIdStr; +} + +/** + * Details about a purchase, including refund status. + */ +export interface PurchaseDetails { + contractTerms: Record<string, undefined>; + hasRefund: boolean; + totalRefundAmount: AmountJson; + totalRefundAndRefreshFees: AmountJson; +} + +export interface WalletDiagnostics { + walletManifestVersion: string; + walletManifestDisplayVersion: string; + errors: string[]; + firefoxIdbProblem: boolean; + dbOutdated: boolean; +} + +export interface TalerErrorDetail { + code: TalerErrorCode; + when?: AbsoluteTime; + hint?: string; + [x: string]: unknown; +} + +/** + * Minimal information needed about a planchet for unblinding a signature. + * + * Can be a withdrawal/tipping/refresh planchet. + */ +export interface PlanchetUnblindInfo { + denomPub: DenominationPubKey; + blindingKey: string; +} + +export interface WithdrawalPlanchet { + coinPub: string; + coinPriv: string; + reservePub: string; + denomPubHash: string; + denomPub: DenominationPubKey; + blindingKey: string; + withdrawSig: string; + coinEv: CoinEnvelope; + coinValue: AmountJson; + coinEvHash: string; + ageCommitmentProof?: AgeCommitmentProof; +} + +export interface PlanchetCreationRequest { + secretSeed: string; + coinIndex: number; + value: AmountJson; + feeWithdraw: AmountJson; + denomPub: DenominationPubKey; + reservePub: string; + reservePriv: string; + restrictAge?: number; +} + +/** + * Reasons for why a coin is being refreshed. + */ +export enum RefreshReason { + Manual = "manual", + PayMerchant = "pay-merchant", + PayDeposit = "pay-deposit", + PayPeerPush = "pay-peer-push", + PayPeerPull = "pay-peer-pull", + Refund = "refund", + AbortPay = "abort-pay", + AbortDeposit = "abort-deposit", + AbortPeerPushDebit = "abort-peer-push-debit", + AbortPeerPullDebit = "abort-peer-pull-debit", + Recoup = "recoup", + BackupRestored = "backup-restored", + Scheduled = "scheduled", +} + +/** + * Request to refresh a single coin. + */ +export interface CoinRefreshRequest { + readonly coinPub: string; + readonly amount: AmountString; +} + +/** + * Private data required to make a deposit permission. + */ +export interface DepositInfo { + exchangeBaseUrl: string; + contractTermsHash: string; + coinPub: string; + coinPriv: string; + spendAmount: AmountJson; + timestamp: TalerProtocolTimestamp; + refundDeadline: TalerProtocolTimestamp; + merchantPub: string; + feeDeposit: AmountJson; + wireInfoHash: string; + denomKeyType: DenomKeyType; + denomPubHash: string; + denomSig: UnblindedSignature; + + requiredMinimumAge?: number; + + ageCommitmentProof?: AgeCommitmentProof; +} + +export interface ExchangesShortListResponse { + exchanges: ShortExchangeListItem[]; +} + +export interface ExchangesListResponse { + exchanges: ExchangeListItem[]; +} + +export interface ExchangeDetailedResponse { + exchange: ExchangeFullDetails; +} + +export interface WalletCoreVersion { + implementationSemver: string; + implementationGitHash: string; + + /** + * Wallet-core protocol version supported by this implementation + * of the API ("server" version). + */ + version: string; + exchange: string; + merchant: string; + + bankIntegrationApiRange: string; + bankConversionApiRange: string; + corebankApiRange: string; + + /** + * @deprecated as bank was split into multiple APIs with separate versioning + */ + bank: string; + + /** + * @deprecated + */ + hash: string | undefined; + + /** + * @deprecated will be removed + */ + devMode: boolean; +} + +export interface KnownBankAccountsInfo { + uri: PaytoUri; + kyc_completed: boolean; + currency: string; + alias: string; +} + +export interface KnownBankAccounts { + accounts: KnownBankAccountsInfo[]; +} + +/** + * Wire fee for one wire method + */ +export interface WireFee { + /** + * Fee for wire transfers. + */ + wireFee: AmountString; + + /** + * Fees to close and refund a reserve. + */ + closingFee: AmountString; + + /** + * Start date of the fee. + */ + startStamp: TalerProtocolTimestamp; + + /** + * End date of the fee. + */ + endStamp: TalerProtocolTimestamp; + + /** + * Signature made by the exchange master key. + */ + sig: string; +} + +export type WireFeeMap = { [wireMethod: string]: WireFee[] }; + +export interface WireInfo { + feesForType: WireFeeMap; + accounts: ExchangeWireAccount[]; +} + +export interface ExchangeGlobalFees { + startDate: TalerProtocolTimestamp; + endDate: TalerProtocolTimestamp; + + historyFee: AmountString; + accountFee: AmountString; + purseFee: AmountString; + + historyTimeout: TalerProtocolDuration; + purseTimeout: TalerProtocolDuration; + + purseLimit: number; + + signature: string; +} + +const codecForWireFee = (): Codec<WireFee> => + buildCodecForObject<WireFee>() + .property("sig", codecForString()) + .property("wireFee", codecForAmountString()) + .property("closingFee", codecForAmountString()) + .property("startStamp", codecForTimestamp) + .property("endStamp", codecForTimestamp) + .build("codecForWireFee"); + +const codecForWireInfo = (): Codec<WireInfo> => + buildCodecForObject<WireInfo>() + .property("feesForType", codecForMap(codecForList(codecForWireFee()))) + .property("accounts", codecForList(codecForExchangeWireAccount())) + .build("codecForWireInfo"); + +export interface DenominationInfo { + /** + * Value of one coin of the denomination. + */ + value: AmountString; + + /** + * Hash of the denomination public key. + * Stored in the database for faster lookups. + */ + denomPubHash: string; + + denomPub: DenominationPubKey; + + /** + * Fee for withdrawing. + */ + feeWithdraw: AmountString; + + /** + * Fee for depositing. + */ + feeDeposit: AmountString; + + /** + * Fee for refreshing. + */ + feeRefresh: AmountString; + + /** + * Fee for refunding. + */ + feeRefund: AmountString; + + /** + * Validity start date of the denomination. + */ + stampStart: TalerProtocolTimestamp; + + /** + * Date after which the currency can't be withdrawn anymore. + */ + stampExpireWithdraw: TalerProtocolTimestamp; + + /** + * Date after the denomination officially doesn't exist anymore. + */ + stampExpireLegal: TalerProtocolTimestamp; + + /** + * Data after which coins of this denomination can't be deposited anymore. + */ + stampExpireDeposit: TalerProtocolTimestamp; + + exchangeBaseUrl: string; +} + +export type DenomOperation = "deposit" | "withdraw" | "refresh" | "refund"; +export type DenomOperationMap<T> = { [op in DenomOperation]: T }; + +export interface FeeDescription { + group: string; + from: AbsoluteTime; + until: AbsoluteTime; + fee?: AmountString; +} + +export interface FeeDescriptionPair { + group: string; + from: AbsoluteTime; + until: AbsoluteTime; + left?: AmountString; + right?: AmountString; +} + +export interface TimePoint<T> { + id: string; + group: string; + fee: AmountString; + type: "start" | "end"; + moment: AbsoluteTime; + denom: T; +} + +export interface ExchangeFullDetails { + exchangeBaseUrl: string; + currency: string; + paytoUris: string[]; + auditors: ExchangeAuditor[]; + wireInfo: WireInfo; + denomFees: DenomOperationMap<FeeDescription[]>; + transferFees: Record<string, FeeDescription[]>; + globalFees: FeeDescription[]; +} + +export enum ExchangeTosStatus { + Pending = "pending", + Proposed = "proposed", + Accepted = "accepted", +} + +export enum ExchangeEntryStatus { + Preset = "preset", + Ephemeral = "ephemeral", + Used = "used", +} + +export enum ExchangeUpdateStatus { + Initial = "initial", + InitialUpdate = "initial-update", + Suspended = "suspended", + UnavailableUpdate = "unavailable-update", + Ready = "ready", + ReadyUpdate = "ready-update", +} + +export interface OperationErrorInfo { + error: TalerErrorDetail; +} + +export interface ShortExchangeListItem { + exchangeBaseUrl: string; +} + +/** + * Info about an exchange entry in the wallet. + */ +export interface ExchangeListItem { + exchangeBaseUrl: string; + masterPub: string | undefined; + currency: string; + paytoUris: string[]; + tosStatus: ExchangeTosStatus; + exchangeEntryStatus: ExchangeEntryStatus; + exchangeUpdateStatus: ExchangeUpdateStatus; + ageRestrictionOptions: number[]; + + /** + * P2P payments are disabled with this exchange + * (e.g. because no global fees are configured). + */ + peerPaymentsDisabled: boolean; + + /** + * Set to true if this exchange doesn't charge any fees. + */ + noFees: boolean; + + scopeInfo: ScopeInfo; + + lastUpdateTimestamp: TalerPreciseTimestamp | undefined; + + /** + * Information about the last error that occurred when trying + * to update the exchange info. + */ + lastUpdateErrorInfo?: OperationErrorInfo; +} + +const codecForAuditorDenomSig = (): Codec<AuditorDenomSig> => + buildCodecForObject<AuditorDenomSig>() + .property("denom_pub_h", codecForString()) + .property("auditor_sig", codecForString()) + .build("AuditorDenomSig"); + +const codecForExchangeAuditor = (): Codec<ExchangeAuditor> => + buildCodecForObject<ExchangeAuditor>() + .property("auditor_pub", codecForString()) + .property("auditor_url", codecForString()) + .property("denomination_keys", codecForList(codecForAuditorDenomSig())) + .build("codecForExchangeAuditor"); + +export const codecForFeeDescriptionPair = (): Codec<FeeDescriptionPair> => + buildCodecForObject<FeeDescriptionPair>() + .property("group", codecForString()) + .property("from", codecForAbsoluteTime) + .property("until", codecForAbsoluteTime) + .property("left", codecOptional(codecForAmountString())) + .property("right", codecOptional(codecForAmountString())) + .build("FeeDescriptionPair"); + +export const codecForFeeDescription = (): Codec<FeeDescription> => + buildCodecForObject<FeeDescription>() + .property("group", codecForString()) + .property("from", codecForAbsoluteTime) + .property("until", codecForAbsoluteTime) + .property("fee", codecOptional(codecForAmountString())) + .build("FeeDescription"); + +export const codecForFeesByOperations = (): Codec< + DenomOperationMap<FeeDescription[]> +> => + buildCodecForObject<DenomOperationMap<FeeDescription[]>>() + .property("deposit", codecForList(codecForFeeDescription())) + .property("withdraw", codecForList(codecForFeeDescription())) + .property("refresh", codecForList(codecForFeeDescription())) + .property("refund", codecForList(codecForFeeDescription())) + .build("DenomOperationMap"); + +export const codecForExchangeFullDetails = (): Codec<ExchangeFullDetails> => + buildCodecForObject<ExchangeFullDetails>() + .property("currency", codecForString()) + .property("exchangeBaseUrl", codecForString()) + .property("paytoUris", codecForList(codecForString())) + .property("auditors", codecForList(codecForExchangeAuditor())) + .property("wireInfo", codecForWireInfo()) + .property("denomFees", codecForFeesByOperations()) + .property( + "transferFees", + codecForMap(codecForList(codecForFeeDescription())), + ) + .property("globalFees", codecForList(codecForFeeDescription())) + .build("ExchangeFullDetails"); + +export const codecForExchangeListItem = (): Codec<ExchangeListItem> => + buildCodecForObject<ExchangeListItem>() + .property("currency", codecForString()) + .property("exchangeBaseUrl", codecForString()) + .property("masterPub", codecOptional(codecForString())) + .property("paytoUris", codecForList(codecForString())) + .property("tosStatus", codecForAny()) + .property("exchangeEntryStatus", codecForAny()) + .property("exchangeUpdateStatus", codecForAny()) + .property("ageRestrictionOptions", codecForList(codecForNumber())) + .property("scopeInfo", codecForScopeInfo()) + .property("lastUpdateErrorInfo", codecForAny()) + .property("lastUpdateTimestamp", codecOptional(codecForPreciseTimestamp)) + .property("noFees", codecForBoolean()) + .property("peerPaymentsDisabled", codecForBoolean()) + .build("ExchangeListItem"); + +export const codecForExchangesListResponse = (): Codec<ExchangesListResponse> => + buildCodecForObject<ExchangesListResponse>() + .property("exchanges", codecForList(codecForExchangeListItem())) + .build("ExchangesListResponse"); + +export interface AcceptManualWithdrawalResult { + /** + * Payto URIs that can be used to fund the withdrawal. + * + * @deprecated in favor of withdrawalAccountsList + */ + exchangePaytoUris: string[]; + + /** + * Public key of the newly created reserve. + */ + reservePub: string; + + withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; + + transactionId: TransactionIdStr; +} + +export interface WithdrawalDetailsForAmount { + /** + * Did the user accept the current version of the exchange's + * terms of service? + * + * @deprecated the client should query the exchange entry instead + */ + tosAccepted: boolean; + + /** + * Amount that the user will transfer to the exchange. + */ + amountRaw: AmountString; + + /** + * Amount that will be added to the user's wallet balance. + */ + amountEffective: AmountString; + + /** + * Number of coins that would be used for withdrawal. + * + * The UIs should warn if this number is too high (roughly at >100). + */ + numCoins: number; + + /** + * Ways to pay the exchange. + * + * @deprecated in favor of withdrawalAccountsList + */ + paytoUris: string[]; + + /** + * Ways to pay the exchange, including accounts that require currency conversion. + */ + withdrawalAccountsList: WithdrawalExchangeAccountDetails[]; + + /** + * If the exchange supports age-restricted coins it will return + * the array of ages. + */ + ageRestrictionOptions?: number[]; + + /** + * Scope info of the currency withdrawn. + */ + scopeInfo: ScopeInfo; +} + +export interface DenomSelItem { + denomPubHash: string; + count: number; + /** + * Number of denoms/planchets to skip, because + * a re-denomination effectively deleted them. + */ + skip?: number; +} + +/** + * Selected denominations withn some extra info. + */ +export interface DenomSelectionState { + totalCoinValue: AmountString; + totalWithdrawCost: AmountString; + selectedDenoms: DenomSelItem[]; + earliestDepositExpiration: TalerProtocolTimestamp; + hasDenomWithAgeRestriction: boolean; +} + +/** + * Information about what will happen doing a withdrawal. + * + * Sent to the wallet frontend to be rendered and shown to the user. + */ +export interface ExchangeWithdrawalDetails { + exchangePaytoUris: string[]; + + /** + * Filtered wire info to send to the bank. + */ + exchangeWireAccounts: string[]; + + exchangeCreditAccountDetails: WithdrawalExchangeAccountDetails[]; + + /** + * Selected denominations for withdraw. + */ + selectedDenoms: DenomSelectionState; + + /** + * Did the user already accept the current terms of service for the exchange? + */ + termsOfServiceAccepted: boolean; + + /** + * The earliest deposit expiration of the selected coins. + */ + earliestDepositExpiration: TalerProtocolTimestamp; + + /** + * Result of checking the wallet's version + * against the exchange's version. + * + * Older exchanges don't return version information. + */ + versionMatch: VersionMatchResult | undefined; + + /** + * Libtool-style version string for the exchange or "unknown" + * for older exchanges. + */ + exchangeVersion: string; + + /** + * Libtool-style version string for the wallet. + */ + walletVersion: string; + + /** + * Amount that will be subtracted from the reserve's balance. + */ + withdrawalAmountRaw: AmountString; + + /** + * Amount that will actually be added to the wallet's balance. + */ + withdrawalAmountEffective: AmountString; + + /** + * If the exchange supports age-restricted coins it will return + * the array of ages. + * + */ + ageRestrictionOptions?: number[]; + + scopeInfo: ScopeInfo; +} + +export interface GetExchangeTosResult { + /** + * Markdown version of the current ToS. + */ + content: string; + + /** + * Version tag of the current ToS. + */ + currentEtag: string; + + /** + * Version tag of the last ToS that the user has accepted, + * if any. + */ + acceptedEtag: string | undefined; + + /** + * Accepted content type + */ + contentType: string; + + /** + * Language of the returned content. + * + * If missing, language is unknown. + */ + contentLanguage: string | undefined; + + /** + * Available languages as advertised by the exchange. + */ + tosAvailableLanguages: string[]; + + tosStatus: ExchangeTosStatus; +} + +export interface TestPayArgs { + merchantBaseUrl: string; + merchantAuthToken?: string; + amount: AmountString; + summary: string; + forcedCoinSel?: ForcedCoinSel; +} + +export const codecForTestPayArgs = (): Codec<TestPayArgs> => + buildCodecForObject<TestPayArgs>() + .property("merchantBaseUrl", codecForString()) + .property("merchantAuthToken", codecOptional(codecForString())) + .property("amount", codecForAmountString()) + .property("summary", codecForString()) + .property("forcedCoinSel", codecForAny()) + .build("TestPayArgs"); + +export interface IntegrationTestArgs { + exchangeBaseUrl: string; + corebankApiBaseUrl: string; + merchantBaseUrl: string; + merchantAuthToken?: string; + amountToWithdraw: AmountString; + amountToSpend: AmountString; +} + +export const codecForIntegrationTestArgs = (): Codec<IntegrationTestArgs> => + buildCodecForObject<IntegrationTestArgs>() + .property("exchangeBaseUrl", codecForString()) + .property("merchantBaseUrl", codecForString()) + .property("merchantAuthToken", codecOptional(codecForString())) + .property("amountToSpend", codecForAmountString()) + .property("amountToWithdraw", codecForAmountString()) + .property("corebankApiBaseUrl", codecForString()) + .build("IntegrationTestArgs"); + +export interface IntegrationTestV2Args { + exchangeBaseUrl: string; + corebankApiBaseUrl: string; + merchantBaseUrl: string; + merchantAuthToken?: string; +} + +export const codecForIntegrationTestV2Args = (): Codec<IntegrationTestV2Args> => + buildCodecForObject<IntegrationTestV2Args>() + .property("exchangeBaseUrl", codecForString()) + .property("merchantBaseUrl", codecForString()) + .property("merchantAuthToken", codecOptional(codecForString())) + .property("corebankApiBaseUrl", codecForString()) + .build("IntegrationTestV2Args"); + +export interface GetExchangeEntryByUrlRequest { + exchangeBaseUrl: string; +} + +export const codecForGetExchangeEntryByUrlRequest = + (): Codec<GetExchangeEntryByUrlRequest> => + buildCodecForObject<GetExchangeEntryByUrlRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("GetExchangeEntryByUrlRequest"); + +export type GetExchangeEntryByUrlResponse = ExchangeListItem; + +export interface AddExchangeRequest { + exchangeBaseUrl: string; + + /** + * @deprecated use a separate API call to start a forced exchange update instead + */ + forceUpdate?: boolean; + + masterPub?: string; +} + +export const codecForAddExchangeRequest = (): Codec<AddExchangeRequest> => + buildCodecForObject<AddExchangeRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("forceUpdate", codecOptional(codecForBoolean())) + .property("masterPub", codecOptional(codecForString())) + .build("AddExchangeRequest"); + +export interface UpdateExchangeEntryRequest { + exchangeBaseUrl: string; + force?: boolean; +} + +export const codecForUpdateExchangeEntryRequest = + (): Codec<UpdateExchangeEntryRequest> => + buildCodecForObject<UpdateExchangeEntryRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("force", codecOptional(codecForBoolean())) + .build("UpdateExchangeEntryRequest"); + +export interface GetExchangeResourcesRequest { + exchangeBaseUrl: string; +} + +export const codecForGetExchangeResourcesRequest = + (): Codec<GetExchangeResourcesRequest> => + buildCodecForObject<GetExchangeResourcesRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("GetExchangeResourcesRequest"); + +export interface GetExchangeResourcesResponse { + hasResources: boolean; +} + +export interface DeleteExchangeRequest { + exchangeBaseUrl: string; + purge?: boolean; +} + +export const codecForDeleteExchangeRequest = (): Codec<DeleteExchangeRequest> => + buildCodecForObject<DeleteExchangeRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("purge", codecOptional(codecForBoolean())) + .build("DeleteExchangeRequest"); + +export interface ForceExchangeUpdateRequest { + exchangeBaseUrl: string; +} + +export const codecForForceExchangeUpdateRequest = + (): Codec<AddExchangeRequest> => + buildCodecForObject<AddExchangeRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("AddExchangeRequest"); + +export interface GetExchangeTosRequest { + exchangeBaseUrl: string; + acceptedFormat?: string[]; + acceptLanguage?: string; +} + +export const codecForGetExchangeTosRequest = (): Codec<GetExchangeTosRequest> => + buildCodecForObject<GetExchangeTosRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("acceptedFormat", codecOptional(codecForList(codecForString()))) + .property("acceptLanguage", codecOptional(codecForString())) + .build("GetExchangeTosRequest"); + +export interface AcceptManualWithdrawalRequest { + exchangeBaseUrl: string; + amount: AmountString; + restrictAge?: number; + + /** + * Instead of generating a fresh, random reserve key pair, + * use the provided reserve private key. + * + * Use with caution. Usage of this field may be restricted + * to developer mode. + */ + forceReservePriv?: EddsaPrivateKeyString; +} + +export const codecForAcceptManualWithdrawalRequest = + (): Codec<AcceptManualWithdrawalRequest> => + buildCodecForObject<AcceptManualWithdrawalRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("amount", codecForAmountString()) + .property("restrictAge", codecOptional(codecForNumber())) + .property("forceReservePriv", codecOptional(codecForString())) + .build("AcceptManualWithdrawalRequest"); + +export interface GetWithdrawalDetailsForAmountRequest { + exchangeBaseUrl: string; + amount: AmountString; + restrictAge?: number; + + /** + * ID provided by the client to cancel the request. + * + * If the same request is made again with the same clientCancellationId, + * all previous requests are cancelled. + * + * The cancelled request will receive an error response with + * an error code that indicates the cancellation. + * + * The cancellation is best-effort, responses might still arrive. + */ + clientCancellationId?: string; +} + +export interface PrepareBankIntegratedWithdrawalRequest { + talerWithdrawUri: string; + exchangeBaseUrl: string; + forcedDenomSel?: ForcedDenomSel; + restrictAge?: number; +} + +export const codecForPrepareBankIntegratedWithdrawalRequest = + (): Codec<PrepareBankIntegratedWithdrawalRequest> => + buildCodecForObject<PrepareBankIntegratedWithdrawalRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("talerWithdrawUri", codecForString()) + .property("forcedDenomSel", codecForAny()) + .property("restrictAge", codecOptional(codecForNumber())) + .build("PrepareBankIntegratedWithdrawalRequest"); + +export interface PrepareBankIntegratedWithdrawalResponse { + transactionId: string; +} + +export interface ConfirmWithdrawalRequest { + transactionId: string; +} + +export const codecForConfirmWithdrawalRequestRequest = + (): Codec<ConfirmWithdrawalRequest> => + buildCodecForObject<ConfirmWithdrawalRequest>() + .property("transactionId", codecForString()) + .build("ConfirmWithdrawalRequest"); + +export interface AcceptBankIntegratedWithdrawalRequest { + talerWithdrawUri: string; + exchangeBaseUrl: string; + forcedDenomSel?: ForcedDenomSel; + restrictAge?: number; +} + +export const codecForAcceptBankIntegratedWithdrawalRequest = + (): Codec<AcceptBankIntegratedWithdrawalRequest> => + buildCodecForObject<AcceptBankIntegratedWithdrawalRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("talerWithdrawUri", codecForString()) + .property("forcedDenomSel", codecForAny()) + .property("restrictAge", codecOptional(codecForNumber())) + .build("AcceptBankIntegratedWithdrawalRequest"); + +export const codecForGetWithdrawalDetailsForAmountRequest = + (): Codec<GetWithdrawalDetailsForAmountRequest> => + buildCodecForObject<GetWithdrawalDetailsForAmountRequest>() + .property("exchangeBaseUrl", codecForString()) + .property("amount", codecForAmountString()) + .property("restrictAge", codecOptional(codecForNumber())) + .property("clientCancellationId", codecOptional(codecForString())) + .build("GetWithdrawalDetailsForAmountRequest"); + +export interface AcceptExchangeTosRequest { + exchangeBaseUrl: string; +} + +export const codecForAcceptExchangeTosRequest = + (): Codec<AcceptExchangeTosRequest> => + buildCodecForObject<AcceptExchangeTosRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("AcceptExchangeTosRequest"); + +export interface ForgetExchangeTosRequest { + exchangeBaseUrl: string; +} + +export const codecForForgetExchangeTosRequest = + (): Codec<ForgetExchangeTosRequest> => + buildCodecForObject<ForgetExchangeTosRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("ForgetExchangeTosRequest"); + +export interface AcceptRefundRequest { + transactionId: TransactionIdStr; +} + +export const codecForApplyRefundRequest = (): Codec<AcceptRefundRequest> => + buildCodecForObject<AcceptRefundRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("AcceptRefundRequest"); + +export interface ApplyRefundFromPurchaseIdRequest { + purchaseId: string; +} + +export const codecForApplyRefundFromPurchaseIdRequest = + (): Codec<ApplyRefundFromPurchaseIdRequest> => + buildCodecForObject<ApplyRefundFromPurchaseIdRequest>() + .property("purchaseId", codecForString()) + .build("ApplyRefundFromPurchaseIdRequest"); + +export interface GetWithdrawalDetailsForUriRequest { + talerWithdrawUri: string; + restrictAge?: number; +} + +export const codecForGetWithdrawalDetailsForUri = + (): Codec<GetWithdrawalDetailsForUriRequest> => + buildCodecForObject<GetWithdrawalDetailsForUriRequest>() + .property("talerWithdrawUri", codecForString()) + .property("restrictAge", codecOptional(codecForNumber())) + .build("GetWithdrawalDetailsForUriRequest"); + +export interface ListKnownBankAccountsRequest { + currency?: string; +} + +export const codecForListKnownBankAccounts = + (): Codec<ListKnownBankAccountsRequest> => + buildCodecForObject<ListKnownBankAccountsRequest>() + .property("currency", codecOptional(codecForString())) + .build("ListKnownBankAccountsRequest"); + +export interface AddKnownBankAccountsRequest { + payto: string; + alias: string; + currency: string; +} +export const codecForAddKnownBankAccounts = + (): Codec<AddKnownBankAccountsRequest> => + buildCodecForObject<AddKnownBankAccountsRequest>() + .property("payto", codecForString()) + .property("alias", codecForString()) + .property("currency", codecForString()) + .build("AddKnownBankAccountsRequest"); + +export interface ForgetKnownBankAccountsRequest { + payto: string; +} + +export const codecForForgetKnownBankAccounts = + (): Codec<ForgetKnownBankAccountsRequest> => + buildCodecForObject<ForgetKnownBankAccountsRequest>() + .property("payto", codecForString()) + .build("ForgetKnownBankAccountsRequest"); + +export interface AbortProposalRequest { + proposalId: string; +} + +export const codecForAbortProposalRequest = (): Codec<AbortProposalRequest> => + buildCodecForObject<AbortProposalRequest>() + .property("proposalId", codecForString()) + .build("AbortProposalRequest"); + +export interface GetContractTermsDetailsRequest { + // @deprecated use transaction id + proposalId?: string; + transactionId?: string; +} + +export const codecForGetContractTermsDetails = + (): Codec<GetContractTermsDetailsRequest> => + buildCodecForObject<GetContractTermsDetailsRequest>() + .property("proposalId", codecOptional(codecForString())) + .property("transactionId", codecOptional(codecForString())) + .build("GetContractTermsDetails"); + +export interface PreparePayRequest { + talerPayUri: string; +} + +export const codecForPreparePayRequest = (): Codec<PreparePayRequest> => + buildCodecForObject<PreparePayRequest>() + .property("talerPayUri", codecForString()) + .build("PreparePay"); + +export interface SharePaymentRequest { + merchantBaseUrl: string; + orderId: string; +} +export const codecForSharePaymentRequest = (): Codec<SharePaymentRequest> => + buildCodecForObject<SharePaymentRequest>() + .property("merchantBaseUrl", codecForString()) + .property("orderId", codecForString()) + .build("SharePaymentRequest"); + +export interface SharePaymentResult { + privatePayUri: string; +} +export const codecForSharePaymentResult = (): Codec<SharePaymentResult> => + buildCodecForObject<SharePaymentResult>() + .property("privatePayUri", codecForString()) + .build("SharePaymentResult"); + +export interface PreparePayTemplateRequest { + talerPayTemplateUri: string; + templateParams?: TemplateParams; +} + +export const codecForPreparePayTemplateRequest = + (): Codec<PreparePayTemplateRequest> => + buildCodecForObject<PreparePayTemplateRequest>() + .property("talerPayTemplateUri", codecForString()) + .property("templateParams", codecForAny()) + .build("PreparePayTemplate"); + +export interface ConfirmPayRequest { + /** + * @deprecated use transactionId instead + */ + proposalId?: string; + transactionId?: TransactionIdStr; + sessionId?: string; + forcedCoinSel?: ForcedCoinSel; +} + +export const codecForConfirmPayRequest = (): Codec<ConfirmPayRequest> => + buildCodecForObject<ConfirmPayRequest>() + .property("proposalId", codecOptional(codecForString())) + .property("transactionId", codecOptional(codecForTransactionIdStr())) + .property("sessionId", codecOptional(codecForString())) + .property("forcedCoinSel", codecForAny()) + .build("ConfirmPay"); + +export interface CoreApiRequestEnvelope { + id: string; + operation: string; + args: unknown; +} + +export type CoreApiResponse = CoreApiResponseSuccess | CoreApiResponseError; + +export type CoreApiMessageEnvelope = CoreApiResponse | CoreApiNotification; + +export interface CoreApiNotification { + type: "notification"; + payload: unknown; +} + +export interface CoreApiResponseSuccess { + // To distinguish the message from notifications + type: "response"; + operation: string; + id: string; + result: unknown; +} + +export interface CoreApiResponseError { + // To distinguish the message from notifications + type: "error"; + operation: string; + id: string; + error: TalerErrorDetail; +} + +export interface WithdrawTestBalanceRequest { + amount: AmountString; + /** + * Corebank API base URL. + */ + corebankApiBaseUrl: string; + exchangeBaseUrl: string; + forcedDenomSel?: ForcedDenomSel; +} + +/** + * Request to the crypto worker to make a sync signature. + */ +export interface MakeSyncSignatureRequest { + accountPriv: string; + oldHash: string | undefined; + newHash: string; +} + +/** + * Planchet for a coin during refresh. + */ +export interface RefreshPlanchetInfo { + /** + * Public key for the coin. + */ + coinPub: string; + + /** + * Private key for the coin. + */ + coinPriv: string; + + /** + * Blinded public key. + */ + coinEv: CoinEnvelope; + + coinEvHash: string; + + /** + * Blinding key used. + */ + blindingKey: string; + + maxAge: number; + ageCommitmentProof?: AgeCommitmentProof; +} + +/** + * Strategy for loading recovery information. + */ +export enum RecoveryMergeStrategy { + /** + * Keep the local wallet root key, import and take over providers. + */ + Ours = "ours", + + /** + * Migrate to the wallet root key from the recovery information. + */ + Theirs = "theirs", +} + +/** + * Load recovery information into the wallet. + */ +export interface RecoveryLoadRequest { + recovery: BackupRecovery; + strategy?: RecoveryMergeStrategy; +} + +export const codecForWithdrawTestBalance = + (): Codec<WithdrawTestBalanceRequest> => + buildCodecForObject<WithdrawTestBalanceRequest>() + .property("amount", codecForAmountString()) + .property("exchangeBaseUrl", codecForString()) + .property("forcedDenomSel", codecForAny()) + .property("corebankApiBaseUrl", codecForString()) + .build("WithdrawTestBalanceRequest"); + +export interface SetCoinSuspendedRequest { + coinPub: string; + suspended: boolean; +} + +export const codecForSetCoinSuspendedRequest = + (): Codec<SetCoinSuspendedRequest> => + buildCodecForObject<SetCoinSuspendedRequest>() + .property("coinPub", codecForString()) + .property("suspended", codecForBoolean()) + .build("SetCoinSuspendedRequest"); + +export interface RefreshCoinSpec { + coinPub: string; + amount?: AmountString; +} + +export const codecForRefreshCoinSpec = (): Codec<RefreshCoinSpec> => + buildCodecForObject<RefreshCoinSpec>() + .property("amount", codecForAmountString()) + .property("coinPub", codecForString()) + .build("ForceRefreshRequest"); + +export interface ForceRefreshRequest { + refreshCoinSpecs: RefreshCoinSpec[]; +} + +export const codecForForceRefreshRequest = (): Codec<ForceRefreshRequest> => + buildCodecForObject<ForceRefreshRequest>() + .property("refreshCoinSpecs", codecForList(codecForRefreshCoinSpec())) + .build("ForceRefreshRequest"); + +export interface PrepareRefundRequest { + talerRefundUri: string; +} + +export interface StartRefundQueryForUriResponse { + /** + * Transaction id of the *payment* where the refund query was started. + */ + transactionId: TransactionIdStr; +} + +export const codecForPrepareRefundRequest = (): Codec<PrepareRefundRequest> => + buildCodecForObject<PrepareRefundRequest>() + .property("talerRefundUri", codecForString()) + .build("PrepareRefundRequest"); + +export interface StartRefundQueryRequest { + transactionId: TransactionIdStr; +} + +export const codecForStartRefundQueryRequest = + (): Codec<StartRefundQueryRequest> => + buildCodecForObject<StartRefundQueryRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("StartRefundQueryRequest"); + +export interface PrepareRewardRequest { + talerRewardUri: string; +} + +export const codecForPrepareRewardRequest = (): Codec<PrepareRewardRequest> => + buildCodecForObject<PrepareRewardRequest>() + .property("talerRewardUri", codecForString()) + .build("PrepareRewardRequest"); + +export interface AcceptRewardRequest { + /** + * @deprecated use transactionId + */ + walletRewardId?: string; + /** + * it will be required when "walletRewardId" is removed + */ + transactionId?: TransactionIdStr; +} + +export const codecForAcceptTipRequest = (): Codec<AcceptRewardRequest> => + buildCodecForObject<AcceptRewardRequest>() + .property("walletRewardId", codecOptional(codecForString())) + .property("transactionId", codecOptional(codecForTransactionIdStr())) + .build("AcceptRewardRequest"); + +export interface FailTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForFailTransactionRequest = + (): Codec<FailTransactionRequest> => + buildCodecForObject<FailTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("FailTransactionRequest"); + +export interface SuspendTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForSuspendTransaction = + (): Codec<SuspendTransactionRequest> => + buildCodecForObject<AbortTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("SuspendTransactionRequest"); + +export interface ResumeTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForResumeTransaction = (): Codec<ResumeTransactionRequest> => + buildCodecForObject<ResumeTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("ResumeTransactionRequest"); + +export interface AbortTransactionRequest { + transactionId: TransactionIdStr; +} + +export interface FailTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForAbortTransaction = (): Codec<AbortTransactionRequest> => + buildCodecForObject<AbortTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("AbortTransactionRequest"); + +export interface DepositGroupFees { + coin: AmountString; + wire: AmountString; + refresh: AmountString; +} + +export interface CreateDepositGroupRequest { + /** + * Pre-allocated transaction ID. + * Allows clients to easily handle notifications + * that occur while the operation has been created but + * before the creation request has returned. + */ + transactionId?: TransactionIdStr; + depositPaytoUri: string; + amount: AmountString; +} + +export interface PrepareDepositRequest { + depositPaytoUri: string; + amount: AmountString; +} +export const codecForPrepareDepositRequest = (): Codec<PrepareDepositRequest> => + buildCodecForObject<PrepareDepositRequest>() + .property("amount", codecForAmountString()) + .property("depositPaytoUri", codecForString()) + .build("PrepareDepositRequest"); + +export interface PrepareDepositResponse { + totalDepositCost: AmountString; + effectiveDepositAmount: AmountString; + fees: DepositGroupFees; +} + +export const codecForCreateDepositGroupRequest = + (): Codec<CreateDepositGroupRequest> => + buildCodecForObject<CreateDepositGroupRequest>() + .property("amount", codecForAmountString()) + .property("depositPaytoUri", codecForString()) + .property("transactionId", codecOptional(codecForTransactionIdStr())) + .build("CreateDepositGroupRequest"); + +export interface CreateDepositGroupResponse { + depositGroupId: string; + transactionId: TransactionIdStr; +} + +export interface TxIdResponse { + transactionId: TransactionIdStr; +} + +export interface WithdrawUriInfoResponse { + operationId: string; + status: WithdrawalOperationStatus; + confirmTransferUrl?: string; + amount: AmountString; + defaultExchangeBaseUrl?: string; + possibleExchanges: ExchangeListItem[]; +} + +export const codecForWithdrawUriInfoResponse = + (): Codec<WithdrawUriInfoResponse> => + buildCodecForObject<WithdrawUriInfoResponse>() + .property("operationId", codecForString()) + .property("confirmTransferUrl", codecOptional(codecForString())) + .property( + "status", + codecForEither( + codecForConstString("pending"), + codecForConstString("selected"), + codecForConstString("aborted"), + codecForConstString("confirmed"), + ), + ) + .property("amount", codecForAmountString()) + .property("defaultExchangeBaseUrl", codecOptional(codecForString())) + .property("possibleExchanges", codecForList(codecForExchangeListItem())) + .build("WithdrawUriInfoResponse"); + +export interface WalletCurrencyInfo { + trustedAuditors: { + currency: string; + auditorPub: string; + auditorBaseUrl: string; + }[]; + trustedExchanges: { + currency: string; + exchangeMasterPub: string; + exchangeBaseUrl: string; + }[]; +} + +export interface TestingListTasksForTransactionRequest { + transactionId: TransactionIdStr; +} + +export interface TestingListTasksForTransactionsResponse { + taskIdList: string[]; +} + +export const codecForTestingListTasksForTransactionRequest = + (): Codec<TestingListTasksForTransactionRequest> => + buildCodecForObject<TestingListTasksForTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("TestingListTasksForTransactionRequest"); + +export interface DeleteTransactionRequest { + transactionId: TransactionIdStr; +} + +export interface RetryTransactionRequest { + transactionId: TransactionIdStr; +} + +export const codecForDeleteTransactionRequest = + (): Codec<DeleteTransactionRequest> => + buildCodecForObject<DeleteTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("DeleteTransactionRequest"); + +export const codecForRetryTransactionRequest = + (): Codec<RetryTransactionRequest> => + buildCodecForObject<RetryTransactionRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("RetryTransactionRequest"); + +export interface SetWalletDeviceIdRequest { + /** + * New wallet device ID to set. + */ + walletDeviceId: string; +} + +export const codecForSetWalletDeviceIdRequest = + (): Codec<SetWalletDeviceIdRequest> => + buildCodecForObject<SetWalletDeviceIdRequest>() + .property("walletDeviceId", codecForString()) + .build("SetWalletDeviceIdRequest"); + +export interface WithdrawFakebankRequest { + amount: AmountString; + exchange: string; + bank: string; +} + +export enum AttentionPriority { + High = "high", + Medium = "medium", + Low = "low", +} + +export interface UserAttentionByIdRequest { + entityId: string; + type: AttentionType; +} + +export const codecForUserAttentionByIdRequest = + (): Codec<UserAttentionByIdRequest> => + buildCodecForObject<UserAttentionByIdRequest>() + .property("type", codecForAny()) + .property("entityId", codecForString()) + .build("UserAttentionByIdRequest"); + +export const codecForUserAttentionsRequest = (): Codec<UserAttentionsRequest> => + buildCodecForObject<UserAttentionsRequest>() + .property( + "priority", + codecOptional( + codecForEither( + codecForConstString(AttentionPriority.Low), + codecForConstString(AttentionPriority.Medium), + codecForConstString(AttentionPriority.High), + ), + ), + ) + .build("UserAttentionsRequest"); + +export interface UserAttentionsRequest { + priority?: AttentionPriority; +} + +export type AttentionInfo = + | AttentionKycWithdrawal + | AttentionBackupUnpaid + | AttentionBackupExpiresSoon + | AttentionMerchantRefund + | AttentionExchangeTosChanged + | AttentionExchangeKeyExpired + | AttentionExchangeDenominationExpired + | AttentionAuditorTosChanged + | AttentionAuditorKeyExpires + | AttentionAuditorDenominationExpires + | AttentionPullPaymentPaid + | AttentionPushPaymentReceived; + +export enum AttentionType { + KycWithdrawal = "kyc-withdrawal", + + BackupUnpaid = "backup-unpaid", + BackupExpiresSoon = "backup-expires-soon", + MerchantRefund = "merchant-refund", + + ExchangeTosChanged = "exchange-tos-changed", + ExchangeKeyExpired = "exchange-key-expired", + ExchangeKeyExpiresSoon = "exchange-key-expires-soon", + ExchangeDenominationsExpired = "exchange-denominations-expired", + ExchangeDenominationsExpiresSoon = "exchange-denominations-expires-soon", + + AuditorTosChanged = "auditor-tos-changed", + AuditorKeyExpires = "auditor-key-expires", + AuditorDenominationsExpires = "auditor-denominations-expires", + + PullPaymentPaid = "pull-payment-paid", + PushPaymentReceived = "push-payment-withdrawn", +} + +export const UserAttentionPriority: { + [type in AttentionType]: AttentionPriority; +} = { + "kyc-withdrawal": AttentionPriority.Medium, + + "backup-unpaid": AttentionPriority.High, + "backup-expires-soon": AttentionPriority.Medium, + "merchant-refund": AttentionPriority.Medium, + + "exchange-tos-changed": AttentionPriority.Medium, + + "exchange-key-expired": AttentionPriority.High, + "exchange-key-expires-soon": AttentionPriority.Medium, + "exchange-denominations-expired": AttentionPriority.High, + "exchange-denominations-expires-soon": AttentionPriority.Medium, + + "auditor-tos-changed": AttentionPriority.Medium, + "auditor-key-expires": AttentionPriority.Medium, + "auditor-denominations-expires": AttentionPriority.Medium, + + "pull-payment-paid": AttentionPriority.High, + "push-payment-withdrawn": AttentionPriority.High, +}; + +interface AttentionBackupExpiresSoon { + type: AttentionType.BackupExpiresSoon; + provider_base_url: string; +} +interface AttentionBackupUnpaid { + type: AttentionType.BackupUnpaid; + provider_base_url: string; + talerUri: string; +} + +interface AttentionMerchantRefund { + type: AttentionType.MerchantRefund; + transactionId: TransactionIdStr; +} + +interface AttentionKycWithdrawal { + type: AttentionType.KycWithdrawal; + transactionId: TransactionIdStr; +} + +interface AttentionExchangeTosChanged { + type: AttentionType.ExchangeTosChanged; + exchange_base_url: string; +} +interface AttentionExchangeKeyExpired { + type: AttentionType.ExchangeKeyExpired; + exchange_base_url: string; +} +interface AttentionExchangeDenominationExpired { + type: AttentionType.ExchangeDenominationsExpired; + exchange_base_url: string; +} +interface AttentionAuditorTosChanged { + type: AttentionType.AuditorTosChanged; + auditor_base_url: string; +} + +interface AttentionAuditorKeyExpires { + type: AttentionType.AuditorKeyExpires; + auditor_base_url: string; +} +interface AttentionAuditorDenominationExpires { + type: AttentionType.AuditorDenominationsExpires; + auditor_base_url: string; +} +interface AttentionPullPaymentPaid { + type: AttentionType.PullPaymentPaid; + transactionId: TransactionIdStr; +} + +interface AttentionPushPaymentReceived { + type: AttentionType.PushPaymentReceived; + transactionId: TransactionIdStr; +} + +export type UserAttentionUnreadList = Array<{ + info: AttentionInfo; + when: TalerPreciseTimestamp; + read: boolean; +}>; + +export interface UserAttentionsResponse { + pending: UserAttentionUnreadList; +} + +export interface UserAttentionsCountResponse { + total: number; +} + +export const codecForWithdrawFakebankRequest = + (): Codec<WithdrawFakebankRequest> => + buildCodecForObject<WithdrawFakebankRequest>() + .property("amount", codecForAmountString()) + .property("bank", codecForString()) + .property("exchange", codecForString()) + .build("WithdrawFakebankRequest"); + +export interface ActiveTask { + taskId: string; + transaction: TransactionIdStr | undefined; + firstTry: AbsoluteTime | undefined; + nextTry: AbsoluteTime | undefined; + retryCounter: number | undefined; + lastError: TalerErrorDetail | undefined; +} + +export interface GetActiveTasksResponse { + tasks: ActiveTask[]; +} + +export const codecForActiveTask = (): Codec<ActiveTask> => + buildCodecForObject<ActiveTask>() + .property("taskId", codecForString()) + .property("transaction", codecOptional(codecForTransactionIdStr())) + .property("retryCounter", codecOptional(codecForNumber())) + .property("firstTry", codecOptional(codecForAbsoluteTime)) + .property("nextTry", codecOptional(codecForAbsoluteTime)) + .property("lastError", codecOptional(codecForTalerErrorDetail())) + .build("ActiveTask"); + +export const codecForGetActiveTasks = (): Codec<GetActiveTasksResponse> => + buildCodecForObject<GetActiveTasksResponse>() + .property("tasks", codecForList(codecForActiveTask())) + .build("GetActiveTasks"); + +export interface ImportDbRequest { + dump: any; +} + +export const codecForImportDbRequest = (): Codec<ImportDbRequest> => + buildCodecForObject<ImportDbRequest>() + .property("dump", codecForAny()) + .build("ImportDbRequest"); + +export interface ForcedDenomSel { + denoms: { + value: AmountString; + count: number; + }[]; +} + +/** + * Forced coin selection for deposits/payments. + */ +export interface ForcedCoinSel { + coins: { + value: AmountString; + contribution: AmountString; + }[]; +} + +export interface TestPayResult { + /** + * Number of coins used for the payment. + */ + numCoins: number; +} + +export interface SelectedCoin { + denomPubHash: string; + coinPub: string; + contribution: AmountString; + exchangeBaseUrl: string; +} + +export interface SelectedProspectiveCoin { + denomPubHash: string; + contribution: AmountString; + exchangeBaseUrl: string; +} + +/** + * Result of selecting coins, contains the exchange, and selected + * coins with their denomination. + */ +export interface PayCoinSelection { + coins: SelectedCoin[]; + + /** + * How much of the wire fees is the customer paying? + */ + customerWireFees: AmountString; + + /** + * How much of the deposit fees is the customer paying? + */ + customerDepositFees: AmountString; +} + +export interface ProspectivePayCoinSelection { + prospectiveCoins: SelectedProspectiveCoin[]; + + /** + * How much of the wire fees is the customer paying? + */ + customerWireFees: AmountString; + + /** + * How much of the deposit fees is the customer paying? + */ + customerDepositFees: AmountString; +} + +export interface CheckPeerPushDebitRequest { + /** + * Preferred exchange to use for the p2p payment. + */ + exchangeBaseUrl?: string; + + /** + * Instructed amount. + * + * FIXME: Allow specifying the instructed amount type. + */ + amount: AmountString; +} + +export const codecForCheckPeerPushDebitRequest = + (): Codec<CheckPeerPushDebitRequest> => + buildCodecForObject<CheckPeerPushDebitRequest>() + .property("exchangeBaseUrl", codecOptional(codecForString())) + .property("amount", codecForAmountString()) + .build("CheckPeerPushDebitRequest"); + +export interface CheckPeerPushDebitResponse { + amountRaw: AmountString; + amountEffective: AmountString; + exchangeBaseUrl: string; + /** + * Maximum expiration date, based on how close the coins + * used for the payment are to expiry. + * + * The value is based on when the wallet would typically + * automatically refresh the coins on its own, leaving enough + * time to get a refund for the push payment and refresh the + * coin. + */ + maxExpirationDate: TalerProtocolTimestamp; +} + +export interface InitiatePeerPushDebitRequest { + exchangeBaseUrl?: string; + partialContractTerms: PeerContractTerms; +} + +export interface InitiatePeerPushDebitResponse { + exchangeBaseUrl: string; + pursePub: string; + mergePriv: string; + contractPriv: string; + transactionId: TransactionIdStr; +} + +export const codecForInitiatePeerPushDebitRequest = + (): Codec<InitiatePeerPushDebitRequest> => + buildCodecForObject<InitiatePeerPushDebitRequest>() + .property("partialContractTerms", codecForPeerContractTerms()) + .build("InitiatePeerPushDebitRequest"); + +export interface PreparePeerPushCreditRequest { + talerUri: string; +} + +export interface PreparePeerPullDebitRequest { + talerUri: string; +} + +export interface PreparePeerPushCreditResponse { + contractTerms: PeerContractTerms; + amountRaw: AmountString; + amountEffective: AmountString; + + transactionId: TransactionIdStr; + + exchangeBaseUrl: string; + + /** + * @deprecated use transaction ID instead. + */ + peerPushCreditId: string; + + /** + * @deprecated + */ + amount: AmountString; +} + +export interface PreparePeerPullDebitResponse { + contractTerms: PeerContractTerms; + /** + * @deprecated Redundant field with bad name, will be removed soon. + */ + amount: AmountString; + + amountRaw: AmountString; + amountEffective: AmountString; + + peerPullDebitId: string; + + transactionId: TransactionIdStr; +} + +export const codecForPreparePeerPushCreditRequest = + (): Codec<PreparePeerPushCreditRequest> => + buildCodecForObject<PreparePeerPushCreditRequest>() + .property("talerUri", codecForString()) + .build("CheckPeerPushPaymentRequest"); + +export const codecForCheckPeerPullPaymentRequest = + (): Codec<PreparePeerPullDebitRequest> => + buildCodecForObject<PreparePeerPullDebitRequest>() + .property("talerUri", codecForString()) + .build("PreparePeerPullDebitRequest"); + +export interface ConfirmPeerPushCreditRequest { + transactionId: string; +} +export interface AcceptPeerPushPaymentResponse { + transactionId: TransactionIdStr; +} + +export interface AcceptPeerPullPaymentResponse { + transactionId: TransactionIdStr; +} + +export const codecForConfirmPeerPushPaymentRequest = + (): Codec<ConfirmPeerPushCreditRequest> => + buildCodecForObject<ConfirmPeerPushCreditRequest>() + .property("transactionId", codecForString()) + .build("ConfirmPeerPushCreditRequest"); + +export interface ConfirmPeerPullDebitRequest { + transactionId: TransactionIdStr; +} + +export interface ApplyDevExperimentRequest { + devExperimentUri: string; +} + +export const codecForApplyDevExperiment = + (): Codec<ApplyDevExperimentRequest> => + buildCodecForObject<ApplyDevExperimentRequest>() + .property("devExperimentUri", codecForString()) + .build("ApplyDevExperimentRequest"); + +export const codecForAcceptPeerPullPaymentRequest = + (): Codec<ConfirmPeerPullDebitRequest> => + buildCodecForObject<ConfirmPeerPullDebitRequest>() + .property("transactionId", codecForTransactionIdStr()) + .build("ConfirmPeerPullDebitRequest"); + +export interface CheckPeerPullCreditRequest { + exchangeBaseUrl?: string; + amount: AmountString; +} +export const codecForPreparePeerPullPaymentRequest = + (): Codec<CheckPeerPullCreditRequest> => + buildCodecForObject<CheckPeerPullCreditRequest>() + .property("amount", codecForAmountString()) + .property("exchangeBaseUrl", codecOptional(codecForString())) + .build("CheckPeerPullCreditRequest"); + +export interface CheckPeerPullCreditResponse { + exchangeBaseUrl: string; + amountRaw: AmountString; + amountEffective: AmountString; + + /** + * Number of coins that will be used, + * can be used by the UI to warn if excessively large. + */ + numCoins: number; +} +export interface InitiatePeerPullCreditRequest { + exchangeBaseUrl?: string; + partialContractTerms: PeerContractTerms; +} + +export const codecForInitiatePeerPullPaymentRequest = + (): Codec<InitiatePeerPullCreditRequest> => + buildCodecForObject<InitiatePeerPullCreditRequest>() + .property("partialContractTerms", codecForPeerContractTerms()) + .property("exchangeBaseUrl", codecOptional(codecForString())) + .build("InitiatePeerPullCreditRequest"); + +export interface InitiatePeerPullCreditResponse { + /** + * Taler URI for the other party to make the payment + * that was requested. + * + * @deprecated since it's not necessarily valid yet until the tx is in the right state + */ + talerUri: string; + + transactionId: TransactionIdStr; +} + +export interface ValidateIbanRequest { + iban: string; +} + +export const codecForValidateIbanRequest = (): Codec<ValidateIbanRequest> => + buildCodecForObject<ValidateIbanRequest>() + .property("iban", codecForString()) + .build("ValidateIbanRequest"); + +export interface ValidateIbanResponse { + valid: boolean; +} + +export const codecForValidateIbanResponse = (): Codec<ValidateIbanResponse> => + buildCodecForObject<ValidateIbanResponse>() + .property("valid", codecForBoolean()) + .build("ValidateIbanResponse"); + +export type TransactionStateFilter = "nonfinal"; + +export interface TransactionRecordFilter { + onlyState?: TransactionStateFilter; + onlyCurrency?: string; +} + +export interface StoredBackupList { + storedBackups: { + name: string; + }[]; +} + +export interface CreateStoredBackupResponse { + name: string; +} + +export interface RecoverStoredBackupRequest { + name: string; +} + +export interface DeleteStoredBackupRequest { + name: string; +} + +export const codecForDeleteStoredBackupRequest = + (): Codec<DeleteStoredBackupRequest> => + buildCodecForObject<DeleteStoredBackupRequest>() + .property("name", codecForString()) + .build("DeleteStoredBackupRequest"); + +export const codecForRecoverStoredBackupRequest = + (): Codec<RecoverStoredBackupRequest> => + buildCodecForObject<RecoverStoredBackupRequest>() + .property("name", codecForString()) + .build("RecoverStoredBackupRequest"); + +export interface TestingSetTimetravelRequest { + offsetMs: number; +} + +export const codecForTestingSetTimetravelRequest = + (): Codec<TestingSetTimetravelRequest> => + buildCodecForObject<TestingSetTimetravelRequest>() + .property("offsetMs", codecForNumber()) + .build("TestingSetTimetravelRequest"); + +export interface AllowedAuditorInfo { + auditorBaseUrl: string; + auditorPub: string; +} + +export interface AllowedExchangeInfo { + exchangeBaseUrl: string; + exchangePub: string; +} + +/** + * Data extracted from the contract terms that is relevant for payment + * processing in the wallet. + */ +export interface WalletContractData { + /** + * Fulfillment URL, or the empty string if the order has no fulfillment URL. + * + * Stored as a non-nullable string as we use this field for IndexedDB indexing. + */ + fulfillmentUrl: string; + + contractTermsHash: string; + fulfillmentMessage?: string; + fulfillmentMessageI18n?: InternationalizedString; + merchantSig: string; + merchantPub: string; + merchant: MerchantInfo; + amount: AmountString; + orderId: string; + merchantBaseUrl: string; + summary: string; + summaryI18n: { [lang_tag: string]: string } | undefined; + autoRefund: TalerProtocolDuration | undefined; + payDeadline: TalerProtocolTimestamp; + refundDeadline: TalerProtocolTimestamp; + allowedExchanges: AllowedExchangeInfo[]; + timestamp: TalerProtocolTimestamp; + wireMethod: string; + wireInfoHash: string; + maxDepositFee: AmountString; + minimumAge?: number; +} + +export interface TestingWaitTransactionRequest { + transactionId: TransactionIdStr; + txState: TransactionState; +} + +export interface TestingGetDenomStatsRequest { + exchangeBaseUrl: string; +} + +export interface TestingGetDenomStatsResponse { + numKnown: number; + numOffered: number; + numLost: number; +} + +export const codecForTestingGetDenomStatsRequest = + (): Codec<TestingGetDenomStatsRequest> => + buildCodecForObject<TestingGetDenomStatsRequest>() + .property("exchangeBaseUrl", codecForString()) + .build("TestingGetDenomStatsRequest"); + +export interface WithdrawalExchangeAccountDetails { + /** + * Payto URI to credit the exchange. + * + * Depending on whether the (manual!) withdrawal is accepted or just + * being checked, this already includes the subject with the + * reserve public key. + */ + paytoUri: string; + + /** + * Status that indicates whether the account can be used + * by the user to send funds for a withdrawal. + * + * ok: account should be shown to the user + * error: account should not be shown to the user, UIs might render the error (in conversionError), + * especially in dev mode. + */ + status: "ok" | "error"; + + /** + * Transfer amount. Might be in a different currency than the requested + * amount for withdrawal. + * + * Absent if this is a conversion account and the conversion failed. + */ + transferAmount?: AmountString; + + /** + * Currency specification for the external currency. + * + * Only included if this account requires a currency conversion. + */ + currencySpecification?: CurrencySpecification; + + /** + * Further restrictions for sending money to the + * exchange. + */ + creditRestrictions?: AccountRestriction[]; + + /** + * Label given to the account or the account's bank by the exchange. + */ + bankLabel?: string; + + /* + * Display priority assigned to this bank account by the exchange. + */ + priority?: number; + + /** + * Error that happened when attempting to request the conversion rate. + */ + conversionError?: TalerErrorDetail; +} + +export interface PrepareWithdrawExchangeRequest { + /** + * A taler://withdraw-exchange URI. + */ + talerUri: string; +} + +export const codecForPrepareWithdrawExchangeRequest = + (): Codec<PrepareWithdrawExchangeRequest> => + buildCodecForObject<PrepareWithdrawExchangeRequest>() + .property("talerUri", codecForString()) + .build("PrepareWithdrawExchangeRequest"); + +export interface PrepareWithdrawExchangeResponse { + /** + * Base URL of the exchange that already existed + * or was ephemerally added as an exchange entry to + * the wallet. + */ + exchangeBaseUrl: string; + + /** + * Amount from the taler://withdraw-exchange URI. + * Only present if specified in the URI. + */ + amount?: AmountString; +} + +export interface ExchangeEntryState { + tosStatus: ExchangeTosStatus; + exchangeEntryStatus: ExchangeEntryStatus; + exchangeUpdateStatus: ExchangeUpdateStatus; +} + +export interface ListGlobalCurrencyAuditorsResponse { + auditors: { + currency: string; + auditorBaseUrl: string; + auditorPub: string; + }[]; +} + +export interface ListGlobalCurrencyExchangesResponse { + exchanges: { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; + }[]; +} + +export interface AddGlobalCurrencyExchangeRequest { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; +} + +export const codecForAddGlobalCurrencyExchangeRequest = + (): Codec<AddGlobalCurrencyExchangeRequest> => + buildCodecForObject<AddGlobalCurrencyExchangeRequest>() + .property("currency", codecForString()) + .property("exchangeBaseUrl", codecForString()) + .property("exchangeMasterPub", codecForString()) + .build("AddGlobalCurrencyExchangeRequest"); + +export interface RemoveGlobalCurrencyExchangeRequest { + currency: string; + exchangeBaseUrl: string; + exchangeMasterPub: string; +} + +export const codecForRemoveGlobalCurrencyExchangeRequest = + (): Codec<RemoveGlobalCurrencyExchangeRequest> => + buildCodecForObject<RemoveGlobalCurrencyExchangeRequest>() + .property("currency", codecForString()) + .property("exchangeBaseUrl", codecForString()) + .property("exchangeMasterPub", codecForString()) + .build("RemoveGlobalCurrencyExchangeRequest"); + +export interface AddGlobalCurrencyAuditorRequest { + currency: string; + auditorBaseUrl: string; + auditorPub: string; +} + +export const codecForAddGlobalCurrencyAuditorRequest = + (): Codec<AddGlobalCurrencyAuditorRequest> => + buildCodecForObject<AddGlobalCurrencyAuditorRequest>() + .property("currency", codecForString()) + .property("auditorBaseUrl", codecForString()) + .property("auditorPub", codecForString()) + .build("AddGlobalCurrencyAuditorRequest"); + +export interface RemoveGlobalCurrencyAuditorRequest { + currency: string; + auditorBaseUrl: string; + auditorPub: string; +} + +export const codecForRemoveGlobalCurrencyAuditorRequest = + (): Codec<RemoveGlobalCurrencyAuditorRequest> => + buildCodecForObject<RemoveGlobalCurrencyAuditorRequest>() + .property("currency", codecForString()) + .property("auditorBaseUrl", codecForString()) + .property("auditorPub", codecForString()) + .build("RemoveGlobalCurrencyAuditorRequest"); + +/** + * Information about one provider. + * + * We don't store the account key here, + * as that's derived from the wallet root key. + */ +export interface ProviderInfo { + active: boolean; + syncProviderBaseUrl: string; + name: string; + terms?: BackupProviderTerms; + /** + * Last communication issue with the provider. + */ + lastError?: TalerErrorDetail; + lastSuccessfulBackupTimestamp?: TalerPreciseTimestamp; + lastAttemptedBackupTimestamp?: TalerPreciseTimestamp; + paymentProposalIds: string[]; + backupProblem?: BackupProblem; + paymentStatus: ProviderPaymentStatus; +} + +export interface BackupProviderTerms { + supportedProtocolVersion: string; + annualFee: AmountString; + storageLimitInMegabytes: number; +} + +export type BackupProblem = + | BackupUnreadableProblem + | BackupConflictingDeviceProblem; + +export interface BackupUnreadableProblem { + type: "backup-unreadable"; +} + +export interface BackupConflictingDeviceProblem { + type: "backup-conflicting-device"; + otherDeviceId: string; + myDeviceId: string; + backupTimestamp: AbsoluteTime; +} + +export type ProviderPaymentStatus = + | ProviderPaymentTermsChanged + | ProviderPaymentPaid + | ProviderPaymentInsufficientBalance + | ProviderPaymentUnpaid + | ProviderPaymentPending; + +export enum ProviderPaymentType { + Unpaid = "unpaid", + Pending = "pending", + InsufficientBalance = "insufficient-balance", + Paid = "paid", + TermsChanged = "terms-changed", +} + +export interface ProviderPaymentUnpaid { + type: ProviderPaymentType.Unpaid; +} + +export interface ProviderPaymentInsufficientBalance { + type: ProviderPaymentType.InsufficientBalance; + amount: AmountString; +} + +export interface ProviderPaymentPending { + type: ProviderPaymentType.Pending; + talerUri?: string; +} + +export interface ProviderPaymentPaid { + type: ProviderPaymentType.Paid; + paidUntil: AbsoluteTime; +} + +export interface ProviderPaymentTermsChanged { + type: ProviderPaymentType.TermsChanged; + paidUntil: AbsoluteTime; + oldTerms: BackupProviderTerms; + newTerms: BackupProviderTerms; +} + +// FIXME: Does not really belong here, move to sync API +export interface SyncTermsOfServiceResponse { + // maximum backup size supported + storage_limit_in_megabytes: number; + + // Fee for an account, per year. + annual_fee: AmountString; + + // protocol version supported by the server, + // for now always "0.0". + version: string; +} + +// FIXME: Does not really belong here, move to sync API +export const codecForSyncTermsOfServiceResponse = + (): Codec<SyncTermsOfServiceResponse> => + buildCodecForObject<SyncTermsOfServiceResponse>() + .property("storage_limit_in_megabytes", codecForNumber()) + .property("annual_fee", codecForAmountString()) + .property("version", codecForString()) + .build("SyncTermsOfServiceResponse"); |