aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/util/coinSelection.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core/src/util/coinSelection.ts')
-rw-r--r--packages/taler-wallet-core/src/util/coinSelection.ts84
1 files changed, 58 insertions, 26 deletions
diff --git a/packages/taler-wallet-core/src/util/coinSelection.ts b/packages/taler-wallet-core/src/util/coinSelection.ts
index f6d8abcd4..c5a810c4f 100644
--- a/packages/taler-wallet-core/src/util/coinSelection.ts
+++ b/packages/taler-wallet-core/src/util/coinSelection.ts
@@ -46,6 +46,7 @@ import {
PayCoinSelection,
PayMerchantInsufficientBalanceDetails,
strcmp,
+ TransactionAmountMode,
TransactionType,
} from "@gnu-taler/taler-util";
import {
@@ -818,7 +819,7 @@ function getCoinsFilter(req: GetPlanForOperationRequest): CoinsFilter {
export function calculatePlanFormAvailableCoins(
transactionType: TransactionType,
amount: AmountJson,
- mode: "effective" | "raw",
+ mode: TransactionAmountMode,
availableCoins: AvailableCoins,
) {
const operationType = getOperationType(transactionType);
@@ -828,7 +829,9 @@ export function calculatePlanFormAvailableCoins(
usableCoins = selectCoinForOperation(
operationType,
amount,
- mode === "effective" ? "net" : "gross",
+ mode === TransactionAmountMode.Effective
+ ? AmountMode.Net
+ : AmountMode.Gross,
availableCoins,
);
break;
@@ -839,11 +842,11 @@ export function calculatePlanFormAvailableCoins(
//are from that exchange
const wireFee = Object.values(availableCoins.exchanges)[0].wireFee!;
- if (mode === "effective") {
+ if (mode === TransactionAmountMode.Effective) {
usableCoins = selectCoinForOperation(
operationType,
amount,
- "gross",
+ AmountMode.Gross,
availableCoins,
);
@@ -857,7 +860,7 @@ export function calculatePlanFormAvailableCoins(
usableCoins = selectCoinForOperation(
operationType,
adjustedAmount,
- "net",
+ AmountMode.Net,
availableCoins,
);
@@ -914,6 +917,27 @@ export async function getPlanForOperation(
}
/**
+ * If the operation going to be plan subtracts
+ * or adds amount in the wallet db
+ */
+export enum OperationType {
+ Credit = "credit",
+ Debit = "debit",
+}
+
+/**
+ * How the amount should be interpreted
+ * net = without fee
+ * gross = with fee
+ *
+ * Net value is always lower than gross
+ */
+export enum AmountMode {
+ Net = "net",
+ Gross = "gross",
+}
+
+/**
*
* @param op defined which fee are we taking into consideration: deposits or withdraw
* @param limit the total amount limit of the operation
@@ -922,9 +946,9 @@ export async function getPlanForOperation(
* @returns
*/
export function selectCoinForOperation(
- op: "debit" | "credit",
+ op: OperationType,
limit: AmountJson,
- mode: "net" | "gross",
+ mode: AmountMode,
coins: AvailableCoins,
): SelectedCoins {
const result: SelectedCoins = {
@@ -951,8 +975,11 @@ export function selectCoinForOperation(
iterateDenoms: while (denomIdx < coins.list.length) {
const denom = coins.list[denomIdx];
let total =
- op === "credit" ? Number.MAX_SAFE_INTEGER : denom.totalAvailable ?? 0;
- const opFee = op === "credit" ? denom.denomWithdraw : denom.denomDeposit;
+ op === OperationType.Credit
+ ? Number.MAX_SAFE_INTEGER
+ : denom.totalAvailable ?? 0;
+ const opFee =
+ op === OperationType.Credit ? denom.denomWithdraw : denom.denomDeposit;
const contribution = Amounts.sub(denom.value, opFee).amount;
if (Amounts.isZero(contribution)) {
@@ -969,7 +996,7 @@ export function selectCoinForOperation(
contribution,
).amount;
- const progress = mode === "gross" ? nextValue : nextContribution;
+ const progress = mode === AmountMode.Gross ? nextValue : nextContribution;
if (Amounts.cmp(progress, limit) === 1) {
//the current coin is more than we need, try next denom
@@ -1008,14 +1035,15 @@ export function selectCoinForOperation(
// we made it
return result;
}
- if (op === "credit") {
+ if (op === OperationType.Credit) {
//doing withdraw there is no way to cover the gap
return result;
}
//tried all the coins but there is a gap
//doing deposit we can try refreshing coins
- const total = mode === "gross" ? result.totalValue : result.totalContribution;
+ const total =
+ mode === AmountMode.Gross ? result.totalValue : result.totalContribution;
const gap = Amounts.sub(limit, total).amount;
//about recursive calls
@@ -1027,7 +1055,7 @@ export function selectCoinForOperation(
refreshIteration: while (refreshIdx < coins.list.length) {
const d = coins.list[refreshIdx];
const denomContribution =
- mode === "gross"
+ mode === AmountMode.Gross
? Amounts.sub(d.value, d.denomRefresh).amount
: Amounts.sub(d.value, d.denomDeposit, d.denomRefresh).amount;
@@ -1038,7 +1066,7 @@ export function selectCoinForOperation(
}
const changeCost = selectCoinForOperation(
- "credit",
+ OperationType.Credit,
changeAfterDeposit,
mode,
coins,
@@ -1067,7 +1095,7 @@ export function selectCoinForOperation(
refreshIdx++;
}
if (choice) {
- if (mode === "gross") {
+ if (mode === AmountMode.Gross) {
result.totalValue = Amounts.add(result.totalValue, gap).amount;
result.totalContribution = Amounts.add(
result.totalContribution,
@@ -1096,9 +1124,9 @@ export function selectCoinForOperation(
}
type CompareCoinsFunction = (d1: CoinInfo, d2: CoinInfo) => -1 | 0 | 1;
-function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction {
+function buildRankingForCoins(op: OperationType): CompareCoinsFunction {
function getFee(d: CoinInfo) {
- return op === "credit" ? d.denomWithdraw : d.denomDeposit;
+ return op === OperationType.Credit ? d.denomWithdraw : d.denomDeposit;
}
//different exchanges may have different wireFee
//ranking should take the relative contribution in the exchange
@@ -1116,28 +1144,32 @@ function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction {
};
}
-function getOperationType(txType: TransactionType): "credit" | "debit" {
+function getOperationType(txType: TransactionType): OperationType {
const operationType =
txType === TransactionType.Withdrawal
- ? ("credit" as const)
+ ? OperationType.Credit
: txType === TransactionType.Deposit
- ? ("debit" as const)
+ ? OperationType.Debit
: undefined;
if (!operationType) {
- throw Error(`operation type ${txType} not supported`);
+ throw Error(`operation type ${txType} not yet supported`);
}
return operationType;
}
function getAmountsWithFee(
- op: "debit" | "credit",
+ op: OperationType,
value: AmountJson,
contribution: AmountJson,
details: any,
): GetPlanForOperationResponse {
return {
- rawAmount: Amounts.stringify(op === "credit" ? value : contribution),
- effectiveAmount: Amounts.stringify(op === "credit" ? contribution : value),
+ rawAmount: Amounts.stringify(
+ op === OperationType.Credit ? value : contribution,
+ ),
+ effectiveAmount: Amounts.stringify(
+ op === OperationType.Credit ? contribution : value,
+ ),
details,
};
}
@@ -1202,7 +1234,7 @@ interface CoinsFilter {
*/
async function getAvailableCoins(
ws: InternalWalletState,
- op: "credit" | "debit",
+ op: OperationType,
currency: string,
filters: CoinsFilter = {},
): Promise<AvailableCoins> {
@@ -1286,7 +1318,7 @@ async function getAvailableCoins(
let creditDeadline = AbsoluteTime.never();
let debitDeadline = AbsoluteTime.never();
//4.- filter coins restricted by age
- if (op === "credit") {
+ if (op === OperationType.Credit) {
const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
exchangeBaseUrl,
);