manual withdrawal
This commit is contained in:
parent
dd3a31f33d
commit
85a095fa7d
@ -37,6 +37,7 @@ import {
|
|||||||
WALLET_EXCHANGE_PROTOCOL_VERSION,
|
WALLET_EXCHANGE_PROTOCOL_VERSION,
|
||||||
WALLET_MERCHANT_PROTOCOL_VERSION,
|
WALLET_MERCHANT_PROTOCOL_VERSION,
|
||||||
} from "../operations/versions";
|
} from "../operations/versions";
|
||||||
|
import { Amounts } from "../util/amounts";
|
||||||
|
|
||||||
// @ts-ignore: special built-in module
|
// @ts-ignore: special built-in module
|
||||||
//import akono = require("akono");
|
//import akono = require("akono");
|
||||||
@ -234,10 +235,9 @@ class AndroidWalletMessageHandler {
|
|||||||
const wallet = await this.wp.promise;
|
const wallet = await this.wp.promise;
|
||||||
return await wallet.confirmPay(args.proposalId, args.sessionId);
|
return await wallet.confirmPay(args.proposalId, args.sessionId);
|
||||||
}
|
}
|
||||||
case "createManualReserve": {
|
case "acceptManualWithdrawal": {
|
||||||
const wallet = await this.wp.promise;
|
const wallet = await this.wp.promise;
|
||||||
const res = await wallet.createReserve(args);
|
const res = await wallet.acceptManualWithdrawal(args.exchangeBaseUrl, Amounts.parseOrThrow(args.amount));
|
||||||
await wallet.confirmReserve({ reservePub: res.reservePub });
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
case "startTunnel": {
|
case "startTunnel": {
|
||||||
|
@ -7,7 +7,7 @@ import { openDatabase, Database, Store, Index } from "./util/query";
|
|||||||
* with each major change. When incrementing the major version,
|
* with each major change. When incrementing the major version,
|
||||||
* the wallet should import data from the previous version.
|
* the wallet should import data from the previous version.
|
||||||
*/
|
*/
|
||||||
const TALER_DB_NAME = "taler-walletdb-v5";
|
const TALER_DB_NAME = "taler-walletdb-v6";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current database minor version, should be incremented
|
* Current database minor version, should be incremented
|
||||||
|
@ -26,7 +26,6 @@ import { Wallet } from "../wallet";
|
|||||||
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
import { MemoryBackend, BridgeIDBFactory, shimIndexedDB } from "idb-bridge";
|
||||||
import { openTalerDatabase } from "../db";
|
import { openTalerDatabase } from "../db";
|
||||||
import { HttpRequestLibrary } from "../util/http";
|
import { HttpRequestLibrary } from "../util/http";
|
||||||
import * as amounts from "../util/amounts";
|
|
||||||
import { Bank } from "./bank";
|
import { Bank } from "./bank";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker";
|
import { NodeThreadCryptoWorkerFactory } from "../crypto/workers/nodeThreadWorker";
|
||||||
@ -36,6 +35,7 @@ import { NodeHttpLib } from "./NodeHttpLib";
|
|||||||
import { Logger } from "../util/logging";
|
import { Logger } from "../util/logging";
|
||||||
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
|
import { SynchronousCryptoWorkerFactory } from "../crypto/workers/synchronousWorker";
|
||||||
import { WithdrawalSourceType } from "../types/dbTypes";
|
import { WithdrawalSourceType } from "../types/dbTypes";
|
||||||
|
import { Amounts } from "../util/amounts";
|
||||||
|
|
||||||
const logger = new Logger("helpers.ts");
|
const logger = new Logger("helpers.ts");
|
||||||
|
|
||||||
@ -142,11 +142,7 @@ export async function withdrawTestBalance(
|
|||||||
bankBaseUrl = "https://bank.test.taler.net/",
|
bankBaseUrl = "https://bank.test.taler.net/",
|
||||||
exchangeBaseUrl = "https://exchange.test.taler.net/",
|
exchangeBaseUrl = "https://exchange.test.taler.net/",
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const reserveResponse = await myWallet.createReserve({
|
const reserveResponse = await myWallet.acceptManualWithdrawal(exchangeBaseUrl, Amounts.parseOrThrow(amount));
|
||||||
amount: amounts.parseOrThrow(amount),
|
|
||||||
exchange: exchangeBaseUrl,
|
|
||||||
exchangeWire: "payto://unknown",
|
|
||||||
});
|
|
||||||
|
|
||||||
const reservePub = reserveResponse.reservePub;
|
const reservePub = reserveResponse.reservePub;
|
||||||
|
|
||||||
@ -176,6 +172,5 @@ export async function withdrawTestBalance(
|
|||||||
});
|
});
|
||||||
|
|
||||||
await bank.createReserve(bankUser, amount, reservePub, exchangePaytoUri);
|
await bank.createReserve(bankUser, amount, reservePub, exchangePaytoUri);
|
||||||
await myWallet.confirmReserve({ reservePub: reserveResponse.reservePub });
|
|
||||||
await donePromise;
|
await donePromise;
|
||||||
}
|
}
|
||||||
|
@ -367,9 +367,7 @@ exchangesCli
|
|||||||
})
|
})
|
||||||
.action(async (args) => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async (wallet) => {
|
await withWallet(args, async (wallet) => {
|
||||||
await wallet.updateExchangeFromUrl(
|
await wallet.updateExchangeFromUrl(args.exchangesAddCmd.url);
|
||||||
args.exchangesAddCmd.url,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -387,7 +385,7 @@ exchangesCli
|
|||||||
await withWallet(args, async (wallet) => {
|
await withWallet(args, async (wallet) => {
|
||||||
await wallet.acceptExchangeTermsOfService(
|
await wallet.acceptExchangeTermsOfService(
|
||||||
args.exchangesAcceptTosCmd.url,
|
args.exchangesAcceptTosCmd.url,
|
||||||
args.exchangesAcceptTosCmd.etag
|
args.exchangesAcceptTosCmd.etag,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -401,9 +399,7 @@ exchangesCli
|
|||||||
})
|
})
|
||||||
.action(async (args) => {
|
.action(async (args) => {
|
||||||
await withWallet(args, async (wallet) => {
|
await withWallet(args, async (wallet) => {
|
||||||
const tosResult = await wallet.getExchangeTos(
|
const tosResult = await wallet.getExchangeTos(args.exchangesTosCmd.url);
|
||||||
args.exchangesTosCmd.url,
|
|
||||||
);
|
|
||||||
console.log(JSON.stringify(tosResult, undefined, 2));
|
console.log(JSON.stringify(tosResult, undefined, 2));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -458,14 +454,10 @@ advancedCli
|
|||||||
console.log("exchange has no accounts");
|
console.log("exchange has no accounts");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reserve = await wallet.createReserve({
|
const reserve = await wallet.acceptManualWithdrawal(
|
||||||
amount: Amounts.parseOrThrow(args.withdrawManually.amount),
|
exchange.baseUrl,
|
||||||
exchangeWire: acct.payto_uri,
|
Amounts.parseOrThrow(args.withdrawManually.amount),
|
||||||
exchange: exchange.baseUrl,
|
);
|
||||||
});
|
|
||||||
await wallet.confirmReserve({
|
|
||||||
reservePub: reserve.reservePub,
|
|
||||||
});
|
|
||||||
const completePaytoUri = addPaytoQueryParams(acct.payto_uri, {
|
const completePaytoUri = addPaytoQueryParams(acct.payto_uri, {
|
||||||
amount: args.withdrawManually.amount,
|
amount: args.withdrawManually.amount,
|
||||||
message: `Taler top-up ${reserve.reservePub}`,
|
message: `Taler top-up ${reserve.reservePub}`,
|
||||||
|
@ -160,20 +160,6 @@ async function gatherReservePending(
|
|||||||
case ReserveRecordStatus.DORMANT:
|
case ReserveRecordStatus.DORMANT:
|
||||||
// nothing to report as pending
|
// nothing to report as pending
|
||||||
break;
|
break;
|
||||||
case ReserveRecordStatus.UNCONFIRMED:
|
|
||||||
if (onlyDue) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
resp.pendingOperations.push({
|
|
||||||
type: PendingOperationType.Reserve,
|
|
||||||
givesLifeness: false,
|
|
||||||
stage: reserve.reserveStatus,
|
|
||||||
timestampCreated: reserve.timestampCreated,
|
|
||||||
reserveType,
|
|
||||||
reservePub: reserve.reservePub,
|
|
||||||
retryInfo: reserve.retryInfo,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case ReserveRecordStatus.WAIT_CONFIRM_BANK:
|
case ReserveRecordStatus.WAIT_CONFIRM_BANK:
|
||||||
case ReserveRecordStatus.WITHDRAWING:
|
case ReserveRecordStatus.WITHDRAWING:
|
||||||
case ReserveRecordStatus.QUERYING_STATUS:
|
case ReserveRecordStatus.QUERYING_STATUS:
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import {
|
import {
|
||||||
CreateReserveRequest,
|
CreateReserveRequest,
|
||||||
CreateReserveResponse,
|
CreateReserveResponse,
|
||||||
ConfirmReserveRequest,
|
|
||||||
OperationError,
|
OperationError,
|
||||||
AcceptWithdrawalResponse,
|
AcceptWithdrawalResponse,
|
||||||
} from "../types/walletTypes";
|
} from "../types/walletTypes";
|
||||||
@ -66,6 +65,8 @@ import {
|
|||||||
reconcileReserveHistory,
|
reconcileReserveHistory,
|
||||||
summarizeReserveHistory,
|
summarizeReserveHistory,
|
||||||
} from "../util/reserveHistoryUtil";
|
} from "../util/reserveHistoryUtil";
|
||||||
|
import { TransactionHandle } from "../util/query";
|
||||||
|
import { addPaytoQueryParams } from "../util/payto";
|
||||||
|
|
||||||
const logger = new Logger("reserves.ts");
|
const logger = new Logger("reserves.ts");
|
||||||
|
|
||||||
@ -99,14 +100,18 @@ export async function createReserve(
|
|||||||
if (req.bankWithdrawStatusUrl) {
|
if (req.bankWithdrawStatusUrl) {
|
||||||
reserveStatus = ReserveRecordStatus.REGISTERING_BANK;
|
reserveStatus = ReserveRecordStatus.REGISTERING_BANK;
|
||||||
} else {
|
} else {
|
||||||
reserveStatus = ReserveRecordStatus.UNCONFIRMED;
|
reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bankInfo: ReserveBankInfo | undefined;
|
let bankInfo: ReserveBankInfo | undefined;
|
||||||
|
|
||||||
if (req.bankWithdrawStatusUrl) {
|
if (req.bankWithdrawStatusUrl) {
|
||||||
|
if (!req.exchangePaytoUri) {
|
||||||
|
throw Error("Exchange payto URI must be specified for a bank-integrated withdrawal");
|
||||||
|
}
|
||||||
bankInfo = {
|
bankInfo = {
|
||||||
statusUrl: req.bankWithdrawStatusUrl,
|
statusUrl: req.bankWithdrawStatusUrl,
|
||||||
|
exchangePaytoUri: req.exchangePaytoUri,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +134,9 @@ export async function createReserve(
|
|||||||
reservePriv: keypair.priv,
|
reservePriv: keypair.priv,
|
||||||
reservePub: keypair.pub,
|
reservePub: keypair.pub,
|
||||||
senderWire: req.senderWire,
|
senderWire: req.senderWire,
|
||||||
timestampConfirmed: undefined,
|
timestampBankConfirmed: undefined,
|
||||||
timestampReserveInfoPosted: undefined,
|
timestampReserveInfoPosted: undefined,
|
||||||
bankInfo,
|
bankInfo,
|
||||||
exchangeWire: req.exchangeWire,
|
|
||||||
reserveStatus,
|
reserveStatus,
|
||||||
lastSuccessfulStatusQuery: undefined,
|
lastSuccessfulStatusQuery: undefined,
|
||||||
retryInfo: initRetryInfo(),
|
retryInfo: initRetryInfo(),
|
||||||
@ -314,7 +318,7 @@ async function registerReserveWithBank(
|
|||||||
// FIXME: parse bank response
|
// FIXME: parse bank response
|
||||||
await ws.http.postJson(bankStatusUrl, {
|
await ws.http.postJson(bankStatusUrl, {
|
||||||
reserve_pub: reservePub,
|
reserve_pub: reservePub,
|
||||||
selected_exchange: reserve.exchangeWire,
|
selected_exchange: bankInfo.exchangePaytoUri,
|
||||||
});
|
});
|
||||||
await ws.db.mutate(Stores.reserves, reservePub, (r) => {
|
await ws.db.mutate(Stores.reserves, reservePub, (r) => {
|
||||||
switch (r.reserveStatus) {
|
switch (r.reserveStatus) {
|
||||||
@ -395,7 +399,7 @@ async function processReserveBankStatusImpl(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const now = getTimestampNow();
|
const now = getTimestampNow();
|
||||||
r.timestampConfirmed = now;
|
r.timestampBankConfirmed = now;
|
||||||
r.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
|
r.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
|
||||||
r.retryInfo = initRetryInfo();
|
r.retryInfo = initRetryInfo();
|
||||||
return r;
|
return r;
|
||||||
@ -461,10 +465,6 @@ async function updateReserve(
|
|||||||
throw Error("reserve not in db");
|
throw Error("reserve not in db");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reserve.timestampConfirmed === undefined) {
|
|
||||||
throw Error("reserve not confirmed yet");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reserve.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
|
if (reserve.reserveStatus !== ReserveRecordStatus.QUERYING_STATUS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -590,9 +590,6 @@ async function processReserveImpl(
|
|||||||
`Processing reserve ${reservePub} with status ${reserve.reserveStatus}`,
|
`Processing reserve ${reservePub} with status ${reserve.reserveStatus}`,
|
||||||
);
|
);
|
||||||
switch (reserve.reserveStatus) {
|
switch (reserve.reserveStatus) {
|
||||||
case ReserveRecordStatus.UNCONFIRMED:
|
|
||||||
// nothing to do
|
|
||||||
break;
|
|
||||||
case ReserveRecordStatus.REGISTERING_BANK:
|
case ReserveRecordStatus.REGISTERING_BANK:
|
||||||
await processReserveBankStatus(ws, reservePub);
|
await processReserveBankStatus(ws, reservePub);
|
||||||
return await processReserveImpl(ws, reservePub, true);
|
return await processReserveImpl(ws, reservePub, true);
|
||||||
@ -615,28 +612,6 @@ async function processReserveImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function confirmReserve(
|
|
||||||
ws: InternalWalletState,
|
|
||||||
req: ConfirmReserveRequest,
|
|
||||||
): Promise<void> {
|
|
||||||
const now = getTimestampNow();
|
|
||||||
await ws.db.mutate(Stores.reserves, req.reservePub, (reserve) => {
|
|
||||||
if (reserve.reserveStatus !== ReserveRecordStatus.UNCONFIRMED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
reserve.timestampConfirmed = now;
|
|
||||||
reserve.reserveStatus = ReserveRecordStatus.QUERYING_STATUS;
|
|
||||||
reserve.retryInfo = initRetryInfo();
|
|
||||||
return reserve;
|
|
||||||
});
|
|
||||||
|
|
||||||
ws.notify({ type: NotificationType.ReserveUpdated });
|
|
||||||
|
|
||||||
processReserve(ws, req.reservePub, true).catch((e) => {
|
|
||||||
console.log("processing reserve (after confirmReserve) failed:", e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Withdraw coins from a reserve until it is empty.
|
* Withdraw coins from a reserve until it is empty.
|
||||||
*
|
*
|
||||||
@ -818,7 +793,7 @@ export async function createTalerWithdrawReserve(
|
|||||||
bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl,
|
bankWithdrawStatusUrl: withdrawInfo.extractedStatusUrl,
|
||||||
exchange: selectedExchange,
|
exchange: selectedExchange,
|
||||||
senderWire: withdrawInfo.senderWire,
|
senderWire: withdrawInfo.senderWire,
|
||||||
exchangeWire: exchangeWire,
|
exchangePaytoUri: exchangeWire,
|
||||||
});
|
});
|
||||||
// We do this here, as the reserve should be registered before we return,
|
// We do this here, as the reserve should be registered before we return,
|
||||||
// so that we can redirect the user to the bank's status page.
|
// so that we can redirect the user to the bank's status page.
|
||||||
@ -829,3 +804,34 @@ export async function createTalerWithdrawReserve(
|
|||||||
confirmTransferUrl: withdrawInfo.confirmTransferUrl,
|
confirmTransferUrl: withdrawInfo.confirmTransferUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get payto URIs needed to fund a reserve.
|
||||||
|
*/
|
||||||
|
export async function getFundingPaytoUris(
|
||||||
|
tx: TransactionHandle,
|
||||||
|
reservePub: string,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const r = await tx.get(Stores.reserves, reservePub);
|
||||||
|
if (!r) {
|
||||||
|
logger.error(`reserve ${reservePub} not found (DB corrupted?)`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const exchange = await tx.get(Stores.exchanges, r.exchangeBaseUrl);
|
||||||
|
if (!exchange) {
|
||||||
|
logger.error(`exchange ${r.exchangeBaseUrl} not found (DB corrupted?)`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const plainPaytoUris =
|
||||||
|
exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [];
|
||||||
|
if (!plainPaytoUris) {
|
||||||
|
logger.error(`exchange ${r.exchangeBaseUrl} has no wire info`);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return plainPaytoUris.map((x) =>
|
||||||
|
addPaytoQueryParams(x, {
|
||||||
|
amount: Amounts.stringify(r.instructedAmount),
|
||||||
|
message: `Taler Withdrawal ${r.reservePub}`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -35,9 +35,7 @@ import {
|
|||||||
WithdrawalType,
|
WithdrawalType,
|
||||||
WithdrawalDetails,
|
WithdrawalDetails,
|
||||||
} from "../types/transactions";
|
} from "../types/transactions";
|
||||||
import { WithdrawalDetailsResponse } from "../types/walletTypes";
|
import { getFundingPaytoUris } from "./reserves";
|
||||||
import { Logger } from "../util/logging";
|
|
||||||
import { addPaytoQueryParams } from "../util/payto";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@ -257,22 +255,9 @@ export async function getTransactions(
|
|||||||
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
bankConfirmationUrl: r.bankInfo.confirmUrl,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const exchange = await tx.get(Stores.exchanges, r.exchangeBaseUrl);
|
|
||||||
if (!exchange) {
|
|
||||||
// FIXME: report somehow
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const plainPaytoUris = exchange.wireInfo?.accounts.map((x) => x.payto_uri) ?? [];
|
|
||||||
if (!plainPaytoUris) {
|
|
||||||
// FIXME: report somehow
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
withdrawalDetails = {
|
withdrawalDetails = {
|
||||||
type: WithdrawalType.ManualTransfer,
|
type: WithdrawalType.ManualTransfer,
|
||||||
exchangePaytoUris: plainPaytoUris.map((x) => addPaytoQueryParams(x, {
|
exchangePaytoUris: await getFundingPaytoUris(tx, r.reservePub),
|
||||||
amount: Amounts.stringify(r.instructedAmount),
|
|
||||||
message: `Taler Withdrawal ${r.reservePub}`,
|
|
||||||
})),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
transactions.push({
|
transactions.push({
|
||||||
|
@ -48,11 +48,6 @@ import { Timestamp, Duration, getTimestampNow } from "../util/time";
|
|||||||
import { PayCoinSelection, PayCostInfo } from "../operations/pay";
|
import { PayCoinSelection, PayCostInfo } from "../operations/pay";
|
||||||
|
|
||||||
export enum ReserveRecordStatus {
|
export enum ReserveRecordStatus {
|
||||||
/**
|
|
||||||
* Waiting for manual confirmation.
|
|
||||||
*/
|
|
||||||
UNCONFIRMED = "unconfirmed",
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserve must be registered with the bank.
|
* Reserve must be registered with the bank.
|
||||||
*/
|
*/
|
||||||
@ -219,8 +214,18 @@ export interface ReserveHistoryRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ReserveBankInfo {
|
export interface ReserveBankInfo {
|
||||||
|
/**
|
||||||
|
* Status URL that the wallet will use to query the status
|
||||||
|
* of the Taler withdrawal operation on the bank's side.
|
||||||
|
*/
|
||||||
statusUrl: string;
|
statusUrl: string;
|
||||||
|
|
||||||
confirmUrl?: string;
|
confirmUrl?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exchange payto URI that the bank will use to fund the reserve.
|
||||||
|
*/
|
||||||
|
exchangePaytoUri: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -262,12 +267,11 @@ export interface ReserveRecord {
|
|||||||
timestampReserveInfoPosted: Timestamp | undefined;
|
timestampReserveInfoPosted: Timestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Time when the reserve was confirmed, either manually by the user
|
* Time when the reserve was confirmed by the bank.
|
||||||
* or by the bank.
|
|
||||||
*
|
*
|
||||||
* Set to undefined if not confirmed yet.
|
* Set to undefined if not confirmed yet.
|
||||||
*/
|
*/
|
||||||
timestampConfirmed: Timestamp | undefined;
|
timestampBankConfirmed: Timestamp | undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire information (as payto URI) for the bank account that
|
* Wire information (as payto URI) for the bank account that
|
||||||
@ -275,12 +279,6 @@ export interface ReserveRecord {
|
|||||||
*/
|
*/
|
||||||
senderWire?: string;
|
senderWire?: string;
|
||||||
|
|
||||||
/**
|
|
||||||
* Wire information (as payto URI) for the exchange, specifically
|
|
||||||
* the account that was transferred to when creating the reserve.
|
|
||||||
*/
|
|
||||||
exchangeWire: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Amount that was sent by the user to fund the reserve.
|
* Amount that was sent by the user to fund the reserve.
|
||||||
*/
|
*/
|
||||||
|
@ -246,7 +246,7 @@ export interface CreateReserveRequest {
|
|||||||
* Payto URI that identifies the exchange's account that the funds
|
* Payto URI that identifies the exchange's account that the funds
|
||||||
* for this reserve go into.
|
* for this reserve go into.
|
||||||
*/
|
*/
|
||||||
exchangeWire: string;
|
exchangePaytoUri?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wire details (as a payto URI) for the bank account that sent the funds to
|
* Wire details (as a payto URI) for the bank account that sent the funds to
|
||||||
@ -264,7 +264,7 @@ export const codecForCreateReserveRequest = (): Codec<CreateReserveRequest> =>
|
|||||||
makeCodecForObject<CreateReserveRequest>()
|
makeCodecForObject<CreateReserveRequest>()
|
||||||
.property("amount", codecForAmountJson())
|
.property("amount", codecForAmountJson())
|
||||||
.property("exchange", codecForString)
|
.property("exchange", codecForString)
|
||||||
.property("exchangeWire", codecForString)
|
.property("exchangePaytoUri", codecForString)
|
||||||
.property("senderWire", makeCodecOptional(codecForString))
|
.property("senderWire", makeCodecOptional(codecForString))
|
||||||
.property("bankWithdrawStatusUrl", makeCodecOptional(codecForString))
|
.property("bankWithdrawStatusUrl", makeCodecOptional(codecForString))
|
||||||
.build("CreateReserveRequest");
|
.build("CreateReserveRequest");
|
||||||
@ -491,6 +491,18 @@ export interface ExchangeListItem {
|
|||||||
paytoUris: string[];
|
paytoUris: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AcceptManualWithdrawalResult {
|
||||||
|
/**
|
||||||
|
* Payto URIs that can be used to fund the withdrawal.
|
||||||
|
*/
|
||||||
|
exchangePaytoUris: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public key of the newly created reserve.
|
||||||
|
*/
|
||||||
|
reservePub: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ManualWithdrawalDetails {
|
export interface ManualWithdrawalDetails {
|
||||||
/**
|
/**
|
||||||
* Did the user accept the current version of the exchange's
|
* Did the user accept the current version of the exchange's
|
||||||
|
@ -56,9 +56,6 @@ import { MerchantRefundDetails, CoinDumpJson } from "./types/talerTypes";
|
|||||||
import {
|
import {
|
||||||
BenchmarkResult,
|
BenchmarkResult,
|
||||||
ConfirmPayResult,
|
ConfirmPayResult,
|
||||||
ConfirmReserveRequest,
|
|
||||||
CreateReserveRequest,
|
|
||||||
CreateReserveResponse,
|
|
||||||
ReturnCoinsRequest,
|
ReturnCoinsRequest,
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
TipStatus,
|
TipStatus,
|
||||||
@ -72,6 +69,7 @@ import {
|
|||||||
ExchangesListRespose,
|
ExchangesListRespose,
|
||||||
ManualWithdrawalDetails,
|
ManualWithdrawalDetails,
|
||||||
GetExchangeTosResult,
|
GetExchangeTosResult,
|
||||||
|
AcceptManualWithdrawalResult,
|
||||||
} from "./types/walletTypes";
|
} from "./types/walletTypes";
|
||||||
import { Logger } from "./util/logging";
|
import { Logger } from "./util/logging";
|
||||||
|
|
||||||
@ -87,10 +85,11 @@ import {
|
|||||||
processReserve,
|
processReserve,
|
||||||
createTalerWithdrawReserve,
|
createTalerWithdrawReserve,
|
||||||
forceQueryReserve,
|
forceQueryReserve,
|
||||||
|
getFundingPaytoUris,
|
||||||
} from "./operations/reserves";
|
} from "./operations/reserves";
|
||||||
|
|
||||||
import { InternalWalletState } from "./operations/state";
|
import { InternalWalletState } from "./operations/state";
|
||||||
import { createReserve, confirmReserve } from "./operations/reserves";
|
import { createReserve } from "./operations/reserves";
|
||||||
import { processRefreshGroup, createRefreshGroup } from "./operations/refresh";
|
import { processRefreshGroup, createRefreshGroup } from "./operations/refresh";
|
||||||
import { processWithdrawGroup } from "./operations/withdraw";
|
import { processWithdrawGroup } from "./operations/withdraw";
|
||||||
import { getHistory } from "./operations/history";
|
import { getHistory } from "./operations/history";
|
||||||
@ -171,8 +170,14 @@ export class Wallet {
|
|||||||
exchangeBaseUrl: string,
|
exchangeBaseUrl: string,
|
||||||
amount: AmountJson,
|
amount: AmountJson,
|
||||||
): Promise<ManualWithdrawalDetails> {
|
): Promise<ManualWithdrawalDetails> {
|
||||||
const wi = await getExchangeWithdrawalInfo(this.ws, exchangeBaseUrl, amount);
|
const wi = await getExchangeWithdrawalInfo(
|
||||||
const paytoUris = wi.exchangeInfo.wireInfo?.accounts.map((x) => x.payto_uri);
|
this.ws,
|
||||||
|
exchangeBaseUrl,
|
||||||
|
amount,
|
||||||
|
);
|
||||||
|
const paytoUris = wi.exchangeInfo.wireInfo?.accounts.map(
|
||||||
|
(x) => x.payto_uri,
|
||||||
|
);
|
||||||
if (!paytoUris) {
|
if (!paytoUris) {
|
||||||
throw Error("exchange is in invalid state");
|
throw Error("exchange is in invalid state");
|
||||||
}
|
}
|
||||||
@ -437,28 +442,23 @@ export class Wallet {
|
|||||||
* Adds the corresponding exchange as a trusted exchange if it is neither
|
* Adds the corresponding exchange as a trusted exchange if it is neither
|
||||||
* audited nor trusted already.
|
* audited nor trusted already.
|
||||||
*/
|
*/
|
||||||
async createReserve(
|
async acceptManualWithdrawal(
|
||||||
req: CreateReserveRequest,
|
exchangeBaseUrl: string,
|
||||||
): Promise<CreateReserveResponse> {
|
amount: AmountJson,
|
||||||
|
): Promise<AcceptManualWithdrawalResult> {
|
||||||
try {
|
try {
|
||||||
return createReserve(this.ws, req);
|
const resp = await createReserve(this.ws, {
|
||||||
} finally {
|
amount,
|
||||||
this.latch.trigger();
|
exchange: exchangeBaseUrl,
|
||||||
}
|
});
|
||||||
}
|
const exchangePaytoUris = await this.db.runWithReadTransaction(
|
||||||
|
[Stores.exchanges, Stores.reserves],
|
||||||
/**
|
(tx) => getFundingPaytoUris(tx, resp.reservePub),
|
||||||
* Mark an existing reserve as confirmed. The wallet will start trying
|
);
|
||||||
* to withdraw from that reserve. This may not immediately succeed,
|
return {
|
||||||
* since the exchange might not know about the reserve yet, even though the
|
reservePub: resp.reservePub,
|
||||||
* bank confirmed its creation.
|
exchangePaytoUris,
|
||||||
*
|
};
|
||||||
* A confirmed reserve should be shown to the user in the UI, while
|
|
||||||
* an unconfirmed reserve should be hidden.
|
|
||||||
*/
|
|
||||||
async confirmReserve(req: ConfirmReserveRequest): Promise<void> {
|
|
||||||
try {
|
|
||||||
return confirmReserve(this.ws, req);
|
|
||||||
} finally {
|
} finally {
|
||||||
this.latch.trigger();
|
this.latch.trigger();
|
||||||
}
|
}
|
||||||
@ -511,7 +511,7 @@ export class Wallet {
|
|||||||
acceptedEtag: exchange.termsOfServiceAcceptedEtag,
|
acceptedEtag: exchange.termsOfServiceAcceptedEtag,
|
||||||
currentEtag,
|
currentEtag,
|
||||||
tos,
|
tos,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -602,7 +602,7 @@ export class Wallet {
|
|||||||
return {
|
return {
|
||||||
exchangeBaseUrl: x.baseUrl,
|
exchangeBaseUrl: x.baseUrl,
|
||||||
currency: details.currency,
|
currency: details.currency,
|
||||||
paytoUris: x.wireInfo.accounts.map(x => x.payto_uri),
|
paytoUris: x.wireInfo.accounts.map((x) => x.payto_uri),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
// Messages are already documented in wxApi.
|
// Messages are already documented in wxApi.
|
||||||
/* tslint:disable:completed-docs */
|
/* tslint:disable:completed-docs */
|
||||||
|
|
||||||
import { AmountJson } from "../util/amounts";
|
|
||||||
import * as dbTypes from "../types/dbTypes";
|
import * as dbTypes from "../types/dbTypes";
|
||||||
import * as walletTypes from "../types/walletTypes";
|
import * as walletTypes from "../types/walletTypes";
|
||||||
|
|
||||||
@ -54,17 +53,6 @@ export interface MessageMap {
|
|||||||
request: {};
|
request: {};
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"create-reserve": {
|
|
||||||
request: {
|
|
||||||
amount: AmountJson;
|
|
||||||
exchange: string;
|
|
||||||
};
|
|
||||||
response: void;
|
|
||||||
};
|
|
||||||
"confirm-reserve": {
|
|
||||||
request: { reservePub: string };
|
|
||||||
response: void;
|
|
||||||
};
|
|
||||||
"confirm-pay": {
|
"confirm-pay": {
|
||||||
request: { proposalId: string; sessionId?: string };
|
request: { proposalId: string; sessionId?: string };
|
||||||
response: walletTypes.ConfirmPayResult;
|
response: walletTypes.ConfirmPayResult;
|
||||||
|
@ -32,7 +32,6 @@ import {
|
|||||||
import {
|
import {
|
||||||
BenchmarkResult,
|
BenchmarkResult,
|
||||||
ConfirmPayResult,
|
ConfirmPayResult,
|
||||||
ExchangeWithdrawDetails,
|
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
TipStatus,
|
TipStatus,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
@ -172,13 +171,6 @@ export function confirmPay(
|
|||||||
return callBackend("confirm-pay", { proposalId, sessionId });
|
return callBackend("confirm-pay", { proposalId, sessionId });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark a reserve as confirmed.
|
|
||||||
*/
|
|
||||||
export function confirmReserve(reservePub: string): Promise<void> {
|
|
||||||
return callBackend("confirm-reserve", { reservePub });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check upgrade information
|
* Check upgrade information
|
||||||
*/
|
*/
|
||||||
@ -186,17 +178,6 @@ export function checkUpgrade(): Promise<UpgradeResponse> {
|
|||||||
return callBackend("check-upgrade", {});
|
return callBackend("check-upgrade", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a reserve.
|
|
||||||
*/
|
|
||||||
export function createReserve(args: {
|
|
||||||
amount: AmountJson;
|
|
||||||
exchange: string;
|
|
||||||
senderWire?: string;
|
|
||||||
}): Promise<any> {
|
|
||||||
return callBackend("create-reserve", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset database
|
* Reset database
|
||||||
*/
|
*/
|
||||||
|
@ -32,10 +32,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
ReturnCoinsRequest,
|
ReturnCoinsRequest,
|
||||||
WalletDiagnostics,
|
WalletDiagnostics,
|
||||||
codecForCreateReserveRequest,
|
|
||||||
codecForConfirmReserveRequest,
|
|
||||||
} from "../types/walletTypes";
|
} from "../types/walletTypes";
|
||||||
import { codecForAmountJson } from "../util/amounts";
|
|
||||||
import { BrowserHttpLib } from "../util/http";
|
import { BrowserHttpLib } from "../util/http";
|
||||||
import { OpenedPromise, openPromise } from "../util/promiseUtils";
|
import { OpenedPromise, openPromise } from "../util/promiseUtils";
|
||||||
import { classifyTalerUri, TalerUriType } from "../util/taleruri";
|
import { classifyTalerUri, TalerUriType } from "../util/taleruri";
|
||||||
@ -111,22 +108,6 @@ async function handleMessage(
|
|||||||
}
|
}
|
||||||
return Promise.resolve({});
|
return Promise.resolve({});
|
||||||
}
|
}
|
||||||
case "create-reserve": {
|
|
||||||
const d = {
|
|
||||||
amount: detail.amount,
|
|
||||||
exchange: detail.exchange,
|
|
||||||
senderWire: detail.senderWire,
|
|
||||||
};
|
|
||||||
const req = codecForCreateReserveRequest().decode(d);
|
|
||||||
return needsWallet().createReserve(req);
|
|
||||||
}
|
|
||||||
case "confirm-reserve": {
|
|
||||||
const d = {
|
|
||||||
reservePub: detail.reservePub,
|
|
||||||
};
|
|
||||||
const req = codecForConfirmReserveRequest().decode(d);
|
|
||||||
return needsWallet().confirmReserve(req);
|
|
||||||
}
|
|
||||||
case "confirm-pay": {
|
case "confirm-pay": {
|
||||||
if (typeof detail.proposalId !== "string") {
|
if (typeof detail.proposalId !== "string") {
|
||||||
throw Error("proposalId must be string");
|
throw Error("proposalId must be string");
|
||||||
|
Loading…
Reference in New Issue
Block a user