aboutsummaryrefslogtreecommitdiff
path: root/packages
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-05-28 10:37:58 -0300
committerSebastian <sebasjm@gmail.com>2024-05-28 10:38:12 -0300
commit02ceacd75fa16889925c8eda6b28f181b2ad392c (patch)
tree9eda088f91d9491a93bf215091d4fe778464f13e /packages
parentefcab636fb053efe83679379c1cc0ed28b4383d8 (diff)
downloadwallet-core-02ceacd75fa16889925c8eda6b28f181b2ad392c.tar.xz
fix #8828
Diffstat (limited to 'packages')
-rw-r--r--packages/taler-util/src/invariants.ts2
-rw-r--r--packages/taler-util/src/wallet-types.ts4
-rw-r--r--packages/taler-wallet-core/src/backup/index.ts16
-rw-r--r--packages/taler-wallet-core/src/coinSelection.ts5
-rw-r--r--packages/taler-wallet-core/src/common.ts51
-rw-r--r--packages/taler-wallet-core/src/db.ts4
-rw-r--r--packages/taler-wallet-core/src/deposits.ts2
-rw-r--r--packages/taler-wallet-core/src/exchanges.ts2
-rw-r--r--packages/taler-wallet-core/src/instructedAmountConversion.ts22
-rw-r--r--packages/taler-wallet-core/src/pay-merchant.ts8
-rw-r--r--packages/taler-wallet-core/src/pay-peer-common.ts6
-rw-r--r--packages/taler-wallet-core/src/pay-peer-pull-credit.ts2
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-credit.ts2
-rw-r--r--packages/taler-wallet-core/src/pay-peer-push-debit.ts2
-rw-r--r--packages/taler-wallet-core/src/recoup.ts4
-rw-r--r--packages/taler-wallet-core/src/refresh.ts14
-rw-r--r--packages/taler-wallet-core/src/transactions.ts22
-rw-r--r--packages/taler-wallet-core/src/wallet.ts10
-rw-r--r--packages/taler-wallet-core/src/withdraw.ts4
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts68
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx15
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts57
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx11
-rw-r--r--packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx1
25 files changed, 178 insertions, 158 deletions
diff --git a/packages/taler-util/src/invariants.ts b/packages/taler-util/src/invariants.ts
index c6e9b8113..113d697c3 100644
--- a/packages/taler-util/src/invariants.ts
+++ b/packages/taler-util/src/invariants.ts
@@ -33,7 +33,7 @@ export class InvariantViolatedError extends Error {
*
* A violation of this invariant means that the database is inconsistent.
*/
-export function checkDbInvariant(b: boolean, m?: string): asserts b {
+export function checkDbInvariant(b: boolean, m: string): asserts b {
if (!b) {
if (m) {
throw Error(`BUG: database invariant failed (${m})`);
diff --git a/packages/taler-util/src/wallet-types.ts b/packages/taler-util/src/wallet-types.ts
index 9301a9723..24a48b415 100644
--- a/packages/taler-util/src/wallet-types.ts
+++ b/packages/taler-util/src/wallet-types.ts
@@ -54,7 +54,7 @@ import {
canonicalizeBaseUrl,
} from "./index.js";
import { VersionMatchResult } from "./libtool-version.js";
-import { PaytoUri } from "./payto.js";
+import { PaytoString, PaytoUri, codecForPaytoString } from "./payto.js";
import { AgeCommitmentProof } from "./taler-crypto.js";
import { TalerErrorCode } from "./taler-error-codes.js";
import {
@@ -229,11 +229,13 @@ interface GetPlanForWalletInitiatedOperation {
export interface ConvertAmountRequest {
amount: AmountString;
type: TransactionAmountMode;
+ depositPaytoUri: PaytoString;
}
export const codecForConvertAmountRequest =
buildCodecForObject<ConvertAmountRequest>()
.property("amount", codecForAmountString())
+ .property("depositPaytoUri", codecForPaytoString())
.property(
"type",
codecForEither(
diff --git a/packages/taler-wallet-core/src/backup/index.ts b/packages/taler-wallet-core/src/backup/index.ts
index 15904b470..09d5ae75d 100644
--- a/packages/taler-wallet-core/src/backup/index.ts
+++ b/packages/taler-wallet-core/src/backup/index.ts
@@ -805,9 +805,10 @@ async function backupRecoveryTheirs(
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
ConfigRecordKey.WalletBackupState,
);
- checkDbInvariant(!!backupStateEntry);
+ checkDbInvariant(!!backupStateEntry, `no backup entry`);
checkDbInvariant(
backupStateEntry.key === ConfigRecordKey.WalletBackupState,
+ `backup entry inconsistent`,
);
backupStateEntry.value.lastBackupNonce = undefined;
backupStateEntry.value.lastBackupTimestamp = undefined;
@@ -913,7 +914,10 @@ export async function provideBackupState(
},
);
if (bs) {
- checkDbInvariant(bs.key === ConfigRecordKey.WalletBackupState);
+ checkDbInvariant(
+ bs.key === ConfigRecordKey.WalletBackupState,
+ `backup entry inconsistent`,
+ );
return bs.value;
}
// We need to generate the key outside of the transaction
@@ -941,6 +945,7 @@ export async function provideBackupState(
}
checkDbInvariant(
backupStateEntry.key === ConfigRecordKey.WalletBackupState,
+ `backup entry inconsistent`,
);
return backupStateEntry.value;
});
@@ -952,7 +957,10 @@ export async function getWalletBackupState(
): Promise<WalletBackupConfState> {
const bs = await tx.config.get(ConfigRecordKey.WalletBackupState);
checkDbInvariant(!!bs, "wallet backup state should be in DB");
- checkDbInvariant(bs.key === ConfigRecordKey.WalletBackupState);
+ checkDbInvariant(
+ bs.key === ConfigRecordKey.WalletBackupState,
+ `backup entry inconsistent`,
+ );
return bs.value;
}
@@ -962,7 +970,7 @@ export async function setWalletDeviceId(
): Promise<void> {
await provideBackupState(wex);
await wex.db.runReadWriteTx({ storeNames: ["config"] }, async (tx) => {
- let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
+ const backupStateEntry: ConfigRecord | undefined = await tx.config.get(
ConfigRecordKey.WalletBackupState,
);
if (
diff --git a/packages/taler-wallet-core/src/coinSelection.ts b/packages/taler-wallet-core/src/coinSelection.ts
index a60e41ecd..db6384c93 100644
--- a/packages/taler-wallet-core/src/coinSelection.ts
+++ b/packages/taler-wallet-core/src/coinSelection.ts
@@ -691,7 +691,7 @@ export function checkAccountRestriction(
switch (myRestriction.type) {
case "deny":
return { ok: false };
- case "regex":
+ case "regex": {
const regex = new RegExp(myRestriction.payto_regex);
if (!regex.test(paytoUri)) {
return {
@@ -700,6 +700,7 @@ export function checkAccountRestriction(
hintI18n: myRestriction.human_hint_i18n,
};
}
+ }
}
}
return {
@@ -909,7 +910,7 @@ async function selectPayCandidates(
coinAvail.exchangeBaseUrl,
coinAvail.denomPubHash,
]);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `denomination of a coin is missing hash: ${coinAvail.denomPubHash}`);
if (denom.isRevoked) {
logger.trace("denom is revoked");
continue;
diff --git a/packages/taler-wallet-core/src/common.ts b/packages/taler-wallet-core/src/common.ts
index 755c46188..00d462d6f 100644
--- a/packages/taler-wallet-core/src/common.ts
+++ b/packages/taler-wallet-core/src/common.ts
@@ -121,7 +121,7 @@ export async function makeCoinAvailable(
coinRecord.exchangeBaseUrl,
coinRecord.denomPubHash,
]);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `denomination of a coin is missing hash: ${coinRecord.denomPubHash}`);
const ageRestriction = coinRecord.maxAge;
let car = await tx.coinAvailability.get([
coinRecord.exchangeBaseUrl,
@@ -175,13 +175,13 @@ export async function spendCoins(
coin.exchangeBaseUrl,
coin.denomPubHash,
);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `denomination of a coin is missing hash: ${coin.denomPubHash}`);
const coinAvailability = await tx.coinAvailability.get([
coin.exchangeBaseUrl,
coin.denomPubHash,
coin.maxAge,
]);
- checkDbInvariant(!!coinAvailability);
+ checkDbInvariant(!!coinAvailability, `age denom info is missing for ${coin.maxAge}`);
const contrib = csi.contributions[i];
if (coin.status !== CoinStatus.Fresh) {
const alloc = coin.spendAllocation;
@@ -213,7 +213,6 @@ export async function spendCoins(
amount: Amounts.stringify(remaining.amount),
coinPub: coin.coinPub,
});
- checkDbInvariant(!!coinAvailability);
if (coinAvailability.freshCoinCount === 0) {
throw Error(
`invalid coin count ${coinAvailability.freshCoinCount} in DB`,
@@ -558,6 +557,28 @@ export function getAutoRefreshExecuteThreshold(d: {
}
/**
+ * Type and schema definitions for pending tasks in the wallet.
+ *
+ * These are only used internally, and are not part of the stable public
+ * interface to the wallet.
+ */
+
+export enum PendingTaskType {
+ ExchangeUpdate = "exchange-update",
+ Purchase = "purchase",
+ Refresh = "refresh",
+ Recoup = "recoup",
+ RewardPickup = "reward-pickup",
+ Withdraw = "withdraw",
+ Deposit = "deposit",
+ Backup = "backup",
+ PeerPushDebit = "peer-push-debit",
+ PeerPullCredit = "peer-pull-credit",
+ PeerPushCredit = "peer-push-credit",
+ PeerPullDebit = "peer-pull-debit",
+}
+
+/**
* Parsed representation of task identifiers.
*/
export type ParsedTaskIdentifier =
@@ -747,28 +768,6 @@ export interface TransactionContext {
deleteTransaction(): Promise<void>;
}
-/**
- * Type and schema definitions for pending tasks in the wallet.
- *
- * These are only used internally, and are not part of the stable public
- * interface to the wallet.
- */
-
-export enum PendingTaskType {
- ExchangeUpdate = "exchange-update",
- Purchase = "purchase",
- Refresh = "refresh",
- Recoup = "recoup",
- RewardPickup = "reward-pickup",
- Withdraw = "withdraw",
- Deposit = "deposit",
- Backup = "backup",
- PeerPushDebit = "peer-push-debit",
- PeerPullCredit = "peer-pull-credit",
- PeerPushCredit = "peer-push-credit",
- PeerPullDebit = "peer-pull-debit",
-}
-
declare const __taskIdStr: unique symbol;
export type TaskIdStr = string & { [__taskIdStr]: true };
diff --git a/packages/taler-wallet-core/src/db.ts b/packages/taler-wallet-core/src/db.ts
index 640d94753..ad9b4f1cb 100644
--- a/packages/taler-wallet-core/src/db.ts
+++ b/packages/taler-wallet-core/src/db.ts
@@ -2941,6 +2941,8 @@ export interface DbDump {
};
}
+const logger = new Logger("db.ts");
+
export async function exportSingleDb(
idb: IDBFactory,
dbName: string,
@@ -3082,8 +3084,6 @@ export interface FixupDescription {
*/
export const walletDbFixups: FixupDescription[] = [];
-const logger = new Logger("db.ts");
-
export async function applyFixups(
db: DbAccess<typeof WalletStoresV1>,
): Promise<void> {
diff --git a/packages/taler-wallet-core/src/deposits.ts b/packages/taler-wallet-core/src/deposits.ts
index c4cd98d73..32b22c1ef 100644
--- a/packages/taler-wallet-core/src/deposits.ts
+++ b/packages/taler-wallet-core/src/deposits.ts
@@ -441,7 +441,7 @@ async function refundDepositGroup(
{ storeNames: ["coins"] },
async (tx) => {
const coinRecord = await tx.coins.get(coinPub);
- checkDbInvariant(!!coinRecord);
+ checkDbInvariant(!!coinRecord, `coin ${coinPub} not found in DB`);
return coinRecord.exchangeBaseUrl;
},
);
diff --git a/packages/taler-wallet-core/src/exchanges.ts b/packages/taler-wallet-core/src/exchanges.ts
index d8063d561..fa3146c38 100644
--- a/packages/taler-wallet-core/src/exchanges.ts
+++ b/packages/taler-wallet-core/src/exchanges.ts
@@ -1548,7 +1548,7 @@ export async function updateExchangeFromUrlHandler(
r.cachebreakNextUpdate = false;
await tx.exchanges.put(r);
const drRowId = await tx.exchangeDetails.put(newDetails);
- checkDbInvariant(typeof drRowId.key === "number");
+ checkDbInvariant(typeof drRowId.key === "number", "exchange details key is not a number");
for (const sk of keysInfo.signingKeys) {
// FIXME: validate signing keys before inserting them
diff --git a/packages/taler-wallet-core/src/instructedAmountConversion.ts b/packages/taler-wallet-core/src/instructedAmountConversion.ts
index 1f7d95959..5b399a0a7 100644
--- a/packages/taler-wallet-core/src/instructedAmountConversion.ts
+++ b/packages/taler-wallet-core/src/instructedAmountConversion.ts
@@ -283,7 +283,7 @@ async function getAvailableDenoms(
coinAvail.exchangeBaseUrl,
coinAvail.denomPubHash,
]);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `denomination of a coin is missing hash: ${coinAvail.denomPubHash}`);
if (denom.isRevoked || !denom.isOffered) {
continue;
}
@@ -472,7 +472,7 @@ export async function getMaxDepositAmount(
export function getMaxDepositAmountForAvailableCoins(
denoms: AvailableCoins,
currency: string,
-) {
+): AmountWithFee {
const zero = Amounts.zeroOfCurrency(currency);
if (!denoms.list.length) {
// no coins in the database
@@ -663,8 +663,13 @@ function rankDenominationForWithdrawals(
//different exchanges may have different wireFee
//ranking should take the relative contribution in the exchange
//which is (value - denomFee / fixedFee)
- const rate1 = Amounts.divmod(d1.value, d1.denomWithdraw).quotient;
- const rate2 = Amounts.divmod(d2.value, d2.denomWithdraw).quotient;
+
+ const rate1 = Amounts.isZero(d1.denomWithdraw)
+ ? Number.MIN_SAFE_INTEGER
+ : Amounts.divmod(d1.value, d1.denomWithdraw).quotient;
+ const rate2 = Amounts.isZero(d2.denomWithdraw)
+ ? Number.MIN_SAFE_INTEGER
+ : Amounts.divmod(d2.value, d2.denomWithdraw).quotient;
const contribCmp = rate1 === rate2 ? 0 : rate1 < rate2 ? 1 : -1;
return (
contribCmp ||
@@ -719,8 +724,13 @@ function rankDenominationForDeposit(
//different exchanges may have different wireFee
//ranking should take the relative contribution in the exchange
//which is (value - denomFee / fixedFee)
- const rate1 = Amounts.divmod(d1.value, d1.denomDeposit).quotient;
- const rate2 = Amounts.divmod(d2.value, d2.denomDeposit).quotient;
+ const rate1 = Amounts.isZero(d1.denomDeposit)
+ ? Number.MIN_SAFE_INTEGER
+ : Amounts.divmod(d1.value, d1.denomDeposit).quotient;
+ const rate2 = Amounts.isZero(d2.denomDeposit)
+ ? Number.MIN_SAFE_INTEGER
+ : Amounts.divmod(d2.value, d2.denomDeposit).quotient;
+
const contribCmp = rate1 === rate2 ? 0 : rate1 < rate2 ? 1 : -1;
return (
contribCmp ||
diff --git a/packages/taler-wallet-core/src/pay-merchant.ts b/packages/taler-wallet-core/src/pay-merchant.ts
index 090a11cf0..aa4919285 100644
--- a/packages/taler-wallet-core/src/pay-merchant.ts
+++ b/packages/taler-wallet-core/src/pay-merchant.ts
@@ -1028,11 +1028,11 @@ async function storeFirstPaySuccess(
purchase.merchantPaySig = payResponse.sig;
purchase.posConfirmation = payResponse.pos_confirmation;
const dl = purchase.download;
- checkDbInvariant(!!dl);
+ checkDbInvariant(!!dl, `purchase ${purchase.orderId} without ct downloaded`);
const contractTermsRecord = await tx.contractTerms.get(
dl.contractTermsHash,
);
- checkDbInvariant(!!contractTermsRecord);
+ checkDbInvariant(!!contractTermsRecord, `no contract terms found for purchase ${purchase.orderId}`);
const contractData = extractContractData(
contractTermsRecord.contractTermsRaw,
dl.contractTermsHash,
@@ -2155,7 +2155,7 @@ async function processPurchasePay(
logger.trace(`paying with session ID ${sessionId}`);
const payInfo = purchase.payInfo;
- checkDbInvariant(!!payInfo, "payInfo");
+ checkDbInvariant(!!payInfo, `purchase ${purchase.orderId} without payInfo`);
const download = await expectProposalDownload(wex, purchase);
@@ -2997,7 +2997,7 @@ async function processPurchaseAbortingRefund(
for (let i = 0; i < payCoinSelection.coinPubs.length; i++) {
const coinPub = payCoinSelection.coinPubs[i];
const coin = await tx.coins.get(coinPub);
- checkDbInvariant(!!coin, "expected coin to be present");
+ checkDbInvariant(!!coin, `coin not found for ${coinPub}`);
abortingCoins.push({
coin_pub: coinPub,
contribution: Amounts.stringify(payCoinSelection.coinContributions[i]),
diff --git a/packages/taler-wallet-core/src/pay-peer-common.ts b/packages/taler-wallet-core/src/pay-peer-common.ts
index bfd39b657..a1729ced7 100644
--- a/packages/taler-wallet-core/src/pay-peer-common.ts
+++ b/packages/taler-wallet-core/src/pay-peer-common.ts
@@ -140,10 +140,10 @@ export async function getMergeReserveInfo(
{ storeNames: ["exchanges", "reserves"] },
async (tx) => {
const ex = await tx.exchanges.get(req.exchangeBaseUrl);
- checkDbInvariant(!!ex);
+ checkDbInvariant(!!ex, `no exchange record for ${req.exchangeBaseUrl}`);
if (ex.currentMergeReserveRowId != null) {
const reserve = await tx.reserves.get(ex.currentMergeReserveRowId);
- checkDbInvariant(!!reserve);
+ checkDbInvariant(!!reserve, `reserver ${ex.currentMergeReserveRowId} missing in db`);
return reserve;
}
const reserve: ReserveRecord = {
@@ -151,7 +151,7 @@ export async function getMergeReserveInfo(
reservePub: newReservePair.pub,
};
const insertResp = await tx.reserves.put(reserve);
- checkDbInvariant(typeof insertResp.key === "number");
+ checkDbInvariant(typeof insertResp.key === "number", `reserve key is not a number`);
reserve.rowId = insertResp.key;
ex.currentMergeReserveRowId = reserve.rowId;
await tx.exchanges.put(ex);
diff --git a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
index 840c244d0..14b3eeaf0 100644
--- a/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-pull-credit.ts
@@ -1039,7 +1039,7 @@ export async function initiatePeerPullPayment(
const withdrawalGroupId = encodeCrock(getRandomBytes(32));
const mergeReserveRowId = mergeReserveInfo.rowId;
- checkDbInvariant(!!mergeReserveRowId);
+ checkDbInvariant(!!mergeReserveRowId, `merge reserve for ${exchangeBaseUrl} without rowid`);
const contractEncNonce = encodeCrock(getRandomBytes(24));
diff --git a/packages/taler-wallet-core/src/pay-peer-push-credit.ts b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
index 93f1a63a7..1476a0f4b 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-credit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-credit.ts
@@ -872,7 +872,7 @@ export async function processPeerPushCredit(
`processing peerPushCredit in state ${peerInc.status.toString(16)}`,
);
- checkDbInvariant(!!contractTerms);
+ checkDbInvariant(!!contractTerms, `not contract terms for peer push ${peerPushCreditId}`);
switch (peerInc.status) {
case PeerPushCreditStatus.PendingMergeKycRequired: {
diff --git a/packages/taler-wallet-core/src/pay-peer-push-debit.ts b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
index 6452407ff..3a936fb04 100644
--- a/packages/taler-wallet-core/src/pay-peer-push-debit.ts
+++ b/packages/taler-wallet-core/src/pay-peer-push-debit.ts
@@ -406,7 +406,7 @@ async function handlePurseCreationConflict(
const instructedAmount = Amounts.parseOrThrow(peerPushInitiation.amount);
const sel = peerPushInitiation.coinSel;
- checkDbInvariant(!!sel);
+ checkDbInvariant(!!sel, `no coin selected for peer push initiation ${peerPushInitiation.pursePub}`);
const repair: PreviousPayCoins = [];
diff --git a/packages/taler-wallet-core/src/recoup.ts b/packages/taler-wallet-core/src/recoup.ts
index 6a09f9a0e..be5731b0b 100644
--- a/packages/taler-wallet-core/src/recoup.ts
+++ b/packages/taler-wallet-core/src/recoup.ts
@@ -199,8 +199,8 @@ async function recoupRefreshCoin(
revokedCoin.exchangeBaseUrl,
revokedCoin.denomPubHash,
);
- checkDbInvariant(!!oldCoinDenom);
- checkDbInvariant(!!revokedCoinDenom);
+ checkDbInvariant(!!oldCoinDenom, `no denom for coin, hash ${oldCoin.denomPubHash}`);
+ checkDbInvariant(!!revokedCoinDenom, `no revoked denom for coin, hash ${revokedCoin.denomPubHash}`);
revokedCoin.status = CoinStatus.Dormant;
if (!revokedCoin.spendAllocation) {
// We don't know what happened to this coin
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
index 38b8b097c..f160e0731 100644
--- a/packages/taler-wallet-core/src/refresh.ts
+++ b/packages/taler-wallet-core/src/refresh.ts
@@ -387,7 +387,6 @@ async function getCoinAvailabilityForDenom(
denom: DenominationInfo,
ageRestriction: number,
): Promise<CoinAvailabilityRecord> {
- checkDbInvariant(!!denom);
let car = await tx.coinAvailability.get([
denom.exchangeBaseUrl,
denom.denomPubHash,
@@ -538,7 +537,7 @@ async function destroyRefreshSession(
denom,
oldCoin.maxAge,
);
- checkDbInvariant(car.pendingRefreshOutputCount != null);
+ checkDbInvariant(car.pendingRefreshOutputCount != null, `no pendingRefreshOutputCount for denom ${dph}`);
car.pendingRefreshOutputCount =
car.pendingRefreshOutputCount - refreshSession.newDenoms[i].count;
await tx.coinAvailability.put(car);
@@ -1251,7 +1250,7 @@ async function refreshReveal(
coin.exchangeBaseUrl,
coin.denomPubHash,
);
- checkDbInvariant(!!denomInfo);
+ checkDbInvariant(!!denomInfo, `no denom with hash ${coin.denomPubHash}`);
const car = await getCoinAvailabilityForDenom(
wex,
tx,
@@ -1261,6 +1260,7 @@ async function refreshReveal(
checkDbInvariant(
car.pendingRefreshOutputCount != null &&
car.pendingRefreshOutputCount > 0,
+ `no pendingRefreshOutputCount for denom ${coin.denomPubHash} age ${coin.maxAge}`
);
car.pendingRefreshOutputCount--;
car.freshCoinCount++;
@@ -1568,8 +1568,8 @@ async function applyRefreshToOldCoins(
coin.denomPubHash,
coin.maxAge,
]);
- checkDbInvariant(!!coinAv);
- checkDbInvariant(coinAv.freshCoinCount > 0);
+ checkDbInvariant(!!coinAv, `no denom info for ${coin.denomPubHash} age ${coin.maxAge}`);
+ checkDbInvariant(coinAv.freshCoinCount > 0, `no fresh coins for ${coin.denomPubHash}`);
coinAv.freshCoinCount--;
await tx.coinAvailability.put(coinAv);
break;
@@ -1779,7 +1779,7 @@ export async function forceRefresh(
],
},
async (tx) => {
- let coinPubs: CoinRefreshRequest[] = [];
+ const coinPubs: CoinRefreshRequest[] = [];
for (const c of req.refreshCoinSpecs) {
const coin = await tx.coins.get(c.coinPub);
if (!coin) {
@@ -1791,7 +1791,7 @@ export async function forceRefresh(
coin.exchangeBaseUrl,
coin.denomPubHash,
);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `no denom hash: ${coin.denomPubHash}`);
coinPubs.push({
coinPub: c.coinPub,
amount: c.amount ?? denom.value,
diff --git a/packages/taler-wallet-core/src/transactions.ts b/packages/taler-wallet-core/src/transactions.ts
index 1adfa5425..72aff319a 100644
--- a/packages/taler-wallet-core/src/transactions.ts
+++ b/packages/taler-wallet-core/src/transactions.ts
@@ -404,7 +404,7 @@ export async function getTransactionById(
const debit = await tx.peerPushDebit.get(parsedTx.pursePub);
if (!debit) throw Error("not found");
const ct = await tx.contractTerms.get(debit.contractTermsHash);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${parsedTx.pursePub}`);
return buildTransactionForPushPaymentDebit(
debit,
ct.contractTermsRaw,
@@ -428,7 +428,7 @@ export async function getTransactionById(
const pushInc = await tx.peerPushCredit.get(peerPushCreditId);
if (!pushInc) throw Error("not found");
const ct = await tx.contractTerms.get(pushInc.contractTermsHash);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${peerPushCreditId}`);
let wg: WithdrawalGroupRecord | undefined = undefined;
let wgOrt: OperationRetryRecord | undefined = undefined;
@@ -440,7 +440,7 @@ export async function getTransactionById(
}
}
const pushIncOpId = TaskIdentifiers.forPeerPushCredit(pushInc);
- let pushIncOrt = await tx.operationRetries.get(pushIncOpId);
+ const pushIncOrt = await tx.operationRetries.get(pushIncOpId);
return buildTransactionForPeerPushCredit(
pushInc,
@@ -468,7 +468,7 @@ export async function getTransactionById(
const pushInc = await tx.peerPullCredit.get(pursePub);
if (!pushInc) throw Error("not found");
const ct = await tx.contractTerms.get(pushInc.contractTermsHash);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${pursePub}`);
let wg: WithdrawalGroupRecord | undefined = undefined;
let wgOrt: OperationRetryRecord | undefined = undefined;
@@ -1034,8 +1034,8 @@ function buildTransactionForPurchase(
}));
const timestamp = purchaseRecord.timestampAccept;
- checkDbInvariant(!!timestamp);
- checkDbInvariant(!!purchaseRecord.payInfo);
+ checkDbInvariant(!!timestamp, `purchase ${purchaseRecord.orderId} without accepted time`);
+ checkDbInvariant(!!purchaseRecord.payInfo, `purchase ${purchaseRecord.orderId} without payinfo`);
const txState = computePayMerchantTransactionState(purchaseRecord);
return {
@@ -1175,7 +1175,7 @@ export async function getTransactions(
return;
}
const ct = await tx.contractTerms.get(pi.contractTermsHash);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${pi.pursePub}`);
transactions.push(
buildTransactionForPushPaymentDebit(pi, ct.contractTermsRaw),
);
@@ -1249,9 +1249,9 @@ export async function getTransactions(
}
}
const pushIncOpId = TaskIdentifiers.forPeerPushCredit(pi);
- let pushIncOrt = await tx.operationRetries.get(pushIncOpId);
+ const pushIncOrt = await tx.operationRetries.get(pushIncOpId);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${pi.pursePub}`);
transactions.push(
buildTransactionForPeerPushCredit(
pi,
@@ -1283,9 +1283,9 @@ export async function getTransactions(
}
}
const pushIncOpId = TaskIdentifiers.forPeerPullPaymentInitiation(pi);
- let pushIncOrt = await tx.operationRetries.get(pushIncOpId);
+ const pushIncOrt = await tx.operationRetries.get(pushIncOpId);
- checkDbInvariant(!!ct);
+ checkDbInvariant(!!ct, `no contract terms for p2p push ${pi.pursePub}`);
transactions.push(
buildTransactionForPeerPullCredit(
pi,
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 86ea7bb4c..d98106d1f 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -479,7 +479,7 @@ async function setCoinSuspended(
c.denomPubHash,
c.maxAge,
]);
- checkDbInvariant(!!coinAvailability);
+ checkDbInvariant(!!coinAvailability, `no denom info for ${c.denomPubHash} age ${c.maxAge}`);
if (suspended) {
if (c.status !== CoinStatus.Fresh) {
return;
@@ -1063,10 +1063,6 @@ async function dispatchRequestInternal(
const req = codecForPreparePayTemplateRequest().decode(payload);
return preparePayForTemplate(wex, req);
}
- case WalletApiOperation.CheckPayForTemplate: {
- const req = codecForCheckPayTemplateRequest().decode(payload);
- return checkPayForTemplate(wex, req);
- }
case WalletApiOperation.ConfirmPay: {
const req = codecForConfirmPayRequest().decode(payload);
let transactionId;
@@ -1403,7 +1399,7 @@ async function dispatchRequestInternal(
return;
}
wex.ws.exchangeCache.clear();
- checkDbInvariant(!!existingRec.id);
+ checkDbInvariant(!!existingRec.id, `no global exchange for ${j2s(key)}`);
await tx.globalCurrencyExchanges.delete(existingRec.id);
},
);
@@ -1452,7 +1448,7 @@ async function dispatchRequestInternal(
if (!existingRec) {
return;
}
- checkDbInvariant(!!existingRec.id);
+ checkDbInvariant(!!existingRec.id, `no global currency for ${j2s(key)}`);
await tx.globalCurrencyAuditors.delete(existingRec.id);
wex.ws.exchangeCache.clear();
},
diff --git a/packages/taler-wallet-core/src/withdraw.ts b/packages/taler-wallet-core/src/withdraw.ts
index 1dc4e0999..b2cecea16 100644
--- a/packages/taler-wallet-core/src/withdraw.ts
+++ b/packages/taler-wallet-core/src/withdraw.ts
@@ -965,7 +965,7 @@ async function processPlanchetGenerate(
return getDenomInfo(wex, tx, exchangeBaseUrl, denomPubHash);
},
);
- checkDbInvariant(!!denom);
+ checkDbInvariant(!!denom, `no denom info for ${denomPubHash}`);
const r = await wex.cryptoApi.createPlanchet({
denomPub: denom.denomPub,
feeWithdraw: Amounts.parseOrThrow(denom.feeWithdraw),
@@ -2314,7 +2314,7 @@ export async function getFundingPaytoUris(
withdrawalGroupId: string,
): Promise<string[]> {
const withdrawalGroup = await tx.withdrawalGroups.get(withdrawalGroupId);
- checkDbInvariant(!!withdrawalGroup);
+ checkDbInvariant(!!withdrawalGroup, `no withdrawal for ${withdrawalGroupId}`);
checkDbInvariant(
withdrawalGroup.instructedAmount !== undefined,
"can't get funding uri from uninitialized wg",
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
index 838739ad1..daba6aba4 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/index.ts
@@ -94,9 +94,9 @@ export namespace State {
currentAccount: PaytoUri;
totalFee: AmountJson;
- totalToDeposit: AmountJson;
amount: AmountFieldHandler;
+ totalToDeposit: AmountFieldHandler;
account: SelectFieldHandler;
cancelHandler: ButtonHandler;
depositHandler: ButtonHandler;
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
index 97b2ab517..b674665cf 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/state.ts
@@ -15,19 +15,18 @@
*/
import {
- AmountJson,
Amounts,
- DepositGroupFees,
KnownBankAccountsInfo,
parsePaytoUri,
PaytoUri,
stringifyPaytoUri,
+ TransactionAmountMode
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
-import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { RecursiveState } from "../../utils/index.js";
import { Props, State } from "./index.js";
@@ -83,8 +82,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
- error: alertFromError(i18n,
- i18n.str`Could not load balance information`, hook),
+ error: alertFromError(
+ i18n,
+ i18n.str`Could not load balance information`,
+ hook,
+ ),
};
}
const { accounts, balances } = hook.response;
@@ -141,21 +143,23 @@ export function useComponentState({
}
const firstAccount = accounts[0].uri;
const currentAccount = !selectedAccount ? firstAccount : selectedAccount;
-
- return () => {
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const [amount, setAmount] = useState<AmountJson>(
- initialValue ?? ({} as any),
+ const zero = Amounts.zeroOfCurrency(currency)
+ return (): State => {
+ const [instructed, setInstructed] = useState(
+ {amount: initialValue ?? zero, type: TransactionAmountMode.Raw},
);
- const amountStr = Amounts.stringify(amount);
+ const amountStr = Amounts.stringify(instructed.amount);
const depositPaytoUri = stringifyPaytoUri(currentAccount);
- // eslint-disable-next-line react-hooks/rules-of-hooks
const hook = useAsyncAsHook(async () => {
- const fee = await api.wallet.call(WalletApiOperation.PrepareDeposit, {
- amount: amountStr,
- depositPaytoUri,
- });
+ const fee = await api.wallet.call(
+ WalletApiOperation.ConvertDepositAmount,
+ {
+ amount: amountStr,
+ type: instructed.type,
+ depositPaytoUri,
+ },
+ );
return { fee };
}, [amountStr, depositPaytoUri]);
@@ -183,18 +187,16 @@ export function useComponentState({
const totalFee =
fee !== undefined
- ? Amounts.sum([fee.fees.wire, fee.fees.coin, fee.fees.refresh]).amount
+ ? Amounts.sub(fee.effectiveAmount, fee.rawAmount).amount
: Amounts.zeroOfCurrency(currency);
- const totalToDeposit =
- fee !== undefined
- ? Amounts.sub(amount, totalFee).amount
- : Amounts.zeroOfCurrency(currency);
+ const totalToDeposit = Amounts.parseOrThrow(fee.rawAmount);
+ const totalEffective = Amounts.parseOrThrow(fee.effectiveAmount);
- const isDirty = amount !== initialValue;
+ const isDirty = instructed.amount !== initialValue;
const amountError = !isDirty
? undefined
- : Amounts.cmp(balance, amount) === -1
+ : Amounts.cmp(balance, totalEffective) === -1
? `Too much, your current balance is ${Amounts.stringifyValue(balance)}`
: undefined;
@@ -207,7 +209,7 @@ export function useComponentState({
if (!currency) return;
const depositPaytoUri = stringifyPaytoUri(currentAccount);
- const amountStr = Amounts.stringify(amount);
+ const amountStr = Amounts.stringify(totalEffective);
await api.wallet.call(WalletApiOperation.CreateDepositGroup, {
amount: amountStr,
depositPaytoUri,
@@ -220,8 +222,19 @@ export function useComponentState({
error: undefined,
currency,
amount: {
- value: amount,
- onInput: pushAlertOnError(async (a) => setAmount(a)),
+ value: totalEffective,
+ onInput: pushAlertOnError(async (a) => setInstructed({
+ amount: a,
+ type: TransactionAmountMode.Effective,
+ })),
+ error: amountError,
+ },
+ totalToDeposit: {
+ value: totalToDeposit,
+ onInput: pushAlertOnError(async (a) => setInstructed({
+ amount: a,
+ type: TransactionAmountMode.Raw,
+ })),
error: amountError,
},
onAddAccount: {
@@ -244,7 +257,6 @@ export function useComponentState({
onClick: unableToDeposit ? undefined : pushAlertOnError(doSend),
},
totalFee,
- totalToDeposit,
};
};
}
@@ -269,7 +281,7 @@ export function createLabelsForBankAccount(
): { [value: string]: string } {
const initialList: Record<string, string> = {};
if (!knownBankAccounts.length) return initialList;
- return knownBankAccounts.reduce((prev, cur, i) => {
+ return knownBankAccounts.reduce((prev, cur) => {
prev[stringifyPaytoUri(cur.uri)] = cur.alias;
return prev;
}, initialList);
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
index c23f83fdd..0ed62220b 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/stories.tsx
@@ -53,7 +53,10 @@ export const WithNoAccountForIBAN = tests.createExample(ReadyView, {
onClick: nullFunction,
},
totalFee: Amounts.zeroOfCurrency("USD"),
- totalToDeposit: Amounts.parseOrThrow("USD:10"),
+ totalToDeposit: {
+ onInput:nullFunction,
+ value: Amounts.parseOrThrow("USD:10"),
+ },
// onCalculateFee: alwaysReturnFeeToOne,
});
@@ -82,7 +85,10 @@ export const WithIBANAccountTypeSelected = tests.createExample(ReadyView, {
onClick: nullFunction,
},
totalFee: Amounts.zeroOfCurrency("USD"),
- totalToDeposit: Amounts.parseOrThrow("USD:10"),
+ totalToDeposit: {
+ onInput:nullFunction,
+ value: Amounts.parseOrThrow("USD:10"),
+ },
// onCalculateFee: alwaysReturnFeeToOne,
});
@@ -111,6 +117,9 @@ export const NewBitcoinAccountTypeSelected = tests.createExample(ReadyView, {
onClick: nullFunction,
},
totalFee: Amounts.zeroOfCurrency("USD"),
- totalToDeposit: Amounts.parseOrThrow("USD:10"),
+ totalToDeposit: {
+ onInput:nullFunction,
+ value: Amounts.parseOrThrow("USD:10"),
+ },
// onCalculateFee: alwaysReturnFeeToOne,
});
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
index 157cb868a..1144095e1 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/test.ts
@@ -20,17 +20,16 @@
*/
import {
+ AmountResponse,
Amounts,
AmountString,
- DepositGroupFees,
parsePaytoUri,
- PrepareDepositResponse,
ScopeType,
- stringifyPaytoUri,
+ stringifyPaytoUri
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { expect } from "chai";
import * as tests from "@gnu-taler/web-util/testing";
+import { expect } from "chai";
import { nullFunction } from "../../mui/handlers.js";
import { createWalletApiMock } from "../../test-utils.js";
@@ -38,24 +37,14 @@ import { useComponentState } from "./state.js";
const currency = "EUR";
const amount = `${currency}:0`;
-const withoutFee = (): PrepareDepositResponse => ({
- effectiveDepositAmount: `${currency}:5` as AmountString,
- totalDepositCost: `${currency}:5` as AmountString,
- fees: {
- coin: Amounts.stringify(`${currency}:0`),
- wire: Amounts.stringify(`${currency}:0`),
- refresh: Amounts.stringify(`${currency}:0`),
- },
+const withoutFee = (value: number): AmountResponse => ({
+ effectiveAmount: `${currency}:${value}` as AmountString,
+ rawAmount: `${currency}:${value}` as AmountString,
});
-const withSomeFee = (): PrepareDepositResponse => ({
- effectiveDepositAmount: `${currency}:5` as AmountString,
- totalDepositCost: `${currency}:5` as AmountString,
- fees: {
- coin: Amounts.stringify(`${currency}:1`),
- wire: Amounts.stringify(`${currency}:1`),
- refresh: Amounts.stringify(`${currency}:1`),
- },
+const withSomeFee = (value: number, fee: number): AmountResponse => ({
+ effectiveAmount: `${currency}:${value}` as AmountString,
+ rawAmount: `${currency}:${value - fee}` as AmountString,
});
describe("DepositPage states", () => {
@@ -195,9 +184,9 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withoutFee(),
+ withoutFee(0),
);
const hookBehavior = await tests.hookBehaveLikeThis(
@@ -255,15 +244,15 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withoutFee(),
+ withoutFee(0),
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withoutFee(),
+ withoutFee(0),
);
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
@@ -345,19 +334,19 @@ describe("DepositPage states", () => {
},
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withoutFee(),
+ withoutFee(0),
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withSomeFee(),
+ withSomeFee(10,3),
);
handler.addWalletCallResponse(
- WalletApiOperation.PrepareDeposit,
+ WalletApiOperation.ConvertDepositAmount,
undefined,
- withSomeFee(),
+ withSomeFee(10,3),
);
const accountSelected = stringifyPaytoUri(ibanPayto.uri);
@@ -404,7 +393,7 @@ describe("DepositPage states", () => {
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
- expect(state.totalToDeposit).deep.eq(
+ expect(state.totalToDeposit.value).deep.eq(
Amounts.parseOrThrow(`${currency}:7`),
);
expect(state.depositHandler.onClick).not.undefined;
@@ -416,7 +405,7 @@ describe("DepositPage states", () => {
expect(state.account.value).eq(accountSelected);
expect(state.amount.value).deep.eq(Amounts.parseOrThrow("EUR:10"));
expect(state.totalFee).deep.eq(Amounts.parseOrThrow(`${currency}:3`));
- expect(state.totalToDeposit).deep.eq(
+ expect(state.totalToDeposit.value).deep.eq(
Amounts.parseOrThrow(`${currency}:7`),
);
expect(state.depositHandler.onClick).not.undefined;
diff --git a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
index 37f1c252d..b3607ebba 100644
--- a/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DepositPage/views.tsx
@@ -26,7 +26,7 @@ import { Grid } from "../../mui/Grid.js";
import { State } from "./index.js";
export function AmountOrCurrencyErrorView(
- p: State.AmountOrCurrencyError,
+ _p: State.AmountOrCurrencyError,
): VNode {
const { i18n } = useTranslationContext();
@@ -156,12 +156,7 @@ export function ReadyView(state: State.Ready): VNode {
/>
</Grid>
<Grid item xs={1}>
- <AmountField
- label={i18n.str`Net amount`}
- handler={{
- value: state.totalToDeposit,
- }}
- />
+ <AmountField label={i18n.str`Net amount`} handler={state.totalToDeposit} />
</Grid>
</Grid>
</section>
@@ -180,7 +175,7 @@ export function ReadyView(state: State.Ready): VNode {
) : (
<Button variant="contained" onClick={state.depositHandler.onClick}>
<i18n.Translate>
- Deposit&nbsp;{Amounts.stringifyValue(state.totalToDeposit)}{" "}
+ Deposit&nbsp;{Amounts.stringifyValue(state.totalToDeposit.value)}{" "}
{state.currency}
</i18n.Translate>
</Button>
diff --git a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
index 7b80977f3..b995a44d0 100644
--- a/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ManageAccount/views.tsx
@@ -130,7 +130,6 @@ export function ReadyView({
))}
</div>
<div style={{ border: "1px solid gray", padding: 8, borderRadius: 5 }}>
- --- {uri.value} ---
<p>
<CustomFieldByAccountType
type={accountType.value as AccountType}