wallet-core: towards DD37 for deposits

This commit is contained in:
Florian Dold 2023-04-05 17:38:34 +02:00
parent 8eee38d559
commit 2823b1cdf4
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
12 changed files with 204 additions and 296 deletions

View File

@ -60,10 +60,4 @@ export async function runDepositTest(t: GlobalTestState) {
// The raw amount is what ends up on the bank account, which includes // The raw amount is what ends up on the bank account, which includes
// deposit and wire fees. // deposit and wire fees.
t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79"); t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79");
const trackResult = wallet.client.call(WalletApiOperation.TrackDepositGroup, {
depositGroupId,
});
console.log(JSON.stringify(trackResult, undefined, 2));
} }

View File

@ -104,6 +104,8 @@ export interface HttpRequestLibrary {
* Make an HTTP GET request. * Make an HTTP GET request.
* *
* FIXME: Get rid of this, we want the API surface to be minimal. * FIXME: Get rid of this, we want the API surface to be minimal.
*
* @deprecated use fetch instead
*/ */
get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse>; get(url: string, opt?: HttpRequestOptions): Promise<HttpResponse>;
@ -111,6 +113,8 @@ export interface HttpRequestLibrary {
* Make an HTTP POST request with a JSON body. * Make an HTTP POST request with a JSON body.
* *
* FIXME: Get rid of this, we want the API surface to be minimal. * FIXME: Get rid of this, we want the API surface to be minimal.
*
* @deprecated use fetch instead
*/ */
postJson( postJson(
url: string, url: string,

View File

@ -59,6 +59,11 @@ export enum ExtendedStatus {
KycRequired = "kyc-required", KycRequired = "kyc-required",
} }
export interface TransactionStateInfo {
txState: TransactionState;
txSubstate: TransactionSubstate;
}
export interface TransactionsRequest { export interface TransactionsRequest {
/** /**
* return only transactions in the given currency * return only transactions in the given currency
@ -76,6 +81,32 @@ export interface TransactionsRequest {
includeRefreshes?: boolean; includeRefreshes?: boolean;
} }
export enum TransactionState {
// No state, only used when reporting transitions into the initial state
None = "none",
Pending = "pending",
Done = "done",
Aborting = "aborting",
Aborted = "aborted",
Suspended = "suspended",
Failed = "failed",
// Only used for the notification, never in the transaction history
Deleted = "deleted",
// Placeholder until D37 is fully implemented
Unknown = "unknown",
}
export enum TransactionSubstate {
// Placeholder until D37 is fully implemented
Unknown = "unknown",
// No substate
None = "none",
DepositPendingInitial = "initial",
DepositKycRequired = "kyc-required",
DepositPendingTrack = "track",
DepositAbortingRefresh = "refresh",
}
export interface TransactionsResponse { export interface TransactionsResponse {
// a list of past and pending transactions sorted by pending, timestamp and transactionId. // a list of past and pending transactions sorted by pending, timestamp and transactionId.
// In case two events are both pending and have the same timestamp, // In case two events are both pending and have the same timestamp,
@ -95,6 +126,13 @@ export interface TransactionCommon {
// main timestamp of the transaction // main timestamp of the transaction
timestamp: TalerProtocolTimestamp; timestamp: TalerProtocolTimestamp;
txState: TransactionState;
txSubstate: TransactionSubstate;
/**
* @deprecated in favor of statusMajor and statusMinor
*/
extendedStatus: ExtendedStatus; extendedStatus: ExtendedStatus;
/** /**

View File

@ -1228,8 +1228,7 @@ export const codecForTestPayArgs = (): Codec<TestPayArgs> =>
export interface IntegrationTestArgs { export interface IntegrationTestArgs {
exchangeBaseUrl: string; exchangeBaseUrl: string;
bankBaseUrl: string; bankAccessApiBaseUrl: string;
bankAccessApiBaseUrl?: string;
merchantBaseUrl: string; merchantBaseUrl: string;
merchantAuthToken?: string; merchantAuthToken?: string;
amountToWithdraw: string; amountToWithdraw: string;
@ -1239,12 +1238,11 @@ export interface IntegrationTestArgs {
export const codecForIntegrationTestArgs = (): Codec<IntegrationTestArgs> => export const codecForIntegrationTestArgs = (): Codec<IntegrationTestArgs> =>
buildCodecForObject<IntegrationTestArgs>() buildCodecForObject<IntegrationTestArgs>()
.property("exchangeBaseUrl", codecForString()) .property("exchangeBaseUrl", codecForString())
.property("bankBaseUrl", codecForString())
.property("merchantBaseUrl", codecForString()) .property("merchantBaseUrl", codecForString())
.property("merchantAuthToken", codecOptional(codecForString())) .property("merchantAuthToken", codecOptional(codecForString()))
.property("amountToSpend", codecForAmountString()) .property("amountToSpend", codecForAmountString())
.property("amountToWithdraw", codecForAmountString()) .property("amountToWithdraw", codecForAmountString())
.property("bankAccessApiBaseUrl", codecOptional(codecForAmountString())) .property("bankAccessApiBaseUrl", codecForAmountString())
.build("IntegrationTestArgs"); .build("IntegrationTestArgs");
export interface IntegrationTestV2Args { export interface IntegrationTestV2Args {
@ -1499,11 +1497,10 @@ export interface CoreApiResponseError {
export interface WithdrawTestBalanceRequest { export interface WithdrawTestBalanceRequest {
amount: string; amount: string;
bankBaseUrl: string;
/** /**
* Bank access API base URL. Defaults to the bankBaseUrl. * Bank access API base URL.
*/ */
bankAccessApiBaseUrl?: string; bankAccessApiBaseUrl: string;
exchangeBaseUrl: string; exchangeBaseUrl: string;
forcedDenomSel?: ForcedDenomSel; forcedDenomSel?: ForcedDenomSel;
} }
@ -1580,10 +1577,9 @@ export const codecForWithdrawTestBalance =
(): Codec<WithdrawTestBalanceRequest> => (): Codec<WithdrawTestBalanceRequest> =>
buildCodecForObject<WithdrawTestBalanceRequest>() buildCodecForObject<WithdrawTestBalanceRequest>()
.property("amount", codecForString()) .property("amount", codecForString())
.property("bankBaseUrl", codecForString())
.property("exchangeBaseUrl", codecForString()) .property("exchangeBaseUrl", codecForString())
.property("forcedDenomSel", codecForAny()) .property("forcedDenomSel", codecForAny())
.property("bankAccessApiBaseUrl", codecOptional(codecForString())) .property("bankAccessApiBaseUrl", codecForString())
.build("WithdrawTestBalanceRequest"); .build("WithdrawTestBalanceRequest");
export interface ApplyRefundResponse { export interface ApplyRefundResponse {
@ -1702,11 +1698,6 @@ export const codecForAbortTransaction = (): Codec<AbortTransactionRequest> =>
.property("forceImmediateAbort", codecOptional(codecForBoolean())) .property("forceImmediateAbort", codecOptional(codecForBoolean()))
.build("AbortTransactionRequest"); .build("AbortTransactionRequest");
export interface GetFeeForDepositRequest {
depositPaytoUri: string;
amount: AmountString;
}
export interface DepositGroupFees { export interface DepositGroupFees {
coin: AmountString; coin: AmountString;
wire: AmountString; wire: AmountString;
@ -1718,12 +1709,6 @@ export interface CreateDepositGroupRequest {
amount: AmountString; amount: AmountString;
} }
export const codecForGetFeeForDeposit = (): Codec<GetFeeForDepositRequest> =>
buildCodecForObject<GetFeeForDepositRequest>()
.property("amount", codecForAmountString())
.property("depositPaytoUri", codecForString())
.build("GetFeeForDepositRequest");
export interface PrepareDepositRequest { export interface PrepareDepositRequest {
depositPaytoUri: string; depositPaytoUri: string;
amount: AmountString; amount: AmountString;
@ -1751,20 +1736,6 @@ export interface CreateDepositGroupResponse {
transactionId: string; transactionId: string;
} }
export interface TrackDepositGroupRequest {
depositGroupId: string;
}
export interface TrackDepositGroupResponse {
responses: TrackTransaction[];
}
export const codecForTrackDepositGroupRequest =
(): Codec<TrackDepositGroupRequest> =>
buildCodecForObject<TrackDepositGroupRequest>()
.property("depositGroupId", codecForAmountString())
.build("TrackDepositGroupRequest");
export interface WithdrawUriInfoResponse { export interface WithdrawUriInfoResponse {
amount: AmountString; amount: AmountString;
defaultExchangeBaseUrl?: string; defaultExchangeBaseUrl?: string;

View File

@ -37,7 +37,6 @@ import {
setDangerousTimetravel, setDangerousTimetravel,
setGlobalLogLevelFromString, setGlobalLogLevelFromString,
summarizeTalerErrorDetail, summarizeTalerErrorDetail,
TalerProtocolTimestamp,
TalerUriType, TalerUriType,
WalletNotification, WalletNotification,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
@ -928,22 +927,6 @@ depositCli
}); });
}); });
// FIXME: should probably be removed
depositCli
.subcommand("trackDepositArgs", "track")
.requiredArgument("depositGroupId", clk.STRING)
.action(async (args) => {
await withWallet(args, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.TrackDepositGroup,
{
depositGroupId: args.trackDepositArgs.depositGroupId,
},
);
console.log(JSON.stringify(resp, undefined, 2));
});
});
const peerCli = walletCli.subcommand("peerArgs", "p2p", { const peerCli = walletCli.subcommand("peerArgs", "p2p", {
help: "Subcommands for peer-to-peer payments.", help: "Subcommands for peer-to-peer payments.",
}); });
@ -1244,7 +1227,6 @@ advancedCli
await wallet.client.call(WalletApiOperation.RunIntegrationTest, { await wallet.client.call(WalletApiOperation.RunIntegrationTest, {
amountToSpend: "TESTKUDOS:1", amountToSpend: "TESTKUDOS:1",
amountToWithdraw: "TESTKUDOS:3", amountToWithdraw: "TESTKUDOS:3",
bankBaseUrl: "http://localhost:8082/",
bankAccessApiBaseUrl: "http://localhost:8082/taler-bank-access/", bankAccessApiBaseUrl: "http://localhost:8082/taler-bank-access/",
exchangeBaseUrl: "http://localhost:8081/", exchangeBaseUrl: "http://localhost:8081/",
merchantBaseUrl: "http://localhost:8083/", merchantBaseUrl: "http://localhost:8083/",
@ -1502,6 +1484,17 @@ testCli
}); });
}); });
testCli.subcommand("withdrawKudos", "withdraw-kudos").action(async (args) => {
await withWallet(args, async (wallet) => {
await wallet.client.call(WalletApiOperation.WithdrawTestBalance, {
amount: "KUDOS:50",
bankAccessApiBaseUrl:
"https://bank.demo.taler.net/demobanks/default/access-api/",
exchangeBaseUrl: "https://exchange.demo.taler.net/",
});
});
});
class PerfTimer { class PerfTimer {
tStarted: bigint | undefined; tStarted: bigint | undefined;
tSum = BigInt(0); tSum = BigInt(0);

View File

@ -858,6 +858,13 @@ export enum RefreshOperationStatus {
FinishedWithError = 51 /* DORMANT_START + 1 */, FinishedWithError = 51 /* DORMANT_START + 1 */,
} }
export enum DepositGroupOperationStatus {
Finished = 50 /* DORMANT_START */,
Failed = 51 /* DORMANT_START + 1 */,
Pending = 10 /* ACTIVE_START */,
AbortingWithRefresh = 11 /* ACTIVE_START + 1 */,
}
export enum TransactionStatus { export enum TransactionStatus {
Unknown = 10, Unknown = 10,
Accepted = 20, Accepted = 20,
@ -1662,6 +1669,11 @@ export interface DepositGroupRecord {
totalPayCost: AmountString; totalPayCost: AmountString;
/**
* The counterparty effective deposit amount.
*
* FIXME: If possible, rename to counterpartyEffectiveDepositAmount.
*/
effectiveDepositAmount: AmountString; effectiveDepositAmount: AmountString;
depositedPerCoin: boolean[]; depositedPerCoin: boolean[];

View File

@ -363,6 +363,8 @@ export enum TombstoneTag {
/** /**
* Create an event ID from the type and the primary key for the event. * Create an event ID from the type and the primary key for the event.
*
* @deprecated use constructTransactionIdentifier instead
*/ */
export function makeTransactionId( export function makeTransactionId(
type: TransactionType, type: TransactionType,

View File

@ -33,7 +33,6 @@ import {
durationFromSpec, durationFromSpec,
encodeCrock, encodeCrock,
ExchangeDepositRequest, ExchangeDepositRequest,
GetFeeForDepositRequest,
getRandomBytes, getRandomBytes,
hashTruncate32, hashTruncate32,
hashWire, hashWire,
@ -41,7 +40,6 @@ import {
j2s, j2s,
Logger, Logger,
MerchantContractTerms, MerchantContractTerms,
NotificationType,
parsePaytoUri, parsePaytoUri,
PayCoinSelection, PayCoinSelection,
PrepareDepositRequest, PrepareDepositRequest,
@ -50,18 +48,16 @@ import {
stringToBytes, stringToBytes,
TalerErrorCode, TalerErrorCode,
TalerProtocolTimestamp, TalerProtocolTimestamp,
TrackDepositGroupRequest,
TrackDepositGroupResponse,
TrackTransaction, TrackTransaction,
TrackTransactionWired, TransactionState,
TransactionStateInfo,
TransactionSubstate,
TransactionType, TransactionType,
URL, URL,
WireFee, WireFee,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
DenominationRecord,
DepositGroupRecord, DepositGroupRecord,
ExchangeDetailsRecord,
OperationStatus, OperationStatus,
TransactionStatus, TransactionStatus,
} from "../db.js"; } from "../db.js";
@ -77,7 +73,6 @@ import {
generateDepositPermissions, generateDepositPermissions,
getTotalPaymentCost, getTotalPaymentCost,
} from "./pay-merchant.js"; } from "./pay-merchant.js";
import { getTotalRefreshCost } from "./refresh.js";
import { selectPayCoinsNew } from "../util/coinSelection.js"; import { selectPayCoinsNew } from "../util/coinSelection.js";
/** /**
@ -85,7 +80,81 @@ import { selectPayCoinsNew } from "../util/coinSelection.js";
*/ */
const logger = new Logger("deposits.ts"); const logger = new Logger("deposits.ts");
export async function checkDepositKycStatus( export async function computeDepositTransactionStatus(
ws: InternalWalletState,
dg: DepositGroupRecord,
): Promise<TransactionStateInfo> {
switch (dg.operationStatus) {
case OperationStatus.Finished: {
return {
txState: TransactionState.Done,
txSubstate: TransactionSubstate.None,
};
}
case OperationStatus.Pending: {
const numTotal = dg.payCoinSelection.coinPubs.length;
let numDeposited = 0;
let numKycRequired = 0;
let numWired = 0;
for (let i = 0; i < numTotal; i++) {
if (dg.depositedPerCoin[i]) {
numDeposited++;
}
switch (dg.transactionPerCoin[i]) {
case TransactionStatus.KycRequired:
numKycRequired++;
break;
case TransactionStatus.Wired:
numWired++;
break;
}
}
if (numKycRequired > 0) {
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositKycRequired,
};
}
if (numDeposited == numTotal) {
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositPendingTrack,
};
}
return {
txState: TransactionState.Pending,
txSubstate: TransactionSubstate.DepositPendingInitial,
};
}
default:
throw Error("unexpected deposit group state");
}
}
export async function suspendDepositGroup(
ws: InternalWalletState,
depositGroupId: string,
): Promise<void> {
throw Error("not implemented");
}
export async function abortDepositGroup(
ws: InternalWalletState,
depositGroupId: string,
): Promise<void> {
throw Error("not implemented");
}
/**
* Check KYC status with the exchange, throw an appropriate exception when KYC is required.
*
* FIXME: Why does this throw an exception when KYC is required?
* Should we not return some proper result record here?
*/
async function checkDepositKycStatus(
ws: InternalWalletState, ws: InternalWalletState,
exchangeUrl: string, exchangeUrl: string,
kycInfo: KycPendingInfo, kycInfo: KycPendingInfo,
@ -119,7 +188,7 @@ export async function checkDepositKycStatus(
} }
/** /**
* @see {processDepositGroup} * Process a deposit group that is not in its final state yet.
*/ */
export async function processDepositGroup( export async function processDepositGroup(
ws: InternalWalletState, ws: InternalWalletState,
@ -181,7 +250,9 @@ export async function processDepositGroup(
options.cancellationToken?.throwIfCancelled(); options.cancellationToken?.throwIfCancelled();
const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url); const url = new URL(`coins/${perm.coin_pub}/deposit`, perm.exchange_url);
logger.info(`depositing to ${url}`); logger.info(`depositing to ${url}`);
const httpResp = await ws.http.postJson(url.href, requestBody, { const httpResp = await ws.http.fetch(url.href, {
method: "POST",
body: requestBody,
cancellationToken: options.cancellationToken, cancellationToken: options.cancellationToken,
}); });
await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess()); await readSuccessResponseJsonOrThrow(httpResp, codecForDepositSuccess());
@ -198,7 +269,6 @@ export async function processDepositGroup(
} }
| undefined; | undefined;
let signature: string | undefined;
if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) { if (depositGroup.transactionPerCoin[i] !== TransactionStatus.Wired) {
const track = await trackDepositPermission(ws, depositGroup, perm); const track = await trackDepositPermission(ws, depositGroup, perm);
@ -347,39 +417,6 @@ async function getExchangeWireFee(
return fee; return fee;
} }
export async function trackDepositGroup(
ws: InternalWalletState,
req: TrackDepositGroupRequest,
): Promise<TrackDepositGroupResponse> {
const responses: TrackTransaction[] = [];
const depositGroup = await ws.db
.mktx((x) => [x.depositGroups])
.runReadOnly(async (tx) => {
return tx.depositGroups.get(req.depositGroupId);
});
if (!depositGroup) {
throw Error("deposit group not found");
}
const contractData = extractContractData(
depositGroup.contractTermsRaw,
depositGroup.contractTermsHash,
"",
);
const depositPermissions = await generateDepositPermissions(
ws,
depositGroup.payCoinSelection,
contractData,
);
for (const dp of depositPermissions) {
const track = await trackDepositPermission(ws, depositGroup, dp);
responses.push(track);
}
return { responses };
}
async function trackDepositPermission( async function trackDepositPermission(
ws: InternalWalletState, ws: InternalWalletState,
depositGroup: DepositGroupRecord, depositGroup: DepositGroupRecord,
@ -423,66 +460,10 @@ async function trackDepositPermission(
} }
} }
export async function getFeeForDeposit( /**
ws: InternalWalletState, * FIXME: This should be renamed to checkDepositGroup,
req: GetFeeForDepositRequest, * as it doesn't prepare anything
): Promise<DepositGroupFees> { */
const p = parsePaytoUri(req.depositPaytoUri);
if (!p) {
throw Error("invalid payto URI");
}
const amount = Amounts.parseOrThrow(req.amount);
const exchangeInfos: { url: string; master_pub: string }[] = [];
await ws.db
.mktx((x) => [x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
const allExchanges = await tx.exchanges.iter().toArray();
for (const e of allExchanges) {
const details = await getExchangeDetails(tx, e.baseUrl);
if (!details || amount.currency !== details.currency) {
continue;
}
exchangeInfos.push({
master_pub: details.masterPublicKey,
url: e.baseUrl,
});
}
});
const payCoinSel = await selectPayCoinsNew(ws, {
auditors: [],
exchanges: Object.values(exchangeInfos).map((v) => ({
exchangeBaseUrl: v.url,
exchangePub: v.master_pub,
})),
wireMethod: p.targetType,
contractTermsAmount: Amounts.parseOrThrow(req.amount),
depositFeeLimit: Amounts.parseOrThrow(req.amount),
wireFeeAmortization: 1,
wireFeeLimit: Amounts.parseOrThrow(req.amount),
prevPayCoins: [],
});
if (payCoinSel.type !== "success") {
throw TalerError.fromDetail(
TalerErrorCode.WALLET_DEPOSIT_GROUP_INSUFFICIENT_BALANCE,
{
insufficientBalanceDetails: payCoinSel.insufficientBalanceDetails,
},
);
}
return await getTotalFeesForDepositAmount(
ws,
p.targetType,
amount,
payCoinSel.coinSel,
);
}
export async function prepareDepositGroup( export async function prepareDepositGroup(
ws: InternalWalletState, ws: InternalWalletState,
req: PrepareDepositRequest, req: PrepareDepositRequest,
@ -569,7 +550,7 @@ export async function prepareDepositGroup(
const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel.coinSel); const totalDepositCost = await getTotalPaymentCost(ws, payCoinSel.coinSel);
const effectiveDepositAmount = await getEffectiveDepositAmount( const effectiveDepositAmount = await getCounterpartyEffectiveDepositAmount(
ws, ws,
p.targetType, p.targetType,
payCoinSel.coinSel, payCoinSel.coinSel,
@ -674,11 +655,12 @@ export async function createDepositGroup(
const depositGroupId = encodeCrock(getRandomBytes(32)); const depositGroupId = encodeCrock(getRandomBytes(32));
const effectiveDepositAmount = await getEffectiveDepositAmount( const countarpartyEffectiveDepositAmount =
ws, await getCounterpartyEffectiveDepositAmount(
p.targetType, ws,
payCoinSel.coinSel, p.targetType,
); payCoinSel.coinSel,
);
const depositGroup: DepositGroupRecord = { const depositGroup: DepositGroupRecord = {
contractTermsHash, contractTermsHash,
@ -697,7 +679,9 @@ export async function createDepositGroup(
merchantPriv: merchantPair.priv, merchantPriv: merchantPair.priv,
merchantPub: merchantPair.pub, merchantPub: merchantPair.pub,
totalPayCost: Amounts.stringify(totalDepositCost), totalPayCost: Amounts.stringify(totalDepositCost),
effectiveDepositAmount: Amounts.stringify(effectiveDepositAmount), effectiveDepositAmount: Amounts.stringify(
countarpartyEffectiveDepositAmount,
),
wire: { wire: {
payto_uri: req.depositPaytoUri, payto_uri: req.depositPaytoUri,
salt: wireSalt, salt: wireSalt,
@ -733,10 +717,10 @@ export async function createDepositGroup(
} }
/** /**
* Get the amount that will be deposited on the merchant's bank * Get the amount that will be deposited on the users bank
* account, not considering aggregation. * account after depositing, not considering aggregation.
*/ */
export async function getEffectiveDepositAmount( export async function getCounterpartyEffectiveDepositAmount(
ws: InternalWalletState, ws: InternalWalletState,
wireType: string, wireType: string,
pcs: PayCoinSelection, pcs: PayCoinSelection,
@ -790,83 +774,3 @@ export async function getEffectiveDepositAmount(
}); });
return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount; return Amounts.sub(Amounts.sum(amt).amount, Amounts.sum(fees).amount).amount;
} }
/**
* Get the fee amount that will be charged when trying to deposit the
* specified amount using the selected coins and the wire method.
*/
export async function getTotalFeesForDepositAmount(
ws: InternalWalletState,
wireType: string,
total: AmountJson,
pcs: PayCoinSelection,
): Promise<DepositGroupFees> {
const wireFee: AmountJson[] = [];
const coinFee: AmountJson[] = [];
const refreshFee: AmountJson[] = [];
const exchangeSet: Set<string> = new Set();
await ws.db
.mktx((x) => [x.coins, x.denominations, x.exchanges, x.exchangeDetails])
.runReadOnly(async (tx) => {
for (let i = 0; i < pcs.coinPubs.length; i++) {
const coin = await tx.coins.get(pcs.coinPubs[i]);
if (!coin) {
throw Error("can't calculate deposit amount, coin not found");
}
const denom = await ws.getDenomInfo(
ws,
tx,
coin.exchangeBaseUrl,
coin.denomPubHash,
);
if (!denom) {
throw Error("can't find denomination to calculate deposit amount");
}
coinFee.push(Amounts.parseOrThrow(denom.feeDeposit));
exchangeSet.add(coin.exchangeBaseUrl);
const allDenoms = await tx.denominations.indexes.byExchangeBaseUrl
.iter(coin.exchangeBaseUrl)
.filter((x) =>
Amounts.isSameCurrency(
DenominationRecord.getValue(x),
pcs.coinContributions[i],
),
);
const amountLeft = Amounts.sub(
denom.value,
pcs.coinContributions[i],
).amount;
const refreshCost = getTotalRefreshCost(allDenoms, denom, amountLeft);
refreshFee.push(refreshCost);
}
for (const exchangeUrl of exchangeSet.values()) {
const exchangeDetails = await getExchangeDetails(tx, exchangeUrl);
if (!exchangeDetails) {
continue;
}
const fee = exchangeDetails.wireInfo.feesForType[wireType]?.find(
(x) => {
return AbsoluteTime.isBetween(
AbsoluteTime.now(),
AbsoluteTime.fromTimestamp(x.startStamp),
AbsoluteTime.fromTimestamp(x.endStamp),
);
},
)?.wireFee;
if (fee) {
wireFee.push(Amounts.parseOrThrow(fee));
}
}
});
return {
coin: Amounts.stringify(Amounts.sumOrZero(total.currency, coinFee).amount),
wire: Amounts.stringify(Amounts.sumOrZero(total.currency, wireFee).amount),
refresh: Amounts.stringify(
Amounts.sumOrZero(total.currency, refreshFee).amount,
),
};
}

View File

@ -106,7 +106,7 @@ export async function withdrawTestBalance(
): Promise<void> { ): Promise<void> {
const amount = req.amount; const amount = req.amount;
const exchangeBaseUrl = req.exchangeBaseUrl; const exchangeBaseUrl = req.exchangeBaseUrl;
const bankAccessApiBaseUrl = req.bankAccessApiBaseUrl ?? req.bankBaseUrl; const bankAccessApiBaseUrl = req.bankAccessApiBaseUrl;
logger.trace( logger.trace(
`Registered bank user, bank access base url ${bankAccessApiBaseUrl}`, `Registered bank user, bank access base url ${bankAccessApiBaseUrl}`,
@ -364,8 +364,7 @@ export async function runIntegrationTest(
logger.info("withdrawing test balance"); logger.info("withdrawing test balance");
await withdrawTestBalance(ws, { await withdrawTestBalance(ws, {
amount: args.amountToWithdraw, amount: args.amountToWithdraw,
bankBaseUrl: args.bankBaseUrl, bankAccessApiBaseUrl: args.bankAccessApiBaseUrl,
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl ?? args.bankBaseUrl,
exchangeBaseUrl: args.exchangeBaseUrl, exchangeBaseUrl: args.exchangeBaseUrl,
}); });
await ws.runUntilDone(); await ws.runUntilDone();
@ -393,8 +392,7 @@ export async function runIntegrationTest(
await withdrawTestBalance(ws, { await withdrawTestBalance(ws, {
amount: Amounts.stringify(withdrawAmountTwo), amount: Amounts.stringify(withdrawAmountTwo),
bankBaseUrl: args.bankBaseUrl, bankAccessApiBaseUrl: args.bankAccessApiBaseUrl,
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl ?? args.bankBaseUrl,
exchangeBaseUrl: args.exchangeBaseUrl, exchangeBaseUrl: args.exchangeBaseUrl,
}); });
@ -457,7 +455,6 @@ export async function runIntegrationTest2(
logger.info("withdrawing test balance"); logger.info("withdrawing test balance");
await withdrawTestBalance(ws, { await withdrawTestBalance(ws, {
amount: Amounts.stringify(amountToWithdraw), amount: Amounts.stringify(amountToWithdraw),
bankBaseUrl: args.bankAccessApiBaseUrl /* FIXME: not necessary */,
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl, bankAccessApiBaseUrl: args.bankAccessApiBaseUrl,
exchangeBaseUrl: args.exchangeBaseUrl, exchangeBaseUrl: args.exchangeBaseUrl,
}); });
@ -491,7 +488,6 @@ export async function runIntegrationTest2(
await withdrawTestBalance(ws, { await withdrawTestBalance(ws, {
amount: Amounts.stringify(withdrawAmountTwo), amount: Amounts.stringify(withdrawAmountTwo),
bankBaseUrl: args.bankAccessApiBaseUrl /* FIXME: not necessary */,
bankAccessApiBaseUrl: args.bankAccessApiBaseUrl, bankAccessApiBaseUrl: args.bankAccessApiBaseUrl,
exchangeBaseUrl: args.exchangeBaseUrl, exchangeBaseUrl: args.exchangeBaseUrl,
}); });

View File

@ -36,6 +36,8 @@ import {
TransactionByIdRequest, TransactionByIdRequest,
TransactionsRequest, TransactionsRequest,
TransactionsResponse, TransactionsResponse,
TransactionState,
TransactionSubstate,
TransactionType, TransactionType,
WithdrawalType, WithdrawalType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
@ -429,6 +431,8 @@ function buildTransactionForPushPaymentDebit(
): Transaction { ): Transaction {
return { return {
type: TransactionType.PeerPushDebit, type: TransactionType.PeerPushDebit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: pi.totalCost, amountEffective: pi.totalCost,
amountRaw: pi.amount, amountRaw: pi.amount,
exchangeBaseUrl: pi.exchangeBaseUrl, exchangeBaseUrl: pi.exchangeBaseUrl,
@ -461,6 +465,8 @@ function buildTransactionForPullPaymentDebit(
): Transaction { ): Transaction {
return { return {
type: TransactionType.PeerPullDebit, type: TransactionType.PeerPullDebit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: pi.coinSel?.totalCost amountEffective: pi.coinSel?.totalCost
? pi.coinSel?.totalCost ? pi.coinSel?.totalCost
: Amounts.stringify(pi.contractTerms.amount), : Amounts.stringify(pi.contractTerms.amount),
@ -510,6 +516,8 @@ function buildTransactionForPeerPullCredit(
}); });
return { return {
type: TransactionType.PeerPullCredit, type: TransactionType.PeerPullCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue), amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount), amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl, exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -543,6 +551,8 @@ function buildTransactionForPeerPullCredit(
return { return {
type: TransactionType.PeerPullCredit, type: TransactionType.PeerPullCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective), amountEffective: Amounts.stringify(pullCredit.estimatedAmountEffective),
amountRaw: Amounts.stringify(peerContractTerms.amount), amountRaw: Amounts.stringify(peerContractTerms.amount),
exchangeBaseUrl: pullCredit.exchangeBaseUrl, exchangeBaseUrl: pullCredit.exchangeBaseUrl,
@ -580,6 +590,8 @@ function buildTransactionForPeerPushCredit(
return { return {
type: TransactionType.PeerPushCredit, type: TransactionType.PeerPushCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue), amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount), amountRaw: Amounts.stringify(wsr.instructedAmount),
exchangeBaseUrl: wsr.exchangeBaseUrl, exchangeBaseUrl: wsr.exchangeBaseUrl,
@ -603,6 +615,8 @@ function buildTransactionForPeerPushCredit(
return { return {
type: TransactionType.PeerPushCredit, type: TransactionType.PeerPushCredit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
// FIXME: This is wrong, needs to consider fees! // FIXME: This is wrong, needs to consider fees!
amountEffective: Amounts.stringify(peerContractTerms.amount), amountEffective: Amounts.stringify(peerContractTerms.amount),
amountRaw: Amounts.stringify(peerContractTerms.amount), amountRaw: Amounts.stringify(peerContractTerms.amount),
@ -632,6 +646,8 @@ function buildTransactionForBankIntegratedWithdraw(
return { return {
type: TransactionType.Withdrawal, type: TransactionType.Withdrawal,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue), amountEffective: Amounts.stringify(wsr.denomsSel.totalCoinValue),
amountRaw: Amounts.stringify(wsr.instructedAmount), amountRaw: Amounts.stringify(wsr.instructedAmount),
withdrawalDetails: { withdrawalDetails: {
@ -677,6 +693,8 @@ function buildTransactionForManualWithdraw(
return { return {
type: TransactionType.Withdrawal, type: TransactionType.Withdrawal,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify( amountEffective: Amounts.stringify(
withdrawalGroup.denomsSel.totalCoinValue, withdrawalGroup.denomsSel.totalCoinValue,
), ),
@ -727,6 +745,8 @@ function buildTransactionForRefresh(
).amount; ).amount;
return { return {
type: TransactionType.Refresh, type: TransactionType.Refresh,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
refreshReason: refreshGroupRecord.reason, refreshReason: refreshGroupRecord.reason,
amountEffective: Amounts.stringify( amountEffective: Amounts.stringify(
Amounts.zeroOfCurrency(refreshGroupRecord.currency), Amounts.zeroOfCurrency(refreshGroupRecord.currency),
@ -768,6 +788,8 @@ function buildTransactionForDeposit(
return { return {
type: TransactionType.Deposit, type: TransactionType.Deposit,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountRaw: Amounts.stringify(dg.effectiveDepositAmount), amountRaw: Amounts.stringify(dg.effectiveDepositAmount),
amountEffective: Amounts.stringify(dg.totalPayCost), amountEffective: Amounts.stringify(dg.totalPayCost),
extendedStatus: dg.timestampFinished extendedStatus: dg.timestampFinished
@ -804,6 +826,8 @@ function buildTransactionForTip(
return { return {
type: TransactionType.Tip, type: TransactionType.Tip,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountEffective: Amounts.stringify(tipRecord.tipAmountEffective), amountEffective: Amounts.stringify(tipRecord.tipAmountEffective),
amountRaw: Amounts.stringify(tipRecord.tipAmountRaw), amountRaw: Amounts.stringify(tipRecord.tipAmountRaw),
extendedStatus: tipRecord.pickedUpTimestamp extendedStatus: tipRecord.pickedUpTimestamp
@ -899,6 +923,8 @@ async function buildTransactionForRefund(
return { return {
type: TransactionType.Refund, type: TransactionType.Refund,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
info, info,
refundedTransactionId: makeTransactionId( refundedTransactionId: makeTransactionId(
TransactionType.Payment, TransactionType.Payment,
@ -1001,6 +1027,8 @@ async function buildTransactionForPurchase(
return { return {
type: TransactionType.Payment, type: TransactionType.Payment,
txState: TransactionState.Unknown,
txSubstate: TransactionSubstate.Unknown,
amountRaw: Amounts.stringify(contractData.amount), amountRaw: Amounts.stringify(contractData.amount),
amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost), amountEffective: Amounts.stringify(purchaseRecord.payInfo.totalPayCost),
totalRefundRaw: Amounts.stringify(totalRefund.raw), totalRefundRaw: Amounts.stringify(totalRefund.raw),

View File

@ -61,7 +61,6 @@ import {
GetContractTermsDetailsRequest, GetContractTermsDetailsRequest,
GetExchangeTosRequest, GetExchangeTosRequest,
GetExchangeTosResult, GetExchangeTosResult,
GetFeeForDepositRequest,
GetWithdrawalDetailsForAmountRequest, GetWithdrawalDetailsForAmountRequest,
GetWithdrawalDetailsForUriRequest, GetWithdrawalDetailsForUriRequest,
InitiatePeerPullCreditRequest, InitiatePeerPullCreditRequest,
@ -94,8 +93,6 @@ import {
SetWalletDeviceIdRequest, SetWalletDeviceIdRequest,
TestPayArgs, TestPayArgs,
TestPayResult, TestPayResult,
TrackDepositGroupRequest,
TrackDepositGroupResponse,
Transaction, Transaction,
TransactionByIdRequest, TransactionByIdRequest,
TransactionsRequest, TransactionsRequest,
@ -175,8 +172,6 @@ export enum WalletApiOperation {
ExportBackupRecovery = "exportBackupRecovery", ExportBackupRecovery = "exportBackupRecovery",
ImportBackupRecovery = "importBackupRecovery", ImportBackupRecovery = "importBackupRecovery",
GetBackupInfo = "getBackupInfo", GetBackupInfo = "getBackupInfo",
TrackDepositGroup = "trackDepositGroup",
GetFeeForDeposit = "getFeeForDeposit",
PrepareDeposit = "prepareDeposit", PrepareDeposit = "prepareDeposit",
GetVersion = "getVersion", GetVersion = "getVersion",
DeleteTransaction = "deleteTransaction", DeleteTransaction = "deleteTransaction",
@ -512,21 +507,6 @@ export type CreateDepositGroupOp = {
response: CreateDepositGroupResponse; response: CreateDepositGroupResponse;
}; };
/**
* Track the status of a deposit group by querying the exchange.
*/
export type TrackDepositGroupOp = {
op: WalletApiOperation.TrackDepositGroup;
request: TrackDepositGroupRequest;
response: TrackDepositGroupResponse;
};
export type GetFeeForDepositOp = {
op: WalletApiOperation.GetFeeForDeposit;
request: GetFeeForDepositRequest;
response: DepositGroupFees;
};
export type PrepareDepositOp = { export type PrepareDepositOp = {
op: WalletApiOperation.PrepareDeposit; op: WalletApiOperation.PrepareDeposit;
request: PrepareDepositRequest; request: PrepareDepositRequest;
@ -928,8 +908,6 @@ export type WalletOperations = {
[WalletApiOperation.SetExchangeTosAccepted]: SetExchangeTosAcceptedOp; [WalletApiOperation.SetExchangeTosAccepted]: SetExchangeTosAcceptedOp;
[WalletApiOperation.GetExchangeTos]: GetExchangeTosOp; [WalletApiOperation.GetExchangeTos]: GetExchangeTosOp;
[WalletApiOperation.GetExchangeDetailedInfo]: GetExchangeDetailedInfoOp; [WalletApiOperation.GetExchangeDetailedInfo]: GetExchangeDetailedInfoOp;
[WalletApiOperation.TrackDepositGroup]: TrackDepositGroupOp;
[WalletApiOperation.GetFeeForDeposit]: GetFeeForDepositOp;
[WalletApiOperation.PrepareDeposit]: PrepareDepositOp; [WalletApiOperation.PrepareDeposit]: PrepareDepositOp;
[WalletApiOperation.CreateDepositGroup]: CreateDepositGroupOp; [WalletApiOperation.CreateDepositGroup]: CreateDepositGroupOp;
[WalletApiOperation.SetWalletDeviceId]: SetWalletDeviceIdOp; [WalletApiOperation.SetWalletDeviceId]: SetWalletDeviceIdOp;

View File

@ -48,7 +48,6 @@ import {
codecForGetBalanceDetailRequest, codecForGetBalanceDetailRequest,
codecForGetContractTermsDetails, codecForGetContractTermsDetails,
codecForGetExchangeTosRequest, codecForGetExchangeTosRequest,
codecForGetFeeForDeposit,
codecForGetWithdrawalDetailsForAmountRequest, codecForGetWithdrawalDetailsForAmountRequest,
codecForGetWithdrawalDetailsForUri, codecForGetWithdrawalDetailsForUri,
codecForImportDbRequest, codecForImportDbRequest,
@ -69,7 +68,6 @@ import {
codecForSetDevModeRequest, codecForSetDevModeRequest,
codecForSetWalletDeviceIdRequest, codecForSetWalletDeviceIdRequest,
codecForTestPayArgs, codecForTestPayArgs,
codecForTrackDepositGroupRequest,
codecForTransactionByIdRequest, codecForTransactionByIdRequest,
codecForTransactionsRequest, codecForTransactionsRequest,
codecForUserAttentionByIdRequest, codecForUserAttentionByIdRequest,
@ -174,10 +172,8 @@ import {
} from "./operations/common.js"; } from "./operations/common.js";
import { import {
createDepositGroup, createDepositGroup,
getFeeForDeposit,
prepareDepositGroup, prepareDepositGroup,
processDepositGroup, processDepositGroup,
trackDepositGroup,
} from "./operations/deposits.js"; } from "./operations/deposits.js";
import { import {
acceptExchangeTermsOfService, acceptExchangeTermsOfService,
@ -1030,8 +1026,8 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
case WalletApiOperation.WithdrawTestkudos: { case WalletApiOperation.WithdrawTestkudos: {
await withdrawTestBalance(ws, { await withdrawTestBalance(ws, {
amount: "TESTKUDOS:10", amount: "TESTKUDOS:10",
bankBaseUrl: "https://bank.test.taler.net/", bankAccessApiBaseUrl:
bankAccessApiBaseUrl: "https://bank.test.taler.net/", "https://bank.test.taler.net/demobanks/default/access-api/",
exchangeBaseUrl: "https://exchange.test.taler.net/", exchangeBaseUrl: "https://exchange.test.taler.net/",
}); });
return { return {
@ -1348,10 +1344,6 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
const resp = await getBackupInfo(ws); const resp = await getBackupInfo(ws);
return resp; return resp;
} }
case WalletApiOperation.GetFeeForDeposit: {
const req = codecForGetFeeForDeposit().decode(payload);
return await getFeeForDeposit(ws, req);
}
case WalletApiOperation.PrepareDeposit: { case WalletApiOperation.PrepareDeposit: {
const req = codecForPrepareDepositRequest().decode(payload); const req = codecForPrepareDepositRequest().decode(payload);
return await prepareDepositGroup(ws, req); return await prepareDepositGroup(ws, req);
@ -1360,10 +1352,6 @@ async function dispatchRequestInternal<Op extends WalletApiOperation>(
const req = codecForCreateDepositGroupRequest().decode(payload); const req = codecForCreateDepositGroupRequest().decode(payload);
return await createDepositGroup(ws, req); return await createDepositGroup(ws, req);
} }
case WalletApiOperation.TrackDepositGroup: {
const req = codecForTrackDepositGroupRequest().decode(payload);
return trackDepositGroup(ws, req);
}
case WalletApiOperation.DeleteTransaction: { case WalletApiOperation.DeleteTransaction: {
const req = codecForDeleteTransactionRequest().decode(payload); const req = codecForDeleteTransactionRequest().decode(payload);
await deleteTransaction(ws, req.transactionId); await deleteTransaction(ws, req.transactionId);