aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/coinSelection.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-core/src/coinSelection.test.ts')
-rw-r--r--packages/taler-wallet-core/src/coinSelection.test.ts185
1 files changed, 179 insertions, 6 deletions
diff --git a/packages/taler-wallet-core/src/coinSelection.test.ts b/packages/taler-wallet-core/src/coinSelection.test.ts
index c7cb2857e..4984552f8 100644
--- a/packages/taler-wallet-core/src/coinSelection.test.ts
+++ b/packages/taler-wallet-core/src/coinSelection.test.ts
@@ -15,17 +15,21 @@
*/
import {
AbsoluteTime,
+ AmountJson,
AmountString,
Amounts,
DenomKeyType,
+ DenominationPubKey,
Duration,
+ TalerProtocolTimestamp,
j2s,
} from "@gnu-taler/taler-util";
import test from "ava";
import {
- AvailableDenom,
+ AvailableCoinsOfDenom,
CoinSelectionTally,
emptyTallyForPeerPayment,
+ testing_getMaxDepositAmountForAvailableCoins,
testing_selectGreedy,
} from "./coinSelection.js";
@@ -42,7 +46,9 @@ const inThePast = AbsoluteTime.toProtocolTimestamp(
test("p2p: should select the coin", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:2");
- const tally = emptyTallyForPeerPayment(instructedAmount);
+ const tally = emptyTallyForPeerPayment({
+ instructedAmount,
+ });
t.log(`tally before: ${j2s(tally)}`);
const coins = testing_selectGreedy(
{
@@ -76,7 +82,9 @@ test("p2p: should select the coin", (t) => {
test("p2p: should select 3 coins", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:20");
- const tally = emptyTallyForPeerPayment(instructedAmount);
+ const tally = emptyTallyForPeerPayment({
+ instructedAmount,
+ });
const coins = testing_selectGreedy(
{
wireFeesPerExchange: {},
@@ -108,7 +116,9 @@ test("p2p: should select 3 coins", (t) => {
test("p2p: can't select since the instructed amount is too high", (t) => {
const instructedAmount = Amounts.parseOrThrow("LOCAL:60");
- const tally = emptyTallyForPeerPayment(instructedAmount);
+ const tally = emptyTallyForPeerPayment({
+ instructedAmount,
+ });
const coins = testing_selectGreedy(
{
wireFeesPerExchange: {},
@@ -138,6 +148,7 @@ test("pay: select one coin to pay with fee", (t) => {
customerWireFees: zero,
wireFeeCoveredForExchange: new Set<string>(),
lastDepositFee: zero,
+ totalDepositFees: zero,
} satisfies CoinSelectionTally;
const coins = testing_selectGreedy(
{
@@ -180,7 +191,7 @@ function createCandidates(
numAvailable: number;
fromExchange: string;
}[],
-): AvailableDenom[] {
+): AvailableCoinsOfDenom[] {
return ar.map((r, idx) => {
return {
denomPub: {
@@ -269,7 +280,9 @@ test("p2p: regression STATER", (t) => {
},
];
const instructedAmount = Amounts.parseOrThrow("STATER:1");
- const tally = emptyTallyForPeerPayment(instructedAmount);
+ const tally = emptyTallyForPeerPayment({
+ instructedAmount,
+ });
const res = testing_selectGreedy(
{
wireFeesPerExchange: {},
@@ -279,3 +292,163 @@ test("p2p: regression STATER", (t) => {
);
t.assert(!!res);
});
+
+function makeCurrencyHelper(currency: string) {
+ return (sx: TemplateStringsArray, ...vx: any[]) => {
+ const s = String.raw({ raw: sx }, ...vx);
+ return Amounts.parseOrThrow(`${currency}:${s}`);
+ };
+}
+
+type TestCoin = [AmountJson, number];
+
+const kudos = makeCurrencyHelper("kudos");
+
+function defaultFeeConfig(
+ value: AmountJson,
+ totalAvailable: number,
+): AvailableCoinsOfDenom {
+ return {
+ denomPub: undefined as any as DenominationPubKey,
+ denomPubHash: "123",
+ feeDeposit: `KUDOS:0.01`,
+ feeRefresh: `KUDOS:0.01`,
+ feeRefund: `KUDOS:0.01`,
+ feeWithdraw: `KUDOS:0.01`,
+ exchangeBaseUrl: "2",
+ maxAge: 0,
+ numAvailable: totalAvailable,
+ stampExpireDeposit: TalerProtocolTimestamp.never(),
+ stampExpireLegal: TalerProtocolTimestamp.never(),
+ stampExpireWithdraw: TalerProtocolTimestamp.never(),
+ stampStart: TalerProtocolTimestamp.never(),
+ value: Amounts.stringify(value),
+ };
+}
+
+test("deposit max 35", (t) => {
+ const coinList: TestCoin[] = [
+ [kudos`2`, 5],
+ [kudos`5`, 5],
+ ];
+ const result = testing_getMaxDepositAmountForAvailableCoins(
+ {
+ currency: "KUDOS",
+ },
+ {
+ coinAvailability: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
+ currentWireFeePerExchange: {
+ "2": kudos`0`,
+ },
+ },
+ );
+ t.is(Amounts.stringifyValue(result.rawAmount), "34.9");
+ t.is(Amounts.stringifyValue(result.effectiveAmount), "35");
+});
+
+test("deposit max 35 with wirefee", (t) => {
+ const coinList: TestCoin[] = [
+ [kudos`2`, 5],
+ [kudos`5`, 5],
+ ];
+ const result = testing_getMaxDepositAmountForAvailableCoins(
+ {
+ currency: "KUDOS",
+ },
+ {
+ coinAvailability: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
+ currentWireFeePerExchange: {
+ "2": kudos`1`,
+ },
+ },
+ );
+ t.is(Amounts.stringifyValue(result.rawAmount), "33.9");
+ t.is(Amounts.stringifyValue(result.effectiveAmount), "35");
+});
+
+test("deposit max repeated denom", (t) => {
+ const coinList: TestCoin[] = [
+ [kudos`2`, 1],
+ [kudos`2`, 1],
+ [kudos`5`, 1],
+ ];
+ const result = testing_getMaxDepositAmountForAvailableCoins(
+ {
+ currency: "KUDOS",
+ },
+ {
+ coinAvailability: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
+ currentWireFeePerExchange: {
+ "2": kudos`0`,
+ },
+ },
+ );
+ t.is(Amounts.stringifyValue(result.rawAmount), "8.97");
+ t.is(Amounts.stringifyValue(result.effectiveAmount), "9");
+});
+
+test("demo: deposit max after withdraw raw 25", (t) => {
+ const coinList: TestCoin[] = [
+ [kudos`0.1`, 8],
+ [kudos`1`, 0],
+ [kudos`2`, 2],
+ [kudos`5`, 0],
+ [kudos`10`, 2],
+ ];
+ const result = testing_getMaxDepositAmountForAvailableCoins(
+ {
+ currency: "KUDOS",
+ },
+ {
+ coinAvailability: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
+ currentWireFeePerExchange: {
+ "2": kudos`0.01`,
+ },
+ },
+ );
+ t.is(Amounts.stringifyValue(result.effectiveAmount), "24.8");
+ t.is(Amounts.stringifyValue(result.rawAmount), "24.67");
+
+ // 8 x 0.1
+ // 2 x 0.2
+ // 2 x 10.0
+ // total effective 24.8
+ // deposit fee 12 x 0.01 = 0.12
+ // wire fee 0.01
+ // total raw: 24.8 - 0.13 = 24.67
+
+ // current wallet impl fee 0.14
+});
+
+test("demo: deposit max after withdraw raw 13", (t) => {
+ const coinList: TestCoin[] = [
+ [kudos`0.1`, 8],
+ [kudos`1`, 0],
+ [kudos`2`, 1],
+ [kudos`5`, 0],
+ [kudos`10`, 1],
+ ];
+ const result = testing_getMaxDepositAmountForAvailableCoins(
+ {
+ currency: "KUDOS",
+ },
+ {
+ coinAvailability: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
+ currentWireFeePerExchange: {
+ "2": kudos`0.01`,
+ },
+ },
+ );
+ t.is(Amounts.stringifyValue(result.effectiveAmount), "12.8");
+ t.is(Amounts.stringifyValue(result.rawAmount), "12.69");
+
+ // 8 x 0.1
+ // 1 x 0.2
+ // 1 x 10.0
+ // total effective 12.8
+ // deposit fee 10 x 0.01 = 0.10
+ // wire fee 0.01
+ // total raw: 12.8 - 0.11 = 12.69
+
+ // current wallet impl fee 0.14
+});