fix lint issues and separate message types into multiple files
This commit is contained in:
parent
eb689d60ac
commit
fd2cd9c383
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,7 +1,7 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
// Use latest language servicesu
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
// Defines space handling after a comma delimiter
|
||||
"typescript.format.insertSpaceAfterCommaDelimiter": true,
|
||||
// Defines space handling after a semicolon in a for statement
|
||||
@ -34,5 +34,6 @@
|
||||
},
|
||||
"**/*.js.map": true
|
||||
},
|
||||
"tslint.enable": true,
|
||||
"editor.wrappingIndent": "same"
|
||||
}
|
44
.vscode/tasks.json
vendored
Normal file
44
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"option": "watch",
|
||||
"problemMatcher": [
|
||||
"$tsc-watch"
|
||||
],
|
||||
"group": "build",
|
||||
"isBackground": true,
|
||||
"promptOnClose": false
|
||||
},
|
||||
{
|
||||
"type": "typescript",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"problemMatcher": [
|
||||
"$tsc"
|
||||
],
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "tslint",
|
||||
"type": "shell",
|
||||
"command": "make lint",
|
||||
"problemMatcher": {
|
||||
"owner": "tslint",
|
||||
"applyTo": "allDocuments",
|
||||
"fileLocation": "absolute",
|
||||
"severity": "warning",
|
||||
"pattern": "$tslint5"
|
||||
},
|
||||
"group": "build"
|
||||
},
|
||||
{
|
||||
"label": "My Task",
|
||||
"type": "shell",
|
||||
"command": "echo Hello"
|
||||
}
|
||||
]
|
||||
}
|
@ -107,7 +107,7 @@ const tsBaseArgs = {
|
||||
experimentalDecorators: true,
|
||||
module: "commonjs",
|
||||
sourceMap: true,
|
||||
lib: ["ES6", "DOM"],
|
||||
lib: ["es6", "dom"],
|
||||
noImplicitReturns: true,
|
||||
noFallthroughCasesInSwitch: true,
|
||||
strict: true,
|
||||
@ -266,6 +266,7 @@ gulp.task("pogen", function (cb) {
|
||||
*/
|
||||
function tsconfig(confBase) {
|
||||
let conf = {
|
||||
compileOnSave: true,
|
||||
compilerOptions: {},
|
||||
files: []
|
||||
};
|
||||
|
257
src/amounts.ts
Normal file
257
src/amounts.ts
Normal file
@ -0,0 +1,257 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2018 GNUnet e.V. and INRIA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Types and helper functions for dealing with Taler amounts.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { Checkable } from "./checkable";
|
||||
|
||||
/**
|
||||
* Number of fractional units that one value unit represents.
|
||||
*/
|
||||
export const fractionalBase = 1e8;
|
||||
|
||||
/**
|
||||
* Non-negative financial amount. Fractional values are expressed as multiples
|
||||
* of 1e-8.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class AmountJson {
|
||||
/**
|
||||
* Value, must be an integer.
|
||||
*/
|
||||
@Checkable.Number
|
||||
readonly value: number;
|
||||
|
||||
/**
|
||||
* Fraction, must be an integer. Represent 1/1e8 of a unit.
|
||||
*/
|
||||
@Checkable.Number
|
||||
readonly fraction: number;
|
||||
|
||||
/**
|
||||
* Currency of the amount.
|
||||
*/
|
||||
@Checkable.String
|
||||
readonly currency: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => AmountJson;
|
||||
}
|
||||
|
||||
/**
|
||||
* Result of a possibly overflowing operation.
|
||||
*/
|
||||
export interface Result {
|
||||
/**
|
||||
* Resulting, possibly saturated amount.
|
||||
*/
|
||||
amount: AmountJson;
|
||||
/**
|
||||
* Was there an over-/underflow?
|
||||
*/
|
||||
saturated: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the largest amount that is safely representable.
|
||||
*/
|
||||
export function getMaxAmount(currency: string): AmountJson {
|
||||
return {
|
||||
currency,
|
||||
fraction: 2 ** 32,
|
||||
value: Number.MAX_SAFE_INTEGER,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an amount that represents zero units of a currency.
|
||||
*/
|
||||
export function getZero(currency: string): AmountJson {
|
||||
return {
|
||||
currency,
|
||||
fraction: 0,
|
||||
value: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add two amounts. Return the result and whether
|
||||
* the addition overflowed. The overflow is always handled
|
||||
* by saturating and never by wrapping.
|
||||
*
|
||||
* Throws when currencies don't match.
|
||||
*/
|
||||
export function add(first: AmountJson, ...rest: AmountJson[]): Result {
|
||||
const currency = first.currency;
|
||||
let value = first.value + Math.floor(first.fraction / fractionalBase);
|
||||
if (value > Number.MAX_SAFE_INTEGER) {
|
||||
return { amount: getMaxAmount(currency), saturated: true };
|
||||
}
|
||||
let fraction = first.fraction % fractionalBase;
|
||||
for (const x of rest) {
|
||||
if (x.currency !== currency) {
|
||||
throw Error(`Mismatched currency: ${x.currency} and ${currency}`);
|
||||
}
|
||||
|
||||
value = value + x.value + Math.floor((fraction + x.fraction) / fractionalBase);
|
||||
fraction = Math.floor((fraction + x.fraction) % fractionalBase);
|
||||
if (value > Number.MAX_SAFE_INTEGER) {
|
||||
return { amount: getMaxAmount(currency), saturated: true };
|
||||
}
|
||||
}
|
||||
return { amount: { currency, value, fraction }, saturated: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtract two amounts. Return the result and whether
|
||||
* the subtraction overflowed. The overflow is always handled
|
||||
* by saturating and never by wrapping.
|
||||
*
|
||||
* Throws when currencies don't match.
|
||||
*/
|
||||
export function sub(a: AmountJson, ...rest: AmountJson[]): Result {
|
||||
const currency = a.currency;
|
||||
let value = a.value;
|
||||
let fraction = a.fraction;
|
||||
|
||||
for (const b of rest) {
|
||||
if (b.currency !== currency) {
|
||||
throw Error(`Mismatched currency: ${b.currency} and ${currency}`);
|
||||
}
|
||||
if (fraction < b.fraction) {
|
||||
if (value < 1) {
|
||||
return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
|
||||
}
|
||||
value--;
|
||||
fraction += fractionalBase;
|
||||
}
|
||||
console.assert(fraction >= b.fraction);
|
||||
fraction -= b.fraction;
|
||||
if (value < b.value) {
|
||||
return { amount: { currency, value: 0, fraction: 0 }, saturated: true };
|
||||
}
|
||||
value -= b.value;
|
||||
}
|
||||
|
||||
return { amount: { currency, value, fraction }, saturated: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two amounts. Returns 0 when equal, -1 when a < b
|
||||
* and +1 when a > b. Throws when currencies don't match.
|
||||
*/
|
||||
export function cmp(a: AmountJson, b: AmountJson): number {
|
||||
if (a.currency !== b.currency) {
|
||||
throw Error(`Mismatched currency: ${a.currency} and ${b.currency}`);
|
||||
}
|
||||
const av = a.value + Math.floor(a.fraction / fractionalBase);
|
||||
const af = a.fraction % fractionalBase;
|
||||
const bv = b.value + Math.floor(b.fraction / fractionalBase);
|
||||
const bf = b.fraction % fractionalBase;
|
||||
switch (true) {
|
||||
case av < bv:
|
||||
return -1;
|
||||
case av > bv:
|
||||
return 1;
|
||||
case af < bf:
|
||||
return -1;
|
||||
case af > bf:
|
||||
return 1;
|
||||
case af === bf:
|
||||
return 0;
|
||||
default:
|
||||
throw Error("assertion failed");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of an amount.
|
||||
*/
|
||||
export function copy(a: AmountJson): AmountJson {
|
||||
return {
|
||||
currency: a.currency,
|
||||
fraction: a.fraction,
|
||||
value: a.value,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Divide an amount. Throws on division by zero.
|
||||
*/
|
||||
export function divide(a: AmountJson, n: number): AmountJson {
|
||||
if (n === 0) {
|
||||
throw Error(`Division by 0`);
|
||||
}
|
||||
if (n === 1) {
|
||||
return {value: a.value, fraction: a.fraction, currency: a.currency};
|
||||
}
|
||||
const r = a.value % n;
|
||||
return {
|
||||
currency: a.currency,
|
||||
fraction: Math.floor(((r * fractionalBase) + a.fraction) / n),
|
||||
value: Math.floor(a.value / n),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an amount is non-zero.
|
||||
*/
|
||||
export function isNonZero(a: AmountJson): boolean {
|
||||
return a.value > 0 || a.fraction > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an amount like 'EUR:20.5' for 20 Euros and 50 ct.
|
||||
*/
|
||||
export function parse(s: string): AmountJson|undefined {
|
||||
const res = s.match(/([a-zA-Z0-9_*-]+):([0-9])+([.][0-9]+)?/);
|
||||
if (!res) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
currency: res[1],
|
||||
fraction: Math.round(fractionalBase * Number.parseFloat(res[3] || "0")),
|
||||
value: Number.parseInt(res[2]),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the amount to a float.
|
||||
*/
|
||||
export function toFloat(a: AmountJson): number {
|
||||
return a.value + (a.fraction / fractionalBase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a float to a Taler amount.
|
||||
* Loss of precision possible.
|
||||
*/
|
||||
export function fromFloat(floatVal: number, currency: string) {
|
||||
return {
|
||||
currency,
|
||||
fraction: Math.floor((floatVal - Math.floor(floatVal)) * fractionalBase),
|
||||
value: Math.floor(floatVal),
|
||||
};
|
||||
}
|
@ -16,15 +16,15 @@
|
||||
|
||||
// tslint:disable:max-line-length
|
||||
|
||||
import {test} from "ava";
|
||||
import { test } from "ava";
|
||||
|
||||
import {
|
||||
DenominationRecord,
|
||||
DenominationStatus,
|
||||
ReserveRecord,
|
||||
} from "../types";
|
||||
} from "../dbTypes";
|
||||
|
||||
import {CryptoApi} from "./cryptoApi";
|
||||
import { CryptoApi } from "./cryptoApi";
|
||||
|
||||
const masterPub1: string = "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
|
||||
|
||||
|
@ -23,20 +23,27 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson } from "../amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
CoinRecord,
|
||||
CoinWithDenom,
|
||||
ContractTerms,
|
||||
DenominationRecord,
|
||||
PayCoinInfo,
|
||||
PaybackRequest,
|
||||
PreCoinRecord,
|
||||
RefreshSessionRecord,
|
||||
ReserveRecord,
|
||||
TipPlanchet,
|
||||
WireFee,
|
||||
} from "../types";
|
||||
} from "../dbTypes";
|
||||
|
||||
import {
|
||||
ContractTerms,
|
||||
PaybackRequest,
|
||||
} from "../talerTypes";
|
||||
|
||||
import {
|
||||
CoinWithDenom,
|
||||
PayCoinInfo,
|
||||
} from "../walletTypes";
|
||||
|
||||
import * as timer from "../timer";
|
||||
|
||||
|
@ -22,27 +22,33 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import * as Amounts from "../amounts";
|
||||
import { AmountJson } from "../amounts";
|
||||
|
||||
import {
|
||||
canonicalJson,
|
||||
} from "../helpers";
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
CoinPaySig,
|
||||
CoinRecord,
|
||||
CoinStatus,
|
||||
CoinWithDenom,
|
||||
ContractTerms,
|
||||
DenominationRecord,
|
||||
PayCoinInfo,
|
||||
PaybackRequest,
|
||||
PreCoinRecord,
|
||||
RefreshPreCoinRecord,
|
||||
RefreshSessionRecord,
|
||||
ReserveRecord,
|
||||
TipPlanchet,
|
||||
WireFee,
|
||||
} from "../types";
|
||||
} from "../dbTypes";
|
||||
|
||||
import {
|
||||
CoinPaySig,
|
||||
ContractTerms,
|
||||
PaybackRequest,
|
||||
} from "../talerTypes";
|
||||
|
||||
import {
|
||||
CoinWithDenom,
|
||||
PayCoinInfo,
|
||||
} from "../walletTypes";
|
||||
|
||||
import { canonicalJson } from "../helpers";
|
||||
|
||||
import {
|
||||
Amount,
|
||||
@ -112,6 +118,9 @@ namespace RpcFunctions {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a planchet used for tipping, including the private keys.
|
||||
*/
|
||||
export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
||||
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
|
||||
const coinPriv = native.EddsaPrivateKey.create();
|
||||
@ -134,8 +143,8 @@ namespace RpcFunctions {
|
||||
coinPriv: coinPriv.toCrock(),
|
||||
coinPub: coinPub.toCrock(),
|
||||
coinValue: denom.value,
|
||||
denomPubHash: denomPub.encode().hash().toCrock(),
|
||||
denomPub: denomPub.encode().toCrock(),
|
||||
denomPubHash: denomPub.encode().hash().toCrock(),
|
||||
};
|
||||
return tipPlanchet;
|
||||
}
|
||||
@ -263,8 +272,8 @@ namespace RpcFunctions {
|
||||
cds: CoinWithDenom[]): PayCoinInfo {
|
||||
const ret: PayCoinInfo = {
|
||||
originalCoins: [],
|
||||
updatedCoins: [],
|
||||
sigs: [],
|
||||
updatedCoins: [],
|
||||
};
|
||||
|
||||
const contractTermsHash = hashString(canonicalJson(contractTerms));
|
||||
@ -325,8 +334,8 @@ namespace RpcFunctions {
|
||||
const s: CoinPaySig = {
|
||||
coin_pub: cd.coin.coinPub,
|
||||
coin_sig: coinSig,
|
||||
denom_pub: cd.coin.denomPub,
|
||||
contribution: coinSpend.toJson(),
|
||||
denom_pub: cd.coin.denomPub,
|
||||
ub_sig: cd.coin.denomSig,
|
||||
};
|
||||
ret.sigs.push(s);
|
||||
|
@ -26,9 +26,9 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {AmountJson} from "../types";
|
||||
import { AmountJson } from "../amounts";
|
||||
|
||||
import {EmscFunGen, getLib} from "./emscLoader";
|
||||
import { EmscFunGen, getLib } from "./emscLoader";
|
||||
|
||||
const emscLib = getLib();
|
||||
|
||||
|
811
src/dbTypes.ts
Normal file
811
src/dbTypes.ts
Normal file
@ -0,0 +1,811 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2018 GNUnet e.V. and INRIA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Types for records stored in the wallet's database.
|
||||
*
|
||||
* Types for the objects in the database should end in "-Record".
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson } from "./amounts";
|
||||
import { Checkable } from "./checkable";
|
||||
import {
|
||||
Auditor,
|
||||
CoinPaySig,
|
||||
ContractTerms,
|
||||
Denomination,
|
||||
PayReq,
|
||||
RefundPermission,
|
||||
TipResponse,
|
||||
} from "./talerTypes";
|
||||
|
||||
|
||||
/**
|
||||
* A reserve record as stored in the wallet's database.
|
||||
*/
|
||||
export interface ReserveRecord {
|
||||
/**
|
||||
* The reserve public key.
|
||||
*/
|
||||
reserve_pub: string;
|
||||
|
||||
/**
|
||||
* The reserve private key.
|
||||
*/
|
||||
reserve_priv: string;
|
||||
|
||||
/**
|
||||
* The exchange base URL.
|
||||
*/
|
||||
exchange_base_url: string;
|
||||
|
||||
/**
|
||||
* Time when the reserve was created.
|
||||
*/
|
||||
created: number;
|
||||
|
||||
/**
|
||||
* Time when the reserve was depleted.
|
||||
* Set to 0 if not depleted yet.
|
||||
*/
|
||||
timestamp_depleted: number;
|
||||
|
||||
/**
|
||||
* Time when the reserve was confirmed.
|
||||
*
|
||||
* Set to 0 if not confirmed yet.
|
||||
*/
|
||||
timestamp_confirmed: number;
|
||||
|
||||
/**
|
||||
* Current amount left in the reserve
|
||||
*/
|
||||
current_amount: AmountJson | null;
|
||||
|
||||
/**
|
||||
* Amount requested when the reserve was created.
|
||||
* When a reserve is re-used (rare!) the current_amount can
|
||||
* be higher than the requested_amount
|
||||
*/
|
||||
requested_amount: AmountJson;
|
||||
|
||||
/**
|
||||
* What's the current amount that sits
|
||||
* in precoins?
|
||||
*/
|
||||
precoin_amount: AmountJson;
|
||||
|
||||
/**
|
||||
* We got some payback to this reserve. We'll cease to automatically
|
||||
* withdraw money from it.
|
||||
*/
|
||||
hasPayback: boolean;
|
||||
|
||||
/**
|
||||
* Wire information for the bank account that
|
||||
* transfered funds for this reserve.
|
||||
*/
|
||||
senderWire?: object;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Auditor record as stored with currencies in the exchange database.
|
||||
*/
|
||||
export interface AuditorRecord {
|
||||
/**
|
||||
* Base url of the auditor.
|
||||
*/
|
||||
baseUrl: string;
|
||||
/**
|
||||
* Public signing key of the auditor.
|
||||
*/
|
||||
auditorPub: string;
|
||||
/**
|
||||
* Time when the auditing expires.
|
||||
*/
|
||||
expirationStamp: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exchange for currencies as stored in the wallet's currency
|
||||
* information database.
|
||||
*/
|
||||
export interface ExchangeForCurrencyRecord {
|
||||
/**
|
||||
* FIXME: unused?
|
||||
*/
|
||||
exchangePub: string;
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about a currency as displayed in the wallet's database.
|
||||
*/
|
||||
export interface CurrencyRecord {
|
||||
/**
|
||||
* Name of the currency.
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
* Number of fractional digits to show when rendering the currency.
|
||||
*/
|
||||
fractionalDigits: number;
|
||||
/**
|
||||
* Auditors that the wallet trusts for this currency.
|
||||
*/
|
||||
auditors: AuditorRecord[];
|
||||
/**
|
||||
* Exchanges that the wallet trusts for this currency.
|
||||
*/
|
||||
exchanges: ExchangeForCurrencyRecord[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of a denomination.
|
||||
*/
|
||||
export enum DenominationStatus {
|
||||
/**
|
||||
* Verification was delayed.
|
||||
*/
|
||||
Unverified,
|
||||
/**
|
||||
* Verified as valid.
|
||||
*/
|
||||
VerifiedGood,
|
||||
/**
|
||||
* Verified as invalid.
|
||||
*/
|
||||
VerifiedBad,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Denomination record as stored in the wallet's database.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class DenominationRecord {
|
||||
/**
|
||||
* Value of one coin of the denomination.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
value: AmountJson;
|
||||
|
||||
/**
|
||||
* The denomination public key.
|
||||
*/
|
||||
@Checkable.String
|
||||
denomPub: string;
|
||||
|
||||
/**
|
||||
* Hash of the denomination public key.
|
||||
* Stored in the database for faster lookups.
|
||||
*/
|
||||
@Checkable.String
|
||||
denomPubHash: string;
|
||||
|
||||
/**
|
||||
* Fee for withdrawing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeWithdraw: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for depositing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeDeposit: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refreshing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeRefresh: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refunding.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
feeRefund: AmountJson;
|
||||
|
||||
/**
|
||||
* Validity start date of the denomination.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampStart: string;
|
||||
|
||||
/**
|
||||
* Date after which the currency can't be withdrawn anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireWithdraw: string;
|
||||
|
||||
/**
|
||||
* Date after the denomination officially doesn't exist anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireLegal: string;
|
||||
|
||||
/**
|
||||
* Data after which coins of this denomination can't be deposited anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stampExpireDeposit: string;
|
||||
|
||||
/**
|
||||
* Signature by the exchange's master key over the denomination
|
||||
* information.
|
||||
*/
|
||||
@Checkable.String
|
||||
masterSig: string;
|
||||
|
||||
/**
|
||||
* Did we verify the signature on the denomination?
|
||||
*/
|
||||
@Checkable.Number
|
||||
status: DenominationStatus;
|
||||
|
||||
/**
|
||||
* Was this denomination still offered by the exchange the last time
|
||||
* we checked?
|
||||
* Only false when the exchange redacts a previously published denomination.
|
||||
*/
|
||||
@Checkable.Boolean
|
||||
isOffered: boolean;
|
||||
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => Denomination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exchange record as stored in the wallet's database.
|
||||
*/
|
||||
export interface ExchangeRecord {
|
||||
/**
|
||||
* Base url of the exchange.
|
||||
*/
|
||||
baseUrl: string;
|
||||
/**
|
||||
* Master public key of the exchange.
|
||||
*/
|
||||
masterPublicKey: string;
|
||||
/**
|
||||
* Auditors (partially) auditing the exchange.
|
||||
*/
|
||||
auditors: Auditor[];
|
||||
|
||||
/**
|
||||
* Currency that the exchange offers.
|
||||
*/
|
||||
currency: string;
|
||||
|
||||
/**
|
||||
* Timestamp for last update.
|
||||
*/
|
||||
lastUpdateTime: number;
|
||||
|
||||
/**
|
||||
* When did we actually use this exchange last (in milliseconds). If we
|
||||
* never used the exchange for anything but just updated its info, this is
|
||||
* set to 0. (Currently only updated when reserves are created.)
|
||||
*/
|
||||
lastUsedTime: number;
|
||||
|
||||
/**
|
||||
* Last observed protocol version.
|
||||
*/
|
||||
protocolVersion?: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A coin that isn't yet signed by an exchange.
|
||||
*/
|
||||
export interface PreCoinRecord {
|
||||
coinPub: string;
|
||||
coinPriv: string;
|
||||
reservePub: string;
|
||||
denomPub: string;
|
||||
blindingKey: string;
|
||||
withdrawSig: string;
|
||||
coinEv: string;
|
||||
exchangeBaseUrl: string;
|
||||
coinValue: AmountJson;
|
||||
/**
|
||||
* Set to true if this pre-coin came from a tip.
|
||||
* Until the tip is marked as "accepted", the resulting
|
||||
* coin will not be used for payments.
|
||||
*/
|
||||
isFromTip: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Planchet for a coin during refrehs.
|
||||
*/
|
||||
export interface RefreshPreCoinRecord {
|
||||
/**
|
||||
* Public key for the coin.
|
||||
*/
|
||||
publicKey: string;
|
||||
/**
|
||||
* Private key for the coin.
|
||||
*/
|
||||
privateKey: string;
|
||||
/**
|
||||
* Blinded public key.
|
||||
*/
|
||||
coinEv: string;
|
||||
/**
|
||||
* Blinding key used.
|
||||
*/
|
||||
blindingKey: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* State of returning a list of coins
|
||||
* to the customer's bank account.
|
||||
*/
|
||||
export interface CoinsReturnRecord {
|
||||
/**
|
||||
* Coins that we're returning.
|
||||
*/
|
||||
coins: CoinPaySig[];
|
||||
|
||||
/**
|
||||
* Responses to the deposit requests.
|
||||
*/
|
||||
responses: any;
|
||||
|
||||
/**
|
||||
* Ephemeral dummy merchant key for
|
||||
* the coins returns operation.
|
||||
*/
|
||||
dummyMerchantPub: string;
|
||||
|
||||
/**
|
||||
* Ephemeral dummy merchant key for
|
||||
* the coins returns operation.
|
||||
*/
|
||||
dummyMerchantPriv: string;
|
||||
|
||||
/**
|
||||
* Contract terms.
|
||||
*/
|
||||
contractTerms: string;
|
||||
|
||||
/**
|
||||
* Hash of contract terms.
|
||||
*/
|
||||
contractTermsHash: string;
|
||||
|
||||
/**
|
||||
* Wire info to send the money for the coins to.
|
||||
*/
|
||||
wire: object;
|
||||
|
||||
/**
|
||||
* Hash of the wire object.
|
||||
*/
|
||||
wireHash: string;
|
||||
|
||||
/**
|
||||
* All coins were deposited.
|
||||
*/
|
||||
finished: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of a coin.
|
||||
*/
|
||||
export enum CoinStatus {
|
||||
/**
|
||||
* Withdrawn and never shown to anybody.
|
||||
*/
|
||||
Fresh,
|
||||
/**
|
||||
* Currently planned to be sent to a merchant for a purchase.
|
||||
*/
|
||||
PurchasePending,
|
||||
/**
|
||||
* Used for a completed transaction and now dirty.
|
||||
*/
|
||||
Dirty,
|
||||
/**
|
||||
* A coin that was refreshed.
|
||||
*/
|
||||
Refreshed,
|
||||
/**
|
||||
* Coin marked to be paid back, but payback not finished.
|
||||
*/
|
||||
PaybackPending,
|
||||
/**
|
||||
* Coin fully paid back.
|
||||
*/
|
||||
PaybackDone,
|
||||
/**
|
||||
* Coin was dirty but can't be refreshed.
|
||||
*/
|
||||
Useless,
|
||||
/**
|
||||
* The coin was withdrawn for a tip that the user hasn't accepted yet.
|
||||
*/
|
||||
TainedByTip,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* CoinRecord as stored in the "coins" data store
|
||||
* of the wallet database.
|
||||
*/
|
||||
export interface CoinRecord {
|
||||
/**
|
||||
* Public key of the coin.
|
||||
*/
|
||||
coinPub: string;
|
||||
|
||||
/**
|
||||
* Private key to authorize operations on the coin.
|
||||
*/
|
||||
coinPriv: string;
|
||||
|
||||
/**
|
||||
* Key used by the exchange used to sign the coin.
|
||||
*/
|
||||
denomPub: string;
|
||||
|
||||
/**
|
||||
* Unblinded signature by the exchange.
|
||||
*/
|
||||
denomSig: string;
|
||||
|
||||
/**
|
||||
* Amount that's left on the coin.
|
||||
*/
|
||||
currentAmount: AmountJson;
|
||||
|
||||
/**
|
||||
* Base URL that identifies the exchange from which we got the
|
||||
* coin.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* We have withdrawn the coin, but it's not accepted by the exchange anymore.
|
||||
* We have to tell an auditor and wait for compensation or for the exchange
|
||||
* to fix it.
|
||||
*/
|
||||
suspended?: boolean;
|
||||
|
||||
/**
|
||||
* Blinding key used when withdrawing the coin.
|
||||
* Potentionally sed again during payback.
|
||||
*/
|
||||
blindingKey: string;
|
||||
|
||||
/**
|
||||
* Reserve public key for the reserve we got this coin from,
|
||||
* or zero when we got the coin from refresh.
|
||||
*/
|
||||
reservePub: string|undefined;
|
||||
|
||||
/**
|
||||
* Status of the coin.
|
||||
*/
|
||||
status: CoinStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* Proposal record, stored in the wallet's database.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ProposalRecord {
|
||||
/**
|
||||
* The contract that was offered by the merchant.
|
||||
*/
|
||||
@Checkable.Value(ContractTerms)
|
||||
contractTerms: ContractTerms;
|
||||
|
||||
/**
|
||||
* Signature by the merchant over the contract details.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchantSig: string;
|
||||
|
||||
/**
|
||||
* Hash of the contract terms.
|
||||
*/
|
||||
@Checkable.String
|
||||
contractTermsHash: string;
|
||||
|
||||
/**
|
||||
* Serial ID when the offer is stored in the wallet DB.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Number)
|
||||
id?: number;
|
||||
|
||||
/**
|
||||
* Timestamp (in ms) of when the record
|
||||
* was created.
|
||||
*/
|
||||
@Checkable.Number
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ProposalRecord;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire fees for an exchange.
|
||||
*/
|
||||
export interface ExchangeWireFeesRecord {
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Mapping from wire method type to the wire fee.
|
||||
*/
|
||||
feesForType: { [wireMethod: string]: WireFee[] };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of a tip we got from a merchant.
|
||||
*/
|
||||
export interface TipRecord {
|
||||
/**
|
||||
* Has the user accepted the tip? Only after the tip has been accepted coins
|
||||
* withdrawn from the tip may be used.
|
||||
*/
|
||||
accepted: boolean;
|
||||
|
||||
/**
|
||||
* The tipped amount.
|
||||
*/
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Coin public keys from the planchets.
|
||||
* This field is redundant and used for indexing the record via
|
||||
* a multi-entry index to look up tip records by coin public key.
|
||||
*/
|
||||
coinPubs: string[];
|
||||
|
||||
/**
|
||||
* Timestamp, the tip can't be picked up anymore after this deadline.
|
||||
*/
|
||||
deadline: number;
|
||||
|
||||
/**
|
||||
* The exchange that will sign our coins, chosen by the merchant.
|
||||
*/
|
||||
exchangeUrl: string;
|
||||
|
||||
/**
|
||||
* Domain of the merchant, necessary to uniquely identify the tip since
|
||||
* merchants can freely choose the ID and a malicious merchant might cause a
|
||||
* collision.
|
||||
*/
|
||||
merchantDomain: string;
|
||||
|
||||
/**
|
||||
* Planchets, the members included in TipPlanchetDetail will be sent to the
|
||||
* merchant.
|
||||
*/
|
||||
planchets: TipPlanchet[];
|
||||
|
||||
/**
|
||||
* Response if the merchant responded,
|
||||
* undefined otherwise.
|
||||
*/
|
||||
response?: TipResponse[];
|
||||
|
||||
/**
|
||||
* Identifier for the tip, chosen by the merchant.
|
||||
*/
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* URL to go to once the tip has been accepted.
|
||||
*/
|
||||
nextUrl: string;
|
||||
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ongoing refresh
|
||||
*/
|
||||
export interface RefreshSessionRecord {
|
||||
/**
|
||||
* Public key that's being melted in this session.
|
||||
*/
|
||||
meltCoinPub: string;
|
||||
|
||||
/**
|
||||
* How much of the coin's value is melted away
|
||||
* with this refresh session?
|
||||
*/
|
||||
valueWithFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Sum of the value of denominations we want
|
||||
* to withdraw in this session, without fees.
|
||||
*/
|
||||
valueOutput: AmountJson;
|
||||
|
||||
/**
|
||||
* Signature to confirm the melting.
|
||||
*/
|
||||
confirmSig: string;
|
||||
|
||||
/**
|
||||
* Hased denominations of the newly requested coins.
|
||||
*/
|
||||
newDenomHashes: string[];
|
||||
|
||||
/**
|
||||
* Denominations of the newly requested coins.
|
||||
*/
|
||||
newDenoms: string[];
|
||||
|
||||
/**
|
||||
* Precoins for each cut-and-choose instance.
|
||||
*/
|
||||
preCoinsForGammas: RefreshPreCoinRecord[][];
|
||||
|
||||
/**
|
||||
* The transfer keys, kappa of them.
|
||||
*/
|
||||
transferPubs: string[];
|
||||
|
||||
/**
|
||||
* Private keys for the transfer public keys.
|
||||
*/
|
||||
transferPrivs: string[];
|
||||
|
||||
/**
|
||||
* The no-reveal-index after we've done the melting.
|
||||
*/
|
||||
norevealIndex?: number;
|
||||
|
||||
/**
|
||||
* Hash of the session.
|
||||
*/
|
||||
hash: string;
|
||||
|
||||
/**
|
||||
* Base URL for the exchange we're doing the refresh with.
|
||||
*/
|
||||
exchangeBaseUrl: string;
|
||||
|
||||
/**
|
||||
* Is this session finished?
|
||||
*/
|
||||
finished: boolean;
|
||||
|
||||
/**
|
||||
* Record ID when retrieved from the DB.
|
||||
*/
|
||||
id?: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tipping planchet stored in the database.
|
||||
*/
|
||||
export interface TipPlanchet {
|
||||
blindingKey: string;
|
||||
coinEv: string;
|
||||
coinPriv: string;
|
||||
coinPub: string;
|
||||
coinValue: AmountJson;
|
||||
denomPubHash: string;
|
||||
denomPub: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire fee for one wire method as stored in the
|
||||
* wallet's database.
|
||||
*/
|
||||
export interface WireFee {
|
||||
/**
|
||||
* Fee for wire transfers.
|
||||
*/
|
||||
wireFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Fees to close and refund a reserve.
|
||||
*/
|
||||
closingFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Start date of the fee.
|
||||
*/
|
||||
startStamp: number;
|
||||
|
||||
/**
|
||||
* End date of the fee.
|
||||
*/
|
||||
endStamp: number;
|
||||
|
||||
/**
|
||||
* Signature made by the exchange master key.
|
||||
*/
|
||||
sig: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that stores status information about one purchase, starting from when
|
||||
* the customer accepts a proposal. Includes refund status if applicable.
|
||||
*/
|
||||
export interface PurchaseRecord {
|
||||
contractTermsHash: string;
|
||||
contractTerms: ContractTerms;
|
||||
payReq: PayReq;
|
||||
merchantSig: string;
|
||||
|
||||
/**
|
||||
* The purchase isn't active anymore, it's either successfully paid or
|
||||
* refunded/aborted.
|
||||
*/
|
||||
finished: boolean;
|
||||
|
||||
refundsPending: { [refundSig: string]: RefundPermission };
|
||||
refundsDone: { [refundSig: string]: RefundPermission };
|
||||
|
||||
/**
|
||||
* When was the purchase made?
|
||||
* Refers to the time that the user accepted.
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* When was the last refund made?
|
||||
* Set to 0 if no refund was made on the purchase.
|
||||
*/
|
||||
timestamp_refund: number;
|
||||
}
|
@ -21,7 +21,8 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import {AmountJson, Amounts} from "./types";
|
||||
import { AmountJson } from "./amounts";
|
||||
import * as Amounts from "./amounts";
|
||||
|
||||
import URI = require("urijs");
|
||||
|
||||
|
@ -825,7 +825,7 @@ export class QueryRoot {
|
||||
const req = tx.objectStore(store.name).get(key);
|
||||
req.onsuccess = () => {
|
||||
results.push(req.result);
|
||||
if (results.length == keys.length) {
|
||||
if (results.length === keys.length) {
|
||||
resolve(results);
|
||||
}
|
||||
};
|
||||
|
632
src/talerTypes.ts
Normal file
632
src/talerTypes.ts
Normal file
@ -0,0 +1,632 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2018 GNUnet e.V. and INRIA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type and schema definitions for the base taler protocol.
|
||||
*
|
||||
* All types here should be "@Checkable".
|
||||
*
|
||||
* Even though the rest of the wallet uses camelCase for fields, use snake_case
|
||||
* here, since that's the convention for the Taler JSON+HTTP API.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson } from "./amounts";
|
||||
import { Checkable } from "./checkable";
|
||||
|
||||
|
||||
/**
|
||||
* Denomination as found in the /keys response from the exchange.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class Denomination {
|
||||
/**
|
||||
* Value of one coin of the denomination.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
value: AmountJson;
|
||||
|
||||
/**
|
||||
* Public signing key of the denomination.
|
||||
*/
|
||||
@Checkable.String
|
||||
denom_pub: string;
|
||||
|
||||
/**
|
||||
* Fee for withdrawing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_withdraw: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for depositing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_deposit: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refreshing.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_refresh: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for refunding.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
fee_refund: AmountJson;
|
||||
|
||||
/**
|
||||
* Start date from which withdraw is allowed.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_start: string;
|
||||
|
||||
/**
|
||||
* End date for withdrawing.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_withdraw: string;
|
||||
|
||||
/**
|
||||
* Expiration date after which the exchange can forget about
|
||||
* the currency.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_legal: string;
|
||||
|
||||
/**
|
||||
* Date after which the coins of this denomination can't be
|
||||
* deposited anymore.
|
||||
*/
|
||||
@Checkable.String
|
||||
stamp_expire_deposit: string;
|
||||
|
||||
/**
|
||||
* Signature over the denomination information by the exchange's master
|
||||
* signing key.
|
||||
*/
|
||||
@Checkable.String
|
||||
master_sig: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => Denomination;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Signature by the auditor that a particular denomination key is audited.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class AuditorDenomSig {
|
||||
/**
|
||||
* Denomination public key's hash.
|
||||
*/
|
||||
@Checkable.String
|
||||
denom_pub_h: string;
|
||||
|
||||
/**
|
||||
* The signature.
|
||||
*/
|
||||
@Checkable.String
|
||||
auditor_sig: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Auditor information as given by the exchange in /keys.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class Auditor {
|
||||
/**
|
||||
* Auditor's public key.
|
||||
*/
|
||||
@Checkable.String
|
||||
auditor_pub: string;
|
||||
|
||||
/**
|
||||
* Base URL of the auditor.
|
||||
*/
|
||||
@Checkable.String
|
||||
auditor_url: string;
|
||||
|
||||
/**
|
||||
* List of signatures for denominations by the auditor.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(AuditorDenomSig))
|
||||
denomination_keys: AuditorDenomSig[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request that we send to the exchange to get a payback.
|
||||
*/
|
||||
export interface PaybackRequest {
|
||||
/**
|
||||
* Denomination public key of the coin we want to get
|
||||
* paid back.
|
||||
*/
|
||||
denom_pub: string;
|
||||
|
||||
/**
|
||||
* Signature over the coin public key by the denomination.
|
||||
*/
|
||||
denom_sig: string;
|
||||
|
||||
/**
|
||||
* Coin public key of the coin we want to refund.
|
||||
*/
|
||||
coin_pub: string;
|
||||
|
||||
/**
|
||||
* Blinding key that was used during withdraw,
|
||||
* used to prove that we were actually withdrawing the coin.
|
||||
*/
|
||||
coin_blind_key_secret: string;
|
||||
|
||||
/**
|
||||
* Signature made by the coin, authorizing the payback.
|
||||
*/
|
||||
coin_sig: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Response that we get from the exchange for a payback request.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class PaybackConfirmation {
|
||||
/**
|
||||
* public key of the reserve that will receive the payback.
|
||||
*/
|
||||
@Checkable.String
|
||||
reserve_pub: string;
|
||||
|
||||
/**
|
||||
* How much will the exchange pay back (needed by wallet in
|
||||
* case coin was partially spent and wallet got restored from backup)
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Time by which the exchange received the /payback request.
|
||||
*/
|
||||
@Checkable.String
|
||||
timestamp: string;
|
||||
|
||||
/**
|
||||
* the EdDSA signature of TALER_PaybackConfirmationPS using a current
|
||||
* signing key of the exchange affirming the successful
|
||||
* payback request, and that the exchange promises to transfer the funds
|
||||
* by the date specified (this allows the exchange delaying the transfer
|
||||
* a bit to aggregate additional payback requests into a larger one).
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange_sig: string;
|
||||
|
||||
/**
|
||||
* Public EdDSA key of the exchange that was used to generate the signature.
|
||||
* Should match one of the exchange's signing keys from /keys. It is given
|
||||
* explicitly as the client might otherwise be confused by clock skew as to
|
||||
* which signing key was used.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange_pub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => PaybackConfirmation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deposit permission for a single coin.
|
||||
*/
|
||||
export interface CoinPaySig {
|
||||
/**
|
||||
* Signature by the coin.
|
||||
*/
|
||||
coin_sig: string;
|
||||
/**
|
||||
* Public key of the coin being spend.
|
||||
*/
|
||||
coin_pub: string;
|
||||
/**
|
||||
* Signature made by the denomination public key.
|
||||
*/
|
||||
ub_sig: string;
|
||||
/**
|
||||
* The denomination public key associated with this coin.
|
||||
*/
|
||||
denom_pub: string;
|
||||
/**
|
||||
* The amount that is subtracted from this coin with this payment.
|
||||
*/
|
||||
contribution: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about an exchange as stored inside a
|
||||
* merchant's contract terms.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ExchangeHandle {
|
||||
/**
|
||||
* Master public signing key of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
master_pub: string;
|
||||
|
||||
/**
|
||||
* Base URL of the exchange.
|
||||
*/
|
||||
@Checkable.String
|
||||
url: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ExchangeHandle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Contract terms from a merchant.
|
||||
*/
|
||||
@Checkable.Class({validate: true})
|
||||
export class ContractTerms {
|
||||
static validate(x: ContractTerms) {
|
||||
if (x.exchanges.length === 0) {
|
||||
throw Error("no exchanges in contract terms");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash of the merchant's wire details.
|
||||
*/
|
||||
@Checkable.String
|
||||
H_wire: string;
|
||||
|
||||
/**
|
||||
* Wire method the merchant wants to use.
|
||||
*/
|
||||
@Checkable.String
|
||||
wire_method: string;
|
||||
|
||||
/**
|
||||
* Human-readable short summary of the contract.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
summary?: string;
|
||||
|
||||
/**
|
||||
* Nonce used to ensure freshness.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
nonce?: string;
|
||||
|
||||
/**
|
||||
* Total amount payable.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Auditors accepted by the merchant.
|
||||
*/
|
||||
@Checkable.List(Checkable.AnyObject)
|
||||
auditors: any[];
|
||||
|
||||
/**
|
||||
* Deadline to pay for the contract.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.String)
|
||||
pay_deadline: string;
|
||||
|
||||
/**
|
||||
* Delivery locations.
|
||||
*/
|
||||
@Checkable.Any
|
||||
locations: any;
|
||||
|
||||
/**
|
||||
* Maximum deposit fee covered by the merchant.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
max_fee: AmountJson;
|
||||
|
||||
/**
|
||||
* Information about the merchant.
|
||||
*/
|
||||
@Checkable.Any
|
||||
merchant: any;
|
||||
|
||||
/**
|
||||
* Public key of the merchant.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchant_pub: string;
|
||||
|
||||
/**
|
||||
* List of accepted exchanges.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(ExchangeHandle))
|
||||
exchanges: ExchangeHandle[];
|
||||
|
||||
/**
|
||||
* Products that are sold in this contract.
|
||||
*/
|
||||
@Checkable.List(Checkable.AnyObject)
|
||||
products: any[];
|
||||
|
||||
/**
|
||||
* Deadline for refunds.
|
||||
*/
|
||||
@Checkable.String
|
||||
refund_deadline: string;
|
||||
|
||||
/**
|
||||
* Time when the contract was generated by the merchant.
|
||||
*/
|
||||
@Checkable.String
|
||||
timestamp: string;
|
||||
|
||||
/**
|
||||
* Order id to uniquely identify the purchase within
|
||||
* one merchant instance.
|
||||
*/
|
||||
@Checkable.String
|
||||
order_id: string;
|
||||
|
||||
/**
|
||||
* URL to post the payment to.
|
||||
*/
|
||||
@Checkable.String
|
||||
pay_url: string;
|
||||
|
||||
/**
|
||||
* Fulfillment URL to view the product or
|
||||
* delivery status.
|
||||
*/
|
||||
@Checkable.String
|
||||
fulfillment_url: string;
|
||||
|
||||
/**
|
||||
* Share of the wire fee that must be settled with one payment.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Number)
|
||||
wire_fee_amortization?: number;
|
||||
|
||||
/**
|
||||
* Maximum wire fee that the merchant agrees to pay for.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Value(AmountJson))
|
||||
max_wire_fee?: AmountJson;
|
||||
|
||||
/**
|
||||
* Extra data, interpreted by the mechant only.
|
||||
*/
|
||||
@Checkable.Any
|
||||
extra: any;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ContractTerms;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Payment body sent to the merchant's /pay.
|
||||
*/
|
||||
export interface PayReq {
|
||||
/**
|
||||
* Coins with signature.
|
||||
*/
|
||||
coins: CoinPaySig[];
|
||||
|
||||
/**
|
||||
* The merchant public key, used to uniquely
|
||||
* identify the merchant instance.
|
||||
*/
|
||||
merchant_pub: string;
|
||||
|
||||
/**
|
||||
* Order ID that's being payed for.
|
||||
*/
|
||||
order_id: string;
|
||||
|
||||
/**
|
||||
* Exchange that the coins are from (base URL).
|
||||
*/
|
||||
exchange: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Refund permission in the format that the merchant gives it to us.
|
||||
*/
|
||||
export interface RefundPermission {
|
||||
/**
|
||||
* Amount to be refunded.
|
||||
*/
|
||||
refund_amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Fee for the refund.
|
||||
*/
|
||||
refund_fee: AmountJson;
|
||||
|
||||
/**
|
||||
* Contract terms hash to identify the contract that this
|
||||
* refund is for.
|
||||
*/
|
||||
h_contract_terms: string;
|
||||
|
||||
/**
|
||||
* Public key of the coin being refunded.
|
||||
*/
|
||||
coin_pub: string;
|
||||
|
||||
/**
|
||||
* Refund transaction ID between merchant and exchange.
|
||||
*/
|
||||
rtransaction_id: number;
|
||||
|
||||
/**
|
||||
* Public key of the merchant.
|
||||
*/
|
||||
merchant_pub: string;
|
||||
|
||||
/**
|
||||
* Signature made by the merchant over the refund permission.
|
||||
*/
|
||||
merchant_sig: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Planchet detail sent to the merchant.
|
||||
*/
|
||||
export interface TipPlanchetDetail {
|
||||
/**
|
||||
* Hashed denomination public key.
|
||||
*/
|
||||
denom_pub_hash: string;
|
||||
|
||||
/**
|
||||
* Coin's blinded public key.
|
||||
*/
|
||||
coin_ev: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request sent to the merchant to pick up a tip.
|
||||
*/
|
||||
export interface TipPickupRequest {
|
||||
/**
|
||||
* Identifier of the tip.
|
||||
*/
|
||||
tip_id: string;
|
||||
|
||||
/**
|
||||
* List of planchets the wallet wants to use for the tip.
|
||||
*/
|
||||
planchets: TipPlanchetDetail[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve signature, defined as separate class to facilitate
|
||||
* schema validation with "@Checkable".
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ReserveSigSingleton {
|
||||
/**
|
||||
* Reserve signature.
|
||||
*/
|
||||
@Checkable.String
|
||||
reserve_sig: string;
|
||||
|
||||
/**
|
||||
* Create a ReserveSigSingleton from untyped JSON.
|
||||
*/
|
||||
static checked: (obj: any) => ReserveSigSingleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Response of the merchant
|
||||
* to the TipPickupRequest.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class TipResponse {
|
||||
/**
|
||||
* Public key of the reserve
|
||||
*/
|
||||
@Checkable.String
|
||||
reserve_pub: string;
|
||||
|
||||
/**
|
||||
* The order of the signatures matches the planchets list.
|
||||
*/
|
||||
@Checkable.List(Checkable.Value(ReserveSigSingleton))
|
||||
reserve_sigs: ReserveSigSingleton[];
|
||||
|
||||
/**
|
||||
* Create a TipResponse from untyped JSON.
|
||||
*/
|
||||
static checked: (obj: any) => TipResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Token containing all the information for the wallet
|
||||
* to process a tip. Given by the merchant to the wallet.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class TipToken {
|
||||
/**
|
||||
* Expiration for the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
expiration: string;
|
||||
|
||||
/**
|
||||
* URL of the exchange that the tip can be withdrawn from.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange_url: string;
|
||||
|
||||
/**
|
||||
* Merchant's URL to pick up the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
pickup_url: string;
|
||||
|
||||
/**
|
||||
* Merchant-chosen tip identifier.
|
||||
*/
|
||||
@Checkable.String
|
||||
tip_id: string;
|
||||
|
||||
/**
|
||||
* Amount of tip.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* URL to navigate after finishing tip processing.
|
||||
*/
|
||||
@Checkable.String
|
||||
next_url: string;
|
||||
|
||||
/**
|
||||
* Create a TipToken from untyped JSON.
|
||||
* Validates the schema and throws on error.
|
||||
*/
|
||||
static checked: (obj: any) => TipToken;
|
||||
}
|
@ -14,17 +14,17 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import {test} from "ava";
|
||||
import {Amounts} from "./types";
|
||||
import * as types from "./types";
|
||||
import { test } from "ava";
|
||||
import * as Amounts from "./amounts";
|
||||
import { ContractTerms } from "./talerTypes";
|
||||
|
||||
const amt = (value: number, fraction: number, currency: string): types.AmountJson => ({value, fraction, currency});
|
||||
const amt = (value: number, fraction: number, currency: string): Amounts.AmountJson => ({value, fraction, currency});
|
||||
|
||||
test("amount addition (simple)", (t) => {
|
||||
const a1 = amt(1, 0, "EUR");
|
||||
const a2 = amt(1, 0, "EUR");
|
||||
const a3 = amt(2, 0, "EUR");
|
||||
t.true(0 === types.Amounts.cmp(Amounts.add(a1, a2).amount, a3));
|
||||
t.true(0 === Amounts.cmp(Amounts.add(a1, a2).amount, a3));
|
||||
t.pass();
|
||||
});
|
||||
|
||||
@ -39,7 +39,7 @@ test("amount subtraction (simple)", (t) => {
|
||||
const a1 = amt(2, 5, "EUR");
|
||||
const a2 = amt(1, 0, "EUR");
|
||||
const a3 = amt(1, 5, "EUR");
|
||||
t.true(0 === types.Amounts.cmp(Amounts.sub(a1, a2).amount, a3));
|
||||
t.true(0 === Amounts.cmp(Amounts.sub(a1, a2).amount, a3));
|
||||
t.pass();
|
||||
});
|
||||
|
||||
@ -73,13 +73,13 @@ test("contract terms validation", (t) => {
|
||||
wire_method: "test",
|
||||
};
|
||||
|
||||
types.ContractTerms.checked(c);
|
||||
ContractTerms.checked(c);
|
||||
|
||||
const c1 = JSON.parse(JSON.stringify(c));
|
||||
c1.exchanges = [];
|
||||
|
||||
try {
|
||||
types.ContractTerms.checked(c1);
|
||||
ContractTerms.checked(c1);
|
||||
} catch (e) {
|
||||
t.pass();
|
||||
return;
|
||||
|
2048
src/types.ts
2048
src/types.ts
File diff suppressed because it is too large
Load Diff
@ -15,13 +15,19 @@
|
||||
*/
|
||||
|
||||
|
||||
import {test} from "ava";
|
||||
import * as types from "./types";
|
||||
import { test } from "ava";
|
||||
|
||||
import * as dbTypes from "./dbTypes";
|
||||
import * as types from "./walletTypes";
|
||||
|
||||
import * as wallet from "./wallet";
|
||||
|
||||
import { AmountJson} from "./amounts";
|
||||
import * as Amounts from "./amounts";
|
||||
|
||||
function a(x: string): types.AmountJson {
|
||||
const amt = types.Amounts.parse(x);
|
||||
|
||||
function a(x: string): AmountJson {
|
||||
const amt = Amounts.parse(x);
|
||||
if (!amt) {
|
||||
throw Error("invalid amount");
|
||||
}
|
||||
@ -40,7 +46,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
|
||||
denomSig: "(mock)",
|
||||
exchangeBaseUrl: "(mock)",
|
||||
reservePub: "(mock)",
|
||||
status: types.CoinStatus.Fresh,
|
||||
status: dbTypes.CoinStatus.Fresh,
|
||||
},
|
||||
denom: {
|
||||
denomPub: "(mock)",
|
||||
@ -56,7 +62,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
|
||||
stampExpireLegal: "(mock)",
|
||||
stampExpireWithdraw: "(mock)",
|
||||
stampStart: "(mock)",
|
||||
status: types.DenominationStatus.VerifiedGood,
|
||||
status: dbTypes.DenominationStatus.VerifiedGood,
|
||||
value: a(value),
|
||||
},
|
||||
};
|
||||
|
136
src/wallet.ts
136
src/wallet.ts
@ -43,56 +43,64 @@ import {
|
||||
QueryRoot,
|
||||
Store,
|
||||
} from "./query";
|
||||
import {TimerGroup} from "./timer";
|
||||
import { TimerGroup } from "./timer";
|
||||
|
||||
import { AmountJson } from "./amounts";
|
||||
import * as Amounts from "./amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
Auditor,
|
||||
CheckPayResult,
|
||||
CoinPaySig,
|
||||
CoinRecord,
|
||||
CoinSelectionResult,
|
||||
CoinStatus,
|
||||
CoinWithDenom,
|
||||
ConfirmPayResult,
|
||||
ConfirmReserveRequest,
|
||||
ContractTerms,
|
||||
CreateReserveRequest,
|
||||
CreateReserveResponse,
|
||||
CurrencyRecord,
|
||||
Denomination,
|
||||
DenominationRecord,
|
||||
DenominationStatus,
|
||||
ExchangeHandle,
|
||||
ExchangeRecord,
|
||||
ExchangeWireFeesRecord,
|
||||
HistoryRecord,
|
||||
Notifier,
|
||||
PayCoinInfo,
|
||||
PayReq,
|
||||
PaybackConfirmation,
|
||||
PreCoinRecord,
|
||||
ProposalRecord,
|
||||
PurchaseRecord,
|
||||
QueryPaymentResult,
|
||||
RefreshPreCoinRecord,
|
||||
RefreshSessionRecord,
|
||||
RefundPermission,
|
||||
ReserveCreationInfo,
|
||||
ReserveRecord,
|
||||
TipRecord,
|
||||
WireFee,
|
||||
} from "./dbTypes";
|
||||
|
||||
import URI = require("urijs");
|
||||
|
||||
import {
|
||||
Auditor,
|
||||
CoinPaySig,
|
||||
ContractTerms,
|
||||
Denomination,
|
||||
ExchangeHandle,
|
||||
PayReq,
|
||||
PaybackConfirmation,
|
||||
RefundPermission,
|
||||
TipPlanchetDetail,
|
||||
TipResponse,
|
||||
} from "./talerTypes";
|
||||
import {
|
||||
CheckPayResult,
|
||||
CoinSelectionResult,
|
||||
CoinWithDenom,
|
||||
ConfirmPayResult,
|
||||
ConfirmReserveRequest,
|
||||
CreateReserveRequest,
|
||||
CreateReserveResponse,
|
||||
HistoryRecord,
|
||||
Notifier,
|
||||
PayCoinInfo,
|
||||
QueryPaymentResult,
|
||||
ReserveCreationInfo,
|
||||
ReturnCoinsRequest,
|
||||
SenderWireInfos,
|
||||
TipPlanchetDetail,
|
||||
TipRecord,
|
||||
TipResponse,
|
||||
TipStatus,
|
||||
WalletBalance,
|
||||
WalletBalanceEntry,
|
||||
WireFee,
|
||||
WireInfo,
|
||||
} from "./types";
|
||||
|
||||
import URI = require("urijs");
|
||||
} from "./walletTypes";
|
||||
|
||||
|
||||
/**
|
||||
@ -561,7 +569,9 @@ export namespace Stores {
|
||||
super("purchases", {keyPath: "contractTermsHash"});
|
||||
}
|
||||
|
||||
fulfillmentUrlIndex = new Index<string, PurchaseRecord>(this, "fulfillmentUrlIndex", "contractTerms.fulfillment_url");
|
||||
fulfillmentUrlIndex = new Index<string, PurchaseRecord>(this,
|
||||
"fulfillmentUrlIndex",
|
||||
"contractTerms.fulfillment_url");
|
||||
orderIdIndex = new Index<string, PurchaseRecord>(this, "orderIdIndex", "contractTerms.order_id");
|
||||
timestampIndex = new Index<string, PurchaseRecord>(this, "timestampIndex", "timestamp");
|
||||
}
|
||||
@ -1077,7 +1087,7 @@ export class Wallet {
|
||||
if (!sp) {
|
||||
return;
|
||||
}
|
||||
if (sp.proposalId != proposalId) {
|
||||
if (sp.proposalId !== proposalId) {
|
||||
return;
|
||||
}
|
||||
const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
|
||||
@ -1090,8 +1100,8 @@ export class Wallet {
|
||||
if (!currentCoin) {
|
||||
return;
|
||||
}
|
||||
if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) != 0) {
|
||||
return
|
||||
if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) !== 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return sp;
|
||||
@ -1135,7 +1145,7 @@ export class Wallet {
|
||||
}
|
||||
|
||||
// Only create speculative signature if we don't already have one for this proposal
|
||||
if ((!this.speculativePayData) || (this.speculativePayData && this.speculativePayData.proposalId != proposalId)) {
|
||||
if ((!this.speculativePayData) || (this.speculativePayData && this.speculativePayData.proposalId !== proposalId)) {
|
||||
const { exchangeUrl, cds } = res;
|
||||
const payCoinInfo = await this.cryptoApi.signDeposit(proposal.contractTerms, cds);
|
||||
this.speculativePayData = {
|
||||
@ -1250,7 +1260,7 @@ export class Wallet {
|
||||
.finish();
|
||||
|
||||
if (coin.status === CoinStatus.TainedByTip) {
|
||||
let tip = await this.q().getIndexed(Stores.tips.coinPubIndex, coin.coinPub);
|
||||
const tip = await this.q().getIndexed(Stores.tips.coinPubIndex, coin.coinPub);
|
||||
if (!tip) {
|
||||
throw Error(`inconsistent DB: tip for coin pub ${coin.coinPub} not found.`);
|
||||
}
|
||||
@ -1263,8 +1273,8 @@ export class Wallet {
|
||||
c.status = CoinStatus.Fresh;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin)
|
||||
};
|
||||
await this.q().mutate(Stores.coins, coin.coinPub, mutateCoin);
|
||||
// Show notifications only for accepted tips
|
||||
this.badge.showNotification();
|
||||
}
|
||||
@ -1724,6 +1734,7 @@ export class Wallet {
|
||||
const ret: ReserveCreationInfo = {
|
||||
earliestDepositExpiration,
|
||||
exchangeInfo,
|
||||
exchangeVersion: exchangeInfo.protocolVersion || "unknown",
|
||||
isAudited,
|
||||
isTrusted,
|
||||
numOfferedDenoms: possibleDenoms.length,
|
||||
@ -1731,11 +1742,10 @@ export class Wallet {
|
||||
selectedDenoms,
|
||||
trustedAuditorPubs,
|
||||
versionMatch,
|
||||
walletVersion: WALLET_PROTOCOL_VERSION,
|
||||
wireFees,
|
||||
wireInfo,
|
||||
withdrawFee: acc,
|
||||
exchangeVersion: exchangeInfo.protocolVersion || "unknown",
|
||||
walletVersion: WALLET_PROTOCOL_VERSION,
|
||||
};
|
||||
return ret;
|
||||
}
|
||||
@ -1779,7 +1789,7 @@ export class Wallet {
|
||||
.indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex,
|
||||
(e) => e.exchangeBaseUrl)
|
||||
.fold((cd: JoinLeftResult<CoinRecord, DenominationRecord>,
|
||||
suspendedCoins: CoinRecord[]) => {
|
||||
suspendedCoins: CoinRecord[]) => {
|
||||
if ((!cd.right) || (!cd.right.isOffered)) {
|
||||
return Array.prototype.concat(suspendedCoins, [cd.left]);
|
||||
}
|
||||
@ -1922,8 +1932,7 @@ export class Wallet {
|
||||
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
|
||||
exchangeInfo.baseUrl)
|
||||
.fold((x: DenominationRecord,
|
||||
acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc),
|
||||
{})
|
||||
acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc), {})
|
||||
);
|
||||
|
||||
const newDenoms: typeof existingDenoms = {};
|
||||
@ -2432,9 +2441,9 @@ export class Wallet {
|
||||
for (const tip of tips) {
|
||||
history.push({
|
||||
detail: {
|
||||
merchantDomain: tip.merchantDomain,
|
||||
amount: tip.amount,
|
||||
accepted: tip.accepted,
|
||||
amount: tip.amount,
|
||||
merchantDomain: tip.merchantDomain,
|
||||
tipId: tip.tipId,
|
||||
},
|
||||
timestamp: tip.timestamp,
|
||||
@ -2760,8 +2769,8 @@ export class Wallet {
|
||||
H_wire: coinsReturnRecord.contractTerms.H_wire,
|
||||
coin_pub: c.coinPaySig.coin_pub,
|
||||
coin_sig: c.coinPaySig.coin_sig,
|
||||
denom_pub: c.coinPaySig.denom_pub,
|
||||
contribution: c.coinPaySig.contribution,
|
||||
denom_pub: c.coinPaySig.denom_pub,
|
||||
h_contract_terms: coinsReturnRecord.contractTermsHash,
|
||||
merchant_pub: coinsReturnRecord.contractTerms.merchant_pub,
|
||||
pay_deadline: coinsReturnRecord.contractTerms.pay_deadline,
|
||||
@ -2950,7 +2959,12 @@ export class Wallet {
|
||||
* Get planchets for a tip. Creates new planchets if they don't exist already
|
||||
* for this tip. The tip is uniquely identified by the merchant's domain and the tip id.
|
||||
*/
|
||||
async getTipPlanchets(merchantDomain: string, tipId: string, amount: AmountJson, deadline: number, exchangeUrl: string, nextUrl: string): Promise<TipPlanchetDetail[]> {
|
||||
async getTipPlanchets(merchantDomain: string,
|
||||
tipId: string,
|
||||
amount: AmountJson,
|
||||
deadline: number,
|
||||
exchangeUrl: string,
|
||||
nextUrl: string): Promise<TipPlanchetDetail[]> {
|
||||
let tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
|
||||
if (!tipRecord) {
|
||||
await this.updateExchangeFromUrl(exchangeUrl);
|
||||
@ -2973,9 +2987,9 @@ export class Wallet {
|
||||
await this.q().put(Stores.tips, tipRecord).finish();
|
||||
}
|
||||
// Planchets in the form that the merchant expects
|
||||
const planchetDetail: TipPlanchetDetail[]= tipRecord.planchets.map((p) => ({
|
||||
denom_pub_hash: p.denomPubHash,
|
||||
const planchetDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
|
||||
coin_ev: p.coinEv,
|
||||
denom_pub_hash: p.denomPubHash,
|
||||
}));
|
||||
return planchetDetail;
|
||||
}
|
||||
@ -2985,7 +2999,7 @@ export class Wallet {
|
||||
* These coins will not appear in the wallet yet.
|
||||
*/
|
||||
async processTipResponse(merchantDomain: string, tipId: string, response: TipResponse): Promise<void> {
|
||||
let tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
|
||||
const tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
|
||||
if (!tipRecord) {
|
||||
throw Error("tip not found");
|
||||
}
|
||||
@ -2995,18 +3009,18 @@ export class Wallet {
|
||||
}
|
||||
|
||||
for (let i = 0; i < tipRecord.planchets.length; i++) {
|
||||
let planchet = tipRecord.planchets[i];
|
||||
let preCoin = {
|
||||
coinPub: planchet.coinPub,
|
||||
coinPriv: planchet.coinPriv,
|
||||
coinEv: planchet.coinEv,
|
||||
coinValue: planchet.coinValue,
|
||||
reservePub: response.reserve_pub,
|
||||
denomPub: planchet.denomPub,
|
||||
const planchet = tipRecord.planchets[i];
|
||||
const preCoin = {
|
||||
blindingKey: planchet.blindingKey,
|
||||
withdrawSig: response.reserve_sigs[i].reserve_sig,
|
||||
coinEv: planchet.coinEv,
|
||||
coinPriv: planchet.coinPriv,
|
||||
coinPub: planchet.coinPub,
|
||||
coinValue: planchet.coinValue,
|
||||
denomPub: planchet.denomPub,
|
||||
exchangeBaseUrl: tipRecord.exchangeUrl,
|
||||
isFromTip: true,
|
||||
reservePub: response.reserve_pub,
|
||||
withdrawSig: response.reserve_sigs[i].reserve_sig,
|
||||
};
|
||||
await this.q().put(Stores.precoins, preCoin);
|
||||
this.processPreCoin(preCoin);
|
||||
@ -3082,8 +3096,8 @@ export class Wallet {
|
||||
const gcProposal = (d: ProposalRecord, n: number) => {
|
||||
// Delete proposal after 60 minutes or 5 minutes before pay deadline,
|
||||
// whatever comes first.
|
||||
let deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
|
||||
let deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
|
||||
const deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
|
||||
const deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
|
||||
return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
|
||||
};
|
||||
await this.q().deleteIf(Stores.proposals, gcProposal).finish();
|
||||
@ -3096,7 +3110,7 @@ export class Wallet {
|
||||
}
|
||||
activeExchanges.push(d.baseUrl);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
await this.q().deleteIf(Stores.exchanges, gcExchange).finish();
|
||||
|
||||
|
572
src/walletTypes.ts
Normal file
572
src/walletTypes.ts
Normal file
@ -0,0 +1,572 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
(C) 2015-2017 GNUnet e.V. and INRIA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Types used by clients of the wallet.
|
||||
*
|
||||
* These types are defined in a separate file make tree shaking easier, since
|
||||
* some components use these types (via RPC) but do not depend on the wallet
|
||||
* code directly.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { Checkable } from "./checkable";
|
||||
import * as LibtoolVersion from "./libtoolVersion";
|
||||
|
||||
import { AmountJson } from "./amounts";
|
||||
|
||||
import {
|
||||
CoinRecord,
|
||||
DenominationRecord,
|
||||
ExchangeRecord,
|
||||
ExchangeWireFeesRecord,
|
||||
TipRecord,
|
||||
} from "./dbTypes";
|
||||
import {
|
||||
CoinPaySig,
|
||||
ContractTerms,
|
||||
PayReq,
|
||||
TipResponse,
|
||||
} from "./talerTypes";
|
||||
|
||||
|
||||
/**
|
||||
* Response for the create reserve request to the wallet.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class CreateReserveResponse {
|
||||
/**
|
||||
* Exchange URL where the bank should create the reserve.
|
||||
* The URL is canonicalized in the response.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange: string;
|
||||
|
||||
/**
|
||||
* Reserve public key of the newly created reserve.
|
||||
*/
|
||||
@Checkable.String
|
||||
reservePub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => CreateReserveResponse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire info, sent to the bank when creating a reserve. Fee information will
|
||||
* be filtered out. Only methods that the bank also supports should be sent.
|
||||
*/
|
||||
export interface WireInfo {
|
||||
/**
|
||||
* Mapping from wire method type to the exchange's wire info,
|
||||
* excluding fees.
|
||||
*/
|
||||
[type: string]: any;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about what will happen when creating a reserve.
|
||||
*
|
||||
* Sent to the wallet frontend to be rendered and shown to the user.
|
||||
*/
|
||||
export interface ReserveCreationInfo {
|
||||
/**
|
||||
* Exchange that the reserve will be created at.
|
||||
*/
|
||||
exchangeInfo: ExchangeRecord;
|
||||
|
||||
/**
|
||||
* Filtered wire info to send to the bank.
|
||||
*/
|
||||
wireInfo: WireInfo;
|
||||
|
||||
/**
|
||||
* Selected denominations for withdraw.
|
||||
*/
|
||||
selectedDenoms: DenominationRecord[];
|
||||
|
||||
/**
|
||||
* Fees for withdraw.
|
||||
*/
|
||||
withdrawFee: AmountJson;
|
||||
|
||||
/**
|
||||
* Remaining balance that is too small to be withdrawn.
|
||||
*/
|
||||
overhead: AmountJson;
|
||||
|
||||
/**
|
||||
* Wire fees from the exchange.
|
||||
*/
|
||||
wireFees: ExchangeWireFeesRecord;
|
||||
|
||||
/**
|
||||
* Does the wallet know about an auditor for
|
||||
* the exchange that the reserve.
|
||||
*/
|
||||
isAudited: boolean;
|
||||
|
||||
/**
|
||||
* The exchange is trusted directly.
|
||||
*/
|
||||
isTrusted: boolean;
|
||||
|
||||
/**
|
||||
* The earliest deposit expiration of the selected coins.
|
||||
*/
|
||||
earliestDepositExpiration: number;
|
||||
|
||||
/**
|
||||
* Number of currently offered denominations.
|
||||
*/
|
||||
numOfferedDenoms: number;
|
||||
/**
|
||||
* Public keys of trusted auditors for the currency we're withdrawing.
|
||||
*/
|
||||
trustedAuditorPubs: string[];
|
||||
|
||||
/**
|
||||
* Result of checking the wallet's version
|
||||
* against the exchange's version.
|
||||
*
|
||||
* Older exchanges don't return version information.
|
||||
*/
|
||||
versionMatch: LibtoolVersion.VersionMatchResult|undefined;
|
||||
|
||||
/**
|
||||
* Libtool-style version string for the exchange or "unknown"
|
||||
* for older exchanges.
|
||||
*/
|
||||
exchangeVersion: string;
|
||||
|
||||
/**
|
||||
* Libtool-style version string for the wallet.
|
||||
*/
|
||||
walletVersion: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Mapping from currency/exchange to detailed balance
|
||||
* information.
|
||||
*/
|
||||
export interface WalletBalance {
|
||||
/**
|
||||
* Mapping from currency name to detailed balance info.
|
||||
*/
|
||||
byExchange: { [exchangeBaseUrl: string]: WalletBalanceEntry };
|
||||
|
||||
/**
|
||||
* Mapping from currency name to detailed balance info.
|
||||
*/
|
||||
byCurrency: { [currency: string]: WalletBalanceEntry };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Detailed wallet balance for a particular currency.
|
||||
*/
|
||||
export interface WalletBalanceEntry {
|
||||
/**
|
||||
* Directly available amount.
|
||||
*/
|
||||
available: AmountJson;
|
||||
/**
|
||||
* Amount that we're waiting for (refresh, withdrawal).
|
||||
*/
|
||||
pendingIncoming: AmountJson;
|
||||
/**
|
||||
* Amount that's marked for a pending payment.
|
||||
*/
|
||||
pendingPayment: AmountJson;
|
||||
/**
|
||||
* Amount that was paid back and we could withdraw again.
|
||||
*/
|
||||
paybackAmount: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Coins used for a payment, with signatures authorizing the payment and the
|
||||
* coins with remaining value updated to accomodate for a payment.
|
||||
*/
|
||||
export interface PayCoinInfo {
|
||||
originalCoins: CoinRecord[];
|
||||
updatedCoins: CoinRecord[];
|
||||
sigs: CoinPaySig[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Listener for notifications from the wallet.
|
||||
*/
|
||||
export interface Notifier {
|
||||
/**
|
||||
* Called when a new notification arrives.
|
||||
*/
|
||||
notify(): void;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* For terseness.
|
||||
*/
|
||||
export function mkAmount(value: number, fraction: number, currency: string): AmountJson {
|
||||
return {value, fraction, currency};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Possible results for checkPay.
|
||||
*/
|
||||
export interface CheckPayResult {
|
||||
status: "paid" | "payment-possible" | "insufficient-balance";
|
||||
coinSelection?: CoinSelectionResult;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Possible results for confirmPay.
|
||||
*/
|
||||
export type ConfirmPayResult = "paid" | "insufficient-balance";
|
||||
|
||||
|
||||
/**
|
||||
* Activity history record.
|
||||
*/
|
||||
export interface HistoryRecord {
|
||||
/**
|
||||
* Type of the history event.
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* Time when the activity was recorded.
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* Subject of the entry. Used to group multiple history records together.
|
||||
* Only the latest history record with the same subjectId will be shown.
|
||||
*/
|
||||
subjectId?: string;
|
||||
|
||||
/**
|
||||
* Details used when rendering the history record.
|
||||
*/
|
||||
detail: any;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Response to a query payment request. Tagged union over the 'found' field.
|
||||
*/
|
||||
export type QueryPaymentResult = QueryPaymentNotFound | QueryPaymentFound;
|
||||
|
||||
|
||||
/**
|
||||
* Query payment response when the payment was found.
|
||||
*/
|
||||
export interface QueryPaymentNotFound {
|
||||
found: false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Query payment response when the payment wasn't found.
|
||||
*/
|
||||
export interface QueryPaymentFound {
|
||||
found: true;
|
||||
contractTermsHash: string;
|
||||
contractTerms: ContractTerms;
|
||||
payReq: PayReq;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Information about all sender wire details known to the wallet,
|
||||
* as well as exchanges that accept these wire types.
|
||||
*/
|
||||
export interface SenderWireInfos {
|
||||
/**
|
||||
* Mapping from exchange base url to list of accepted
|
||||
* wire types.
|
||||
*/
|
||||
exchangeWireTypes: { [exchangeBaseUrl: string]: string[] };
|
||||
|
||||
/**
|
||||
* Sender wire types stored in the wallet.
|
||||
*/
|
||||
senderWires: object[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to mark a reserve as confirmed.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class CreateReserveRequest {
|
||||
/**
|
||||
* The initial amount for the reserve.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Exchange URL where the bank should create the reserve.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange: string;
|
||||
|
||||
/**
|
||||
* Wire details for the bank account that sent the funds to the exchange.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Any)
|
||||
senderWire?: object;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => CreateReserveRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to mark a reserve as confirmed.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ConfirmReserveRequest {
|
||||
/**
|
||||
* Public key of then reserve that should be marked
|
||||
* as confirmed.
|
||||
*/
|
||||
@Checkable.String
|
||||
reservePub: string;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ConfirmReserveRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wire coins to the user's own bank account.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ReturnCoinsRequest {
|
||||
/**
|
||||
* The amount to wire.
|
||||
*/
|
||||
@Checkable.Value(AmountJson)
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* The exchange to take the coins from.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchange: string;
|
||||
|
||||
/**
|
||||
* Wire details for the bank account of the customer that will
|
||||
* receive the funds.
|
||||
*/
|
||||
@Checkable.Any
|
||||
senderWire?: object;
|
||||
|
||||
/**
|
||||
* Verify that a value matches the schema of this class and convert it into a
|
||||
* member.
|
||||
*/
|
||||
static checked: (obj: any) => ReturnCoinsRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Result of selecting coins, contains the exchange, and selected
|
||||
* coins with their denomination.
|
||||
*/
|
||||
export interface CoinSelectionResult {
|
||||
exchangeUrl: string;
|
||||
cds: CoinWithDenom[];
|
||||
totalFees: AmountJson;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Named tuple of coin and denomination.
|
||||
*/
|
||||
export interface CoinWithDenom {
|
||||
/**
|
||||
* A coin. Must have the same denomination public key as the associated
|
||||
* denomination.
|
||||
*/
|
||||
coin: CoinRecord;
|
||||
/**
|
||||
* An associated denomination.
|
||||
*/
|
||||
denom: DenominationRecord;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Status of processing a tip.
|
||||
*/
|
||||
export interface TipStatus {
|
||||
tip: TipRecord;
|
||||
rci?: ReserveCreationInfo;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request to the wallet for the status of processing a tip.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class TipStatusRequest {
|
||||
/**
|
||||
* Identifier of the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Merchant domain. Within each merchant domain, the tip identifier
|
||||
* uniquely identifies a tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchantDomain: string;
|
||||
|
||||
/**
|
||||
* Create a TipStatusRequest from untyped JSON.
|
||||
*/
|
||||
static checked: (obj: any) => TipStatusRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request to the wallet to accept a tip.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class AcceptTipRequest {
|
||||
/**
|
||||
* Identifier of the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Merchant domain. Within each merchant domain, the tip identifier
|
||||
* uniquely identifies a tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchantDomain: string;
|
||||
|
||||
/**
|
||||
* Create an AcceptTipRequest from untyped JSON.
|
||||
* Validates the schema and throws on error.
|
||||
*/
|
||||
static checked: (obj: any) => AcceptTipRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request for the wallet to process a tip response from a merchant.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class ProcessTipResponseRequest {
|
||||
/**
|
||||
* Identifier of the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Merchant domain. Within each merchant domain, the tip identifier
|
||||
* uniquely identifies a tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchantDomain: string;
|
||||
|
||||
/**
|
||||
* Tip response from the merchant.
|
||||
*/
|
||||
@Checkable.Value(TipResponse)
|
||||
tipResponse: TipResponse;
|
||||
|
||||
/**
|
||||
* Create an AcceptTipRequest from untyped JSON.
|
||||
* Validates the schema and throws on error.
|
||||
*/
|
||||
static checked: (obj: any) => ProcessTipResponseRequest;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Request for the wallet to generate tip planchets.
|
||||
*/
|
||||
@Checkable.Class()
|
||||
export class GetTipPlanchetsRequest {
|
||||
/**
|
||||
* Identifier of the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
tipId: string;
|
||||
|
||||
/**
|
||||
* Merchant domain. Within each merchant domain, the tip identifier
|
||||
* uniquely identifies a tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
merchantDomain: string;
|
||||
|
||||
/**
|
||||
* Amount of the tip.
|
||||
*/
|
||||
@Checkable.Optional(Checkable.Value(AmountJson))
|
||||
amount: AmountJson;
|
||||
|
||||
/**
|
||||
* Deadline for picking up the tip.
|
||||
*/
|
||||
@Checkable.Number
|
||||
deadline: number;
|
||||
|
||||
/**
|
||||
* Exchange URL that must be used to pick up the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
exchangeUrl: string;
|
||||
|
||||
/**
|
||||
* URL to nagivate to after processing the tip.
|
||||
*/
|
||||
@Checkable.String
|
||||
nextUrl: string;
|
||||
|
||||
/**
|
||||
* Create an AcceptTipRequest from untyped JSON.
|
||||
* Validates the schema and throws on error.
|
||||
*/
|
||||
static checked: (obj: any) => GetTipPlanchetsRequest;
|
||||
}
|
@ -21,7 +21,10 @@
|
||||
// Messages are already documented in wxApi.
|
||||
/* tslint:disable:completed-docs */
|
||||
|
||||
import * as types from "../types";
|
||||
import { AmountJson } from "../amounts";
|
||||
import * as dbTypes from "../dbTypes";
|
||||
import * as talerTypes from "../talerTypes";
|
||||
import * as walletTypes from "../walletTypes";
|
||||
|
||||
/**
|
||||
* Message type information.
|
||||
@ -29,7 +32,7 @@ import * as types from "../types";
|
||||
export interface MessageMap {
|
||||
"balances": {
|
||||
request: { };
|
||||
response: types.WalletBalance;
|
||||
response: walletTypes.WalletBalance;
|
||||
};
|
||||
"dump-db": {
|
||||
request: { };
|
||||
@ -55,7 +58,7 @@ export interface MessageMap {
|
||||
};
|
||||
"create-reserve": {
|
||||
request: {
|
||||
amount: types.AmountJson;
|
||||
amount: AmountJson;
|
||||
exchange: string
|
||||
};
|
||||
response: void;
|
||||
@ -70,11 +73,11 @@ export interface MessageMap {
|
||||
};
|
||||
"confirm-pay": {
|
||||
request: { proposalId: number; };
|
||||
response: types.ConfirmPayResult;
|
||||
response: walletTypes.ConfirmPayResult;
|
||||
};
|
||||
"check-pay": {
|
||||
request: { proposalId: number; };
|
||||
response: types.CheckPayResult;
|
||||
response: walletTypes.CheckPayResult;
|
||||
};
|
||||
"query-payment": {
|
||||
request: { };
|
||||
@ -82,31 +85,31 @@ export interface MessageMap {
|
||||
};
|
||||
"exchange-info": {
|
||||
request: { baseUrl: string };
|
||||
response: types.ExchangeRecord;
|
||||
response: dbTypes.ExchangeRecord;
|
||||
};
|
||||
"currency-info": {
|
||||
request: { name: string };
|
||||
response: types.CurrencyRecord;
|
||||
response: dbTypes.CurrencyRecord;
|
||||
};
|
||||
"hash-contract": {
|
||||
request: { contract: object };
|
||||
response: string;
|
||||
};
|
||||
"save-proposal": {
|
||||
request: { proposal: types.ProposalRecord };
|
||||
request: { proposal: dbTypes.ProposalRecord };
|
||||
response: void;
|
||||
};
|
||||
"reserve-creation-info": {
|
||||
request: { baseUrl: string, amount: types.AmountJson };
|
||||
response: types.ReserveCreationInfo;
|
||||
request: { baseUrl: string, amount: AmountJson };
|
||||
response: walletTypes.ReserveCreationInfo;
|
||||
};
|
||||
"get-history": {
|
||||
request: { };
|
||||
response: types.HistoryRecord[];
|
||||
response: walletTypes.HistoryRecord[];
|
||||
};
|
||||
"get-proposal": {
|
||||
request: { proposalId: number };
|
||||
response: types.ProposalRecord | undefined;
|
||||
response: dbTypes.ProposalRecord | undefined;
|
||||
};
|
||||
"get-coins": {
|
||||
request: { exchangeBaseUrl: string };
|
||||
@ -118,23 +121,23 @@ export interface MessageMap {
|
||||
};
|
||||
"get-currencies": {
|
||||
request: { };
|
||||
response: types.CurrencyRecord[];
|
||||
response: dbTypes.CurrencyRecord[];
|
||||
};
|
||||
"update-currency": {
|
||||
request: { currencyRecord: types.CurrencyRecord };
|
||||
request: { currencyRecord: dbTypes.CurrencyRecord };
|
||||
response: void;
|
||||
};
|
||||
"get-exchanges": {
|
||||
request: { };
|
||||
response: types.ExchangeRecord[];
|
||||
response: dbTypes.ExchangeRecord[];
|
||||
};
|
||||
"get-reserves": {
|
||||
request: { exchangeBaseUrl: string };
|
||||
response: types.ReserveRecord[];
|
||||
response: dbTypes.ReserveRecord[];
|
||||
};
|
||||
"get-payback-reserves": {
|
||||
request: { };
|
||||
response: types.ReserveRecord[];
|
||||
response: dbTypes.ReserveRecord[];
|
||||
};
|
||||
"withdraw-payback-reserve": {
|
||||
request: { reservePub: string };
|
||||
@ -142,11 +145,11 @@ export interface MessageMap {
|
||||
};
|
||||
"get-precoins": {
|
||||
request: { exchangeBaseUrl: string };
|
||||
response: types.PreCoinRecord[];
|
||||
response: dbTypes.PreCoinRecord[];
|
||||
};
|
||||
"get-denoms": {
|
||||
request: { exchangeBaseUrl: string };
|
||||
response: types.DenominationRecord[];
|
||||
response: dbTypes.DenominationRecord[];
|
||||
};
|
||||
"payback-coin": {
|
||||
request: { coinPub: string };
|
||||
@ -189,23 +192,23 @@ export interface MessageMap {
|
||||
response: void;
|
||||
};
|
||||
"get-full-refund-fees": {
|
||||
request: { refundPermissions: types.RefundPermission[] };
|
||||
request: { refundPermissions: talerTypes.RefundPermission[] };
|
||||
response: void;
|
||||
};
|
||||
"get-tip-planchets": {
|
||||
request: types.GetTipPlanchetsRequest;
|
||||
request: walletTypes.GetTipPlanchetsRequest;
|
||||
response: void;
|
||||
};
|
||||
"process-tip-response": {
|
||||
request: types.ProcessTipResponseRequest;
|
||||
request: walletTypes.ProcessTipResponseRequest;
|
||||
response: void;
|
||||
};
|
||||
"accept-tip": {
|
||||
request: types.AcceptTipRequest;
|
||||
request: walletTypes.AcceptTipRequest;
|
||||
response: void;
|
||||
};
|
||||
"get-tip-status": {
|
||||
request: types.TipStatusRequest;
|
||||
request: walletTypes.TipStatusRequest;
|
||||
response: void;
|
||||
};
|
||||
"clear-notification": {
|
||||
|
@ -29,7 +29,8 @@ import URI = require("urijs");
|
||||
import wxApi = require("./wxApi");
|
||||
|
||||
import { getTalerStampSec } from "../helpers";
|
||||
import { TipToken, QueryPaymentResult } from "../types";
|
||||
import { TipToken } from "../talerTypes";
|
||||
import { QueryPaymentResult } from "../walletTypes";
|
||||
|
||||
|
||||
import axios from "axios";
|
||||
@ -272,7 +273,12 @@ function talerPay(msg: any): Promise<any> {
|
||||
const merchantDomain = new URI(document.location.href).origin();
|
||||
let walletResp;
|
||||
try {
|
||||
walletResp = await wxApi.getTipPlanchets(merchantDomain, tipToken.tip_id, tipToken.amount, deadlineSec, tipToken.exchange_url, tipToken.next_url);
|
||||
walletResp = await wxApi.getTipPlanchets(merchantDomain,
|
||||
tipToken.tip_id,
|
||||
tipToken.amount,
|
||||
deadlineSec,
|
||||
tipToken.exchange_url,
|
||||
tipToken.next_url);
|
||||
} catch (e) {
|
||||
wxApi.logAndDisplayError({
|
||||
message: e.message,
|
||||
@ -283,12 +289,12 @@ function talerPay(msg: any): Promise<any> {
|
||||
throw e;
|
||||
}
|
||||
|
||||
let planchets = walletResp;
|
||||
const planchets = walletResp;
|
||||
|
||||
if (!planchets) {
|
||||
wxApi.logAndDisplayError({
|
||||
message: "processing tip failed",
|
||||
detail: walletResp,
|
||||
message: "processing tip failed",
|
||||
name: "tipping-failed",
|
||||
sameTab: true,
|
||||
});
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
import {
|
||||
CurrencyRecord,
|
||||
} from "../../types";
|
||||
} from "../../dbTypes";
|
||||
|
||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||
import {
|
||||
|
@ -25,7 +25,7 @@ import {
|
||||
AuditorRecord,
|
||||
CurrencyRecord,
|
||||
ExchangeForCurrencyRecord,
|
||||
} from "../../types";
|
||||
} from "../../dbTypes";
|
||||
|
||||
import {
|
||||
getCurrencies,
|
||||
|
@ -24,12 +24,15 @@
|
||||
* Imports.
|
||||
*/
|
||||
import * as i18n from "../../i18n";
|
||||
|
||||
import {
|
||||
CheckPayResult,
|
||||
ContractTerms,
|
||||
ExchangeRecord,
|
||||
ProposalRecord,
|
||||
} from "../../types";
|
||||
} from "../../dbTypes";
|
||||
import { ContractTerms } from "../../talerTypes";
|
||||
import {
|
||||
CheckPayResult,
|
||||
} from "../../walletTypes";
|
||||
|
||||
import { renderAmount } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
|
@ -24,13 +24,17 @@
|
||||
|
||||
import { canonicalizeBaseUrl } from "../../helpers";
|
||||
import * as i18n from "../../i18n";
|
||||
|
||||
import { AmountJson } from "../../amounts";
|
||||
import * as Amounts from "../../amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
CreateReserveResponse,
|
||||
CurrencyRecord,
|
||||
} from "../../dbTypes";
|
||||
import {
|
||||
CreateReserveResponse,
|
||||
ReserveCreationInfo,
|
||||
} from "../../types";
|
||||
} from "../../walletTypes";
|
||||
|
||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||
import {
|
||||
@ -40,7 +44,10 @@ import {
|
||||
getReserveCreationInfo,
|
||||
} from "../wxApi";
|
||||
|
||||
import { renderAmount, WithdrawDetailView } from "../renderHtml";
|
||||
import {
|
||||
WithdrawDetailView,
|
||||
renderAmount,
|
||||
} from "../renderHtml";
|
||||
|
||||
import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
@ -78,8 +85,6 @@ class EventTrigger {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
interface ExchangeSelectionProps {
|
||||
suggestedExchangeUrl: string;
|
||||
amount: AmountJson;
|
||||
@ -273,7 +278,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
if (rci.versionMatch.currentCmp === -1) {
|
||||
return (
|
||||
<p className="errorbox">
|
||||
Your wallet (protocol version <span>{rci.walletVersion}</span>) might be outdated. The exchange has a higher, incompatible
|
||||
Your wallet (protocol version <span>{rci.walletVersion}</span>) might be outdated.<span> </span>
|
||||
The exchange has a higher, incompatible
|
||||
protocol version (<span>{rci.exchangeVersion}</span>).
|
||||
</p>
|
||||
);
|
||||
@ -281,7 +287,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
if (rci.versionMatch.currentCmp === 1) {
|
||||
return (
|
||||
<p className="errorbox">
|
||||
The chosen exchange (protocol version <span>{rci.exchangeVersion}</span> might be outdated. The exchange has a lower, incompatible
|
||||
The chosen exchange (protocol version <span>{rci.exchangeVersion}</span> might be outdated.<span> </span>
|
||||
The exchange has a lower, incompatible
|
||||
protocol version than your wallet (protocol version <span>{rci.walletVersion}</span>).
|
||||
</p>
|
||||
);
|
||||
@ -429,8 +436,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
||||
amount_fraction: amount.fraction,
|
||||
amount_value: amount.value,
|
||||
exchange: resp.exchange,
|
||||
reserve_pub: resp.reservePub,
|
||||
exchange_wire_details: JSON.stringify(filteredWireDetails),
|
||||
reserve_pub: resp.reservePub,
|
||||
};
|
||||
const url = new URI(callback_url).addQuery(q);
|
||||
if (!url.is("absolute")) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
*/
|
||||
import {
|
||||
ReserveRecord,
|
||||
} from "../../types";
|
||||
} from "../../dbTypes";
|
||||
|
||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||
import { renderAmount } from "../renderHtml";
|
||||
|
@ -26,13 +26,15 @@
|
||||
* Imports.
|
||||
*/
|
||||
import * as i18n from "../../i18n";
|
||||
|
||||
import { AmountJson } from "../../amounts";
|
||||
import * as Amounts from "../../amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
HistoryRecord,
|
||||
WalletBalance,
|
||||
WalletBalanceEntry,
|
||||
} from "../../types";
|
||||
} from "../../walletTypes";
|
||||
|
||||
import { abbrev, renderAmount } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
@ -407,7 +409,8 @@ function formatHistoryItem(historyItem: HistoryRecord) {
|
||||
const url = tipPageUrl.query(params).href();
|
||||
return (
|
||||
<i18n.Translate wrap="p">
|
||||
Merchant <span>{d.merchantDomain}</span> gave a <a href={url} onClick={openTab(url)}> tip</a> of <span>{renderAmount(d.amount)}</span>.
|
||||
Merchant <span>{d.merchantDomain}</span> gave
|
||||
a <a href={url} onClick={openTab(url)}> tip</a> of <span>{renderAmount(d.amount)}</span>.
|
||||
<span> </span>
|
||||
{ d.accepted ? null : <span>You did not accept the tip yet.</span> }
|
||||
</i18n.Translate>
|
||||
|
@ -26,7 +26,10 @@ import * as React from "react";
|
||||
import * as ReactDOM from "react-dom";
|
||||
import URI = require("urijs");
|
||||
|
||||
import * as types from "../../types";
|
||||
import * as dbTypes from "../../dbTypes";
|
||||
|
||||
import { AmountJson } from "../../amounts";
|
||||
import * as Amounts from "../../amounts";
|
||||
|
||||
import { AmountDisplay } from "../renderHtml";
|
||||
import * as wxApi from "../wxApi";
|
||||
@ -36,14 +39,14 @@ interface RefundStatusViewProps {
|
||||
}
|
||||
|
||||
interface RefundStatusViewState {
|
||||
purchase?: types.PurchaseRecord;
|
||||
refundFees?: types.AmountJson;
|
||||
purchase?: dbTypes.PurchaseRecord;
|
||||
refundFees?: AmountJson;
|
||||
gotResult: boolean;
|
||||
}
|
||||
|
||||
interface RefundDetailProps {
|
||||
purchase: types.PurchaseRecord;
|
||||
fullRefundFees: types.AmountJson;
|
||||
purchase: dbTypes.PurchaseRecord;
|
||||
fullRefundFees: AmountJson;
|
||||
}
|
||||
|
||||
const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
|
||||
@ -59,13 +62,13 @@ const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
|
||||
throw Error("invariant");
|
||||
}
|
||||
|
||||
let amountPending = types.Amounts.getZero(currency);
|
||||
let amountPending = Amounts.getZero(currency);
|
||||
for (const k of pendingKeys) {
|
||||
amountPending = types.Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount;
|
||||
amountPending = Amounts.add(amountPending, purchase.refundsPending[k].refund_amount).amount;
|
||||
}
|
||||
let amountDone = types.Amounts.getZero(currency);
|
||||
let amountDone = Amounts.getZero(currency);
|
||||
for (const k of doneKeys) {
|
||||
amountDone = types.Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
|
||||
amountDone = Amounts.add(amountDone, purchase.refundsDone[k].refund_amount).amount;
|
||||
}
|
||||
|
||||
const hasPending = amountPending.fraction !== 0 || amountPending.value !== 0;
|
||||
|
@ -25,12 +25,13 @@
|
||||
* Imports.
|
||||
*/
|
||||
|
||||
import { AmountJson } from "../../amounts";
|
||||
import * as Amounts from "../../amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
SenderWireInfos,
|
||||
WalletBalance,
|
||||
} from "../../types";
|
||||
} from "../../walletTypes";
|
||||
|
||||
import * as i18n from "../../i18n";
|
||||
|
||||
|
@ -33,9 +33,13 @@ import {
|
||||
getTipStatus,
|
||||
} from "../wxApi";
|
||||
|
||||
import { renderAmount, WithdrawDetailView } from "../renderHtml";
|
||||
import {
|
||||
WithdrawDetailView,
|
||||
renderAmount,
|
||||
} from "../renderHtml";
|
||||
|
||||
import { Amounts, TipStatus } from "../../types";
|
||||
import * as Amounts from "../../amounts";
|
||||
import { TipStatus } from "../../walletTypes";
|
||||
|
||||
interface TipDisplayProps {
|
||||
merchantDomain: string;
|
||||
@ -54,7 +58,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
||||
}
|
||||
|
||||
async update() {
|
||||
let tipStatus = await getTipStatus(this.props.merchantDomain, this.props.tipId);
|
||||
const tipStatus = await getTipStatus(this.props.merchantDomain, this.props.tipId);
|
||||
this.setState({ tipStatus });
|
||||
}
|
||||
|
||||
@ -73,7 +77,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
||||
renderExchangeInfo(ts: TipStatus) {
|
||||
const rci = ts.rci;
|
||||
if (!rci) {
|
||||
return <p>Waiting for info about exchange ...</p>
|
||||
return <p>Waiting for info about exchange ...</p>;
|
||||
}
|
||||
const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
|
||||
return (
|
||||
@ -102,7 +106,9 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
||||
className="pure-button pure-button-primary"
|
||||
type="button"
|
||||
onClick={() => this.accept()}>
|
||||
{ this.state.working ? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span> : null }
|
||||
{ this.state.working
|
||||
? <span><object className="svg-icon svg-baseline" data="/img/spinner-bars.svg" /> </span>
|
||||
: null }
|
||||
Accept tip
|
||||
</button>
|
||||
{" "}
|
||||
@ -119,7 +125,8 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
||||
return (
|
||||
<div>
|
||||
<h2>Tip Received!</h2>
|
||||
<p>You received a tip of <strong>{renderAmount(ts.tip.amount)}</strong> from <strong>{this.props.merchantDomain}</strong>.</p>
|
||||
<p>You received a tip of <strong>{renderAmount(ts.tip.amount)}</strong> from <span> </span>
|
||||
<strong>{this.props.merchantDomain}</strong>.</p>
|
||||
{ts.tip.accepted
|
||||
? <p>You've accepted this tip! <a href={ts.tip.nextUrl}>Go back to merchant</a></p>
|
||||
: this.renderButtons()
|
||||
@ -135,9 +142,9 @@ async function main() {
|
||||
const url = new URI(document.location.href);
|
||||
const query: any = URI.parseQuery(url.query());
|
||||
|
||||
let merchantDomain = query.merchant_domain;
|
||||
let tipId = query.tip_id;
|
||||
let props: TipDisplayProps = { tipId, merchantDomain };
|
||||
const merchantDomain = query.merchant_domain;
|
||||
const tipId = query.tip_id;
|
||||
const props: TipDisplayProps = { tipId, merchantDomain };
|
||||
|
||||
ReactDOM.render(<TipDisplay {...props} />,
|
||||
document.getElementById("container")!);
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
|
||||
import { getTalerStampDate } from "../../helpers";
|
||||
|
||||
import {
|
||||
CoinRecord,
|
||||
CoinStatus,
|
||||
@ -29,7 +30,7 @@ import {
|
||||
ExchangeRecord,
|
||||
PreCoinRecord,
|
||||
ReserveRecord,
|
||||
} from "../../types";
|
||||
} from "../../dbTypes";
|
||||
|
||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||
import {
|
||||
|
@ -24,12 +24,16 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson } from "../amounts";
|
||||
import * as Amounts from "../amounts";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
DenominationRecord,
|
||||
} from "../dbTypes";
|
||||
import {
|
||||
ReserveCreationInfo,
|
||||
} from "../types";
|
||||
} from "../walletTypes";
|
||||
|
||||
|
||||
import { ImplicitStateComponent } from "./components";
|
||||
|
||||
@ -239,7 +243,9 @@ function FeeDetailsView(props: {rci: ReserveCreationInfo|null}): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Shows details about a withdraw request.
|
||||
*/
|
||||
export function WithdrawDetailView(props: {rci: ReserveCreationInfo | null}): JSX.Element {
|
||||
const rci = props.rci;
|
||||
return (
|
||||
@ -259,6 +265,9 @@ interface ExpanderTextProps {
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a heading with a toggle to show/hide the expandable content.
|
||||
*/
|
||||
export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
|
||||
private expanded = this.makeState<boolean>(false);
|
||||
private textArea: any = undefined;
|
||||
|
@ -22,26 +22,31 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson } from "../amounts";
|
||||
import {
|
||||
AmountJson,
|
||||
CheckPayResult,
|
||||
CoinRecord,
|
||||
ConfirmPayResult,
|
||||
CurrencyRecord,
|
||||
DenominationRecord,
|
||||
ExchangeRecord,
|
||||
PreCoinRecord,
|
||||
PurchaseRecord,
|
||||
QueryPaymentResult,
|
||||
RefundPermission,
|
||||
ReserveCreationInfo,
|
||||
ReserveRecord,
|
||||
} from "../dbTypes";
|
||||
import {
|
||||
CheckPayResult,
|
||||
ConfirmPayResult,
|
||||
QueryPaymentResult,
|
||||
ReserveCreationInfo,
|
||||
SenderWireInfos,
|
||||
TipResponse,
|
||||
TipPlanchetDetail,
|
||||
TipStatus,
|
||||
WalletBalance,
|
||||
} from "../types";
|
||||
} from "../walletTypes";
|
||||
|
||||
import {
|
||||
RefundPermission,
|
||||
TipPlanchetDetail,
|
||||
TipResponse,
|
||||
} from "../talerTypes";
|
||||
|
||||
import { MessageMap, MessageType } from "./messages";
|
||||
|
||||
@ -366,22 +371,39 @@ export function getFullRefundFees(args: { refundPermissions: RefundPermission[]
|
||||
/**
|
||||
* Get or generate planchets to give the merchant that wants to tip us.
|
||||
*/
|
||||
export function getTipPlanchets(merchantDomain: string, tipId: string, amount: AmountJson, deadline: number, exchangeUrl: string, nextUrl: string): Promise<TipPlanchetDetail[]> {
|
||||
export function getTipPlanchets(merchantDomain: string,
|
||||
tipId: string,
|
||||
amount: AmountJson,
|
||||
deadline: number,
|
||||
exchangeUrl: string,
|
||||
nextUrl: string): Promise<TipPlanchetDetail[]> {
|
||||
return callBackend("get-tip-planchets", { merchantDomain, tipId, amount, deadline, exchangeUrl, nextUrl });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the status of processing a tip.
|
||||
*/
|
||||
export function getTipStatus(merchantDomain: string, tipId: string): Promise<TipStatus> {
|
||||
return callBackend("get-tip-status", { merchantDomain, tipId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a tip as accepted by the user.
|
||||
*/
|
||||
export function acceptTip(merchantDomain: string, tipId: string): Promise<TipStatus> {
|
||||
return callBackend("accept-tip", { merchantDomain, tipId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a response from the merchant for a tip request.
|
||||
*/
|
||||
export function processTipResponse(merchantDomain: string, tipId: string, tipResponse: TipResponse): Promise<void> {
|
||||
return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse });
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear notifications that the wallet shows to the user.
|
||||
*/
|
||||
export function clearNotification(): Promise<void> {
|
||||
return callBackend("clear-notification", { });
|
||||
}
|
||||
|
@ -30,18 +30,21 @@ import {
|
||||
Index,
|
||||
Store,
|
||||
} from "../query";
|
||||
|
||||
import { AmountJson } from "../amounts";
|
||||
|
||||
import { ProposalRecord } from "../dbTypes";
|
||||
import {
|
||||
AcceptTipRequest,
|
||||
AmountJson,
|
||||
ConfirmReserveRequest,
|
||||
CreateReserveRequest,
|
||||
GetTipPlanchetsRequest,
|
||||
Notifier,
|
||||
ProcessTipResponseRequest,
|
||||
ProposalRecord,
|
||||
ReturnCoinsRequest,
|
||||
TipStatusRequest,
|
||||
} from "../types";
|
||||
} from "../walletTypes";
|
||||
|
||||
import {
|
||||
Stores,
|
||||
WALLET_DB_VERSION,
|
||||
@ -335,7 +338,12 @@ function handleMessage(sender: MessageSender,
|
||||
}
|
||||
case "get-tip-planchets": {
|
||||
const req = GetTipPlanchetsRequest.checked(detail);
|
||||
return needsWallet().getTipPlanchets(req.merchantDomain, req.tipId, req.amount, req.deadline, req.exchangeUrl, req.nextUrl);
|
||||
return needsWallet().getTipPlanchets(req.merchantDomain,
|
||||
req.tipId,
|
||||
req.amount,
|
||||
req.deadline,
|
||||
req.exchangeUrl,
|
||||
req.nextUrl);
|
||||
}
|
||||
case "clear-notification": {
|
||||
return needsWallet().clearNotification();
|
||||
@ -702,11 +710,10 @@ export async function wxMain() {
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Clear notifications both when the popop opens,
|
||||
// as well when it closes.
|
||||
chrome.runtime.onConnect.addListener((port) => {
|
||||
if (port.name == "popup") {
|
||||
if (port.name === "popup") {
|
||||
if (currentWallet) {
|
||||
currentWallet.clearNotification();
|
||||
}
|
||||
|
@ -21,12 +21,10 @@
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
/// <reference path="../decl/node.d.ts" />
|
||||
|
||||
"use strict";
|
||||
|
||||
import {readFileSync} from "fs";
|
||||
import {execSync} from "child_process";
|
||||
import { readFileSync } from "fs";
|
||||
import { execSync } from "child_process";
|
||||
import * as ts from "typescript";
|
||||
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"jsx": "react",
|
||||
@ -7,8 +8,8 @@
|
||||
"module": "commonjs",
|
||||
"sourceMap": true,
|
||||
"lib": [
|
||||
"ES6",
|
||||
"DOM"
|
||||
"es6",
|
||||
"dom"
|
||||
],
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
@ -23,6 +24,7 @@
|
||||
"decl/chrome/chrome.d.ts",
|
||||
"decl/jed.d.ts",
|
||||
"decl/urijs.d.ts",
|
||||
"src/amounts.ts",
|
||||
"src/checkable.ts",
|
||||
"src/crypto/cryptoApi-test.ts",
|
||||
"src/crypto/cryptoApi.ts",
|
||||
@ -34,6 +36,7 @@
|
||||
"src/crypto/nodeWorker.ts",
|
||||
"src/crypto/nodeWorkerEntry.ts",
|
||||
"src/crypto/startWorker.js",
|
||||
"src/dbTypes.ts",
|
||||
"src/helpers-test.ts",
|
||||
"src/helpers.ts",
|
||||
"src/http.ts",
|
||||
@ -42,18 +45,13 @@
|
||||
"src/libtoolVersion-test.ts",
|
||||
"src/libtoolVersion.ts",
|
||||
"src/logging.ts",
|
||||
"src/memidb/aatree-test.ts",
|
||||
"src/memidb/aatree.ts",
|
||||
"src/memidb/memidb-test.ts",
|
||||
"src/memidb/memidb.ts",
|
||||
"src/memidb/w3c-wpt/abort-in-initial-upgradeneeded-test.ts",
|
||||
"src/memidb/w3c-wpt/support.ts",
|
||||
"src/query.ts",
|
||||
"src/talerTypes.ts",
|
||||
"src/timer.ts",
|
||||
"src/types-test.ts",
|
||||
"src/types.ts",
|
||||
"src/wallet-test.ts",
|
||||
"src/wallet.ts",
|
||||
"src/walletTypes.ts",
|
||||
"src/webex/background.ts",
|
||||
"src/webex/chromeBadge.ts",
|
||||
"src/webex/components.ts",
|
||||
|
@ -5,6 +5,7 @@
|
||||
],
|
||||
"jsRules": {},
|
||||
"rules": {
|
||||
"arrow-parens": false,
|
||||
"max-line-length": {
|
||||
"options": [120]
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user