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.
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
{
|
{
|
||||||
// Use latest language servicesu
|
// Use latest language servicesu
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||||
// Defines space handling after a comma delimiter
|
// Defines space handling after a comma delimiter
|
||||||
"typescript.format.insertSpaceAfterCommaDelimiter": true,
|
"typescript.format.insertSpaceAfterCommaDelimiter": true,
|
||||||
// Defines space handling after a semicolon in a for statement
|
// Defines space handling after a semicolon in a for statement
|
||||||
@ -34,5 +34,6 @@
|
|||||||
},
|
},
|
||||||
"**/*.js.map": true
|
"**/*.js.map": true
|
||||||
},
|
},
|
||||||
|
"tslint.enable": true,
|
||||||
"editor.wrappingIndent": "same"
|
"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,
|
experimentalDecorators: true,
|
||||||
module: "commonjs",
|
module: "commonjs",
|
||||||
sourceMap: true,
|
sourceMap: true,
|
||||||
lib: ["ES6", "DOM"],
|
lib: ["es6", "dom"],
|
||||||
noImplicitReturns: true,
|
noImplicitReturns: true,
|
||||||
noFallthroughCasesInSwitch: true,
|
noFallthroughCasesInSwitch: true,
|
||||||
strict: true,
|
strict: true,
|
||||||
@ -266,6 +266,7 @@ gulp.task("pogen", function (cb) {
|
|||||||
*/
|
*/
|
||||||
function tsconfig(confBase) {
|
function tsconfig(confBase) {
|
||||||
let conf = {
|
let conf = {
|
||||||
|
compileOnSave: true,
|
||||||
compilerOptions: {},
|
compilerOptions: {},
|
||||||
files: []
|
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
|
// tslint:disable:max-line-length
|
||||||
|
|
||||||
import {test} from "ava";
|
import { test } from "ava";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationStatus,
|
DenominationStatus,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
} from "../types";
|
} from "../dbTypes";
|
||||||
|
|
||||||
import {CryptoApi} from "./cryptoApi";
|
import { CryptoApi } from "./cryptoApi";
|
||||||
|
|
||||||
const masterPub1: string = "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
|
const masterPub1: string = "CQQZ9DY3MZ1ARMN5K1VKDETS04Y2QCKMMCFHZSWJWWVN82BTTH00";
|
||||||
|
|
||||||
|
@ -23,20 +23,27 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinWithDenom,
|
|
||||||
ContractTerms,
|
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
PayCoinInfo,
|
|
||||||
PaybackRequest,
|
|
||||||
PreCoinRecord,
|
PreCoinRecord,
|
||||||
RefreshSessionRecord,
|
RefreshSessionRecord,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
TipPlanchet,
|
TipPlanchet,
|
||||||
WireFee,
|
WireFee,
|
||||||
} from "../types";
|
} from "../dbTypes";
|
||||||
|
|
||||||
|
import {
|
||||||
|
ContractTerms,
|
||||||
|
PaybackRequest,
|
||||||
|
} from "../talerTypes";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CoinWithDenom,
|
||||||
|
PayCoinInfo,
|
||||||
|
} from "../walletTypes";
|
||||||
|
|
||||||
import * as timer from "../timer";
|
import * as timer from "../timer";
|
||||||
|
|
||||||
|
@ -22,27 +22,33 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import * as Amounts from "../amounts";
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
canonicalJson,
|
|
||||||
} from "../helpers";
|
|
||||||
import {
|
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
CoinPaySig,
|
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
CoinWithDenom,
|
|
||||||
ContractTerms,
|
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
PayCoinInfo,
|
|
||||||
PaybackRequest,
|
|
||||||
PreCoinRecord,
|
PreCoinRecord,
|
||||||
RefreshPreCoinRecord,
|
RefreshPreCoinRecord,
|
||||||
RefreshSessionRecord,
|
RefreshSessionRecord,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
TipPlanchet,
|
TipPlanchet,
|
||||||
WireFee,
|
WireFee,
|
||||||
} from "../types";
|
} from "../dbTypes";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CoinPaySig,
|
||||||
|
ContractTerms,
|
||||||
|
PaybackRequest,
|
||||||
|
} from "../talerTypes";
|
||||||
|
|
||||||
|
import {
|
||||||
|
CoinWithDenom,
|
||||||
|
PayCoinInfo,
|
||||||
|
} from "../walletTypes";
|
||||||
|
|
||||||
|
import { canonicalJson } from "../helpers";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Amount,
|
Amount,
|
||||||
@ -112,6 +118,9 @@ namespace RpcFunctions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a planchet used for tipping, including the private keys.
|
||||||
|
*/
|
||||||
export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
export function createTipPlanchet(denom: DenominationRecord): TipPlanchet {
|
||||||
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
|
const denomPub = native.RsaPublicKey.fromCrock(denom.denomPub);
|
||||||
const coinPriv = native.EddsaPrivateKey.create();
|
const coinPriv = native.EddsaPrivateKey.create();
|
||||||
@ -134,8 +143,8 @@ namespace RpcFunctions {
|
|||||||
coinPriv: coinPriv.toCrock(),
|
coinPriv: coinPriv.toCrock(),
|
||||||
coinPub: coinPub.toCrock(),
|
coinPub: coinPub.toCrock(),
|
||||||
coinValue: denom.value,
|
coinValue: denom.value,
|
||||||
denomPubHash: denomPub.encode().hash().toCrock(),
|
|
||||||
denomPub: denomPub.encode().toCrock(),
|
denomPub: denomPub.encode().toCrock(),
|
||||||
|
denomPubHash: denomPub.encode().hash().toCrock(),
|
||||||
};
|
};
|
||||||
return tipPlanchet;
|
return tipPlanchet;
|
||||||
}
|
}
|
||||||
@ -263,8 +272,8 @@ namespace RpcFunctions {
|
|||||||
cds: CoinWithDenom[]): PayCoinInfo {
|
cds: CoinWithDenom[]): PayCoinInfo {
|
||||||
const ret: PayCoinInfo = {
|
const ret: PayCoinInfo = {
|
||||||
originalCoins: [],
|
originalCoins: [],
|
||||||
updatedCoins: [],
|
|
||||||
sigs: [],
|
sigs: [],
|
||||||
|
updatedCoins: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
const contractTermsHash = hashString(canonicalJson(contractTerms));
|
const contractTermsHash = hashString(canonicalJson(contractTerms));
|
||||||
@ -325,8 +334,8 @@ namespace RpcFunctions {
|
|||||||
const s: CoinPaySig = {
|
const s: CoinPaySig = {
|
||||||
coin_pub: cd.coin.coinPub,
|
coin_pub: cd.coin.coinPub,
|
||||||
coin_sig: coinSig,
|
coin_sig: coinSig,
|
||||||
denom_pub: cd.coin.denomPub,
|
|
||||||
contribution: coinSpend.toJson(),
|
contribution: coinSpend.toJson(),
|
||||||
|
denom_pub: cd.coin.denomPub,
|
||||||
ub_sig: cd.coin.denomSig,
|
ub_sig: cd.coin.denomSig,
|
||||||
};
|
};
|
||||||
ret.sigs.push(s);
|
ret.sigs.push(s);
|
||||||
|
@ -26,9 +26,9 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {AmountJson} from "../types";
|
import { AmountJson } from "../amounts";
|
||||||
|
|
||||||
import {EmscFunGen, getLib} from "./emscLoader";
|
import { EmscFunGen, getLib } from "./emscLoader";
|
||||||
|
|
||||||
const emscLib = getLib();
|
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.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import {AmountJson, Amounts} from "./types";
|
import { AmountJson } from "./amounts";
|
||||||
|
import * as Amounts from "./amounts";
|
||||||
|
|
||||||
import URI = require("urijs");
|
import URI = require("urijs");
|
||||||
|
|
||||||
|
@ -825,7 +825,7 @@ export class QueryRoot {
|
|||||||
const req = tx.objectStore(store.name).get(key);
|
const req = tx.objectStore(store.name).get(key);
|
||||||
req.onsuccess = () => {
|
req.onsuccess = () => {
|
||||||
results.push(req.result);
|
results.push(req.result);
|
||||||
if (results.length == keys.length) {
|
if (results.length === keys.length) {
|
||||||
resolve(results);
|
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/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {test} from "ava";
|
import { test } from "ava";
|
||||||
import {Amounts} from "./types";
|
import * as Amounts from "./amounts";
|
||||||
import * as types from "./types";
|
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) => {
|
test("amount addition (simple)", (t) => {
|
||||||
const a1 = amt(1, 0, "EUR");
|
const a1 = amt(1, 0, "EUR");
|
||||||
const a2 = amt(1, 0, "EUR");
|
const a2 = amt(1, 0, "EUR");
|
||||||
const a3 = amt(2, 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();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ test("amount subtraction (simple)", (t) => {
|
|||||||
const a1 = amt(2, 5, "EUR");
|
const a1 = amt(2, 5, "EUR");
|
||||||
const a2 = amt(1, 0, "EUR");
|
const a2 = amt(1, 0, "EUR");
|
||||||
const a3 = amt(1, 5, "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();
|
t.pass();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -73,13 +73,13 @@ test("contract terms validation", (t) => {
|
|||||||
wire_method: "test",
|
wire_method: "test",
|
||||||
};
|
};
|
||||||
|
|
||||||
types.ContractTerms.checked(c);
|
ContractTerms.checked(c);
|
||||||
|
|
||||||
const c1 = JSON.parse(JSON.stringify(c));
|
const c1 = JSON.parse(JSON.stringify(c));
|
||||||
c1.exchanges = [];
|
c1.exchanges = [];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
types.ContractTerms.checked(c1);
|
ContractTerms.checked(c1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
t.pass();
|
t.pass();
|
||||||
return;
|
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 { test } from "ava";
|
||||||
import * as types from "./types";
|
|
||||||
|
import * as dbTypes from "./dbTypes";
|
||||||
|
import * as types from "./walletTypes";
|
||||||
|
|
||||||
import * as wallet from "./wallet";
|
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) {
|
if (!amt) {
|
||||||
throw Error("invalid amount");
|
throw Error("invalid amount");
|
||||||
}
|
}
|
||||||
@ -40,7 +46,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
|
|||||||
denomSig: "(mock)",
|
denomSig: "(mock)",
|
||||||
exchangeBaseUrl: "(mock)",
|
exchangeBaseUrl: "(mock)",
|
||||||
reservePub: "(mock)",
|
reservePub: "(mock)",
|
||||||
status: types.CoinStatus.Fresh,
|
status: dbTypes.CoinStatus.Fresh,
|
||||||
},
|
},
|
||||||
denom: {
|
denom: {
|
||||||
denomPub: "(mock)",
|
denomPub: "(mock)",
|
||||||
@ -56,7 +62,7 @@ function fakeCwd(current: string, value: string, feeDeposit: string): types.Coin
|
|||||||
stampExpireLegal: "(mock)",
|
stampExpireLegal: "(mock)",
|
||||||
stampExpireWithdraw: "(mock)",
|
stampExpireWithdraw: "(mock)",
|
||||||
stampStart: "(mock)",
|
stampStart: "(mock)",
|
||||||
status: types.DenominationStatus.VerifiedGood,
|
status: dbTypes.DenominationStatus.VerifiedGood,
|
||||||
value: a(value),
|
value: a(value),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
136
src/wallet.ts
136
src/wallet.ts
@ -43,56 +43,64 @@ import {
|
|||||||
QueryRoot,
|
QueryRoot,
|
||||||
Store,
|
Store,
|
||||||
} from "./query";
|
} from "./query";
|
||||||
import {TimerGroup} from "./timer";
|
import { TimerGroup } from "./timer";
|
||||||
|
|
||||||
|
import { AmountJson } from "./amounts";
|
||||||
|
import * as Amounts from "./amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
Auditor,
|
|
||||||
CheckPayResult,
|
|
||||||
CoinPaySig,
|
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinSelectionResult,
|
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
CoinWithDenom,
|
|
||||||
ConfirmPayResult,
|
|
||||||
ConfirmReserveRequest,
|
|
||||||
ContractTerms,
|
|
||||||
CreateReserveRequest,
|
|
||||||
CreateReserveResponse,
|
|
||||||
CurrencyRecord,
|
CurrencyRecord,
|
||||||
Denomination,
|
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
DenominationStatus,
|
DenominationStatus,
|
||||||
ExchangeHandle,
|
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
ExchangeWireFeesRecord,
|
ExchangeWireFeesRecord,
|
||||||
HistoryRecord,
|
|
||||||
Notifier,
|
|
||||||
PayCoinInfo,
|
|
||||||
PayReq,
|
|
||||||
PaybackConfirmation,
|
|
||||||
PreCoinRecord,
|
PreCoinRecord,
|
||||||
ProposalRecord,
|
ProposalRecord,
|
||||||
PurchaseRecord,
|
PurchaseRecord,
|
||||||
QueryPaymentResult,
|
|
||||||
RefreshPreCoinRecord,
|
RefreshPreCoinRecord,
|
||||||
RefreshSessionRecord,
|
RefreshSessionRecord,
|
||||||
RefundPermission,
|
|
||||||
ReserveCreationInfo,
|
|
||||||
ReserveRecord,
|
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,
|
ReturnCoinsRequest,
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
TipPlanchetDetail,
|
|
||||||
TipRecord,
|
|
||||||
TipResponse,
|
|
||||||
TipStatus,
|
TipStatus,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
WalletBalanceEntry,
|
WalletBalanceEntry,
|
||||||
WireFee,
|
|
||||||
WireInfo,
|
WireInfo,
|
||||||
} from "./types";
|
|
||||||
|
|
||||||
import URI = require("urijs");
|
} from "./walletTypes";
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -561,7 +569,9 @@ export namespace Stores {
|
|||||||
super("purchases", {keyPath: "contractTermsHash"});
|
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");
|
orderIdIndex = new Index<string, PurchaseRecord>(this, "orderIdIndex", "contractTerms.order_id");
|
||||||
timestampIndex = new Index<string, PurchaseRecord>(this, "timestampIndex", "timestamp");
|
timestampIndex = new Index<string, PurchaseRecord>(this, "timestampIndex", "timestamp");
|
||||||
}
|
}
|
||||||
@ -1077,7 +1087,7 @@ export class Wallet {
|
|||||||
if (!sp) {
|
if (!sp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sp.proposalId != proposalId) {
|
if (sp.proposalId !== proposalId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
|
const coinKeys = sp.payCoinInfo.updatedCoins.map(x => x.coinPub);
|
||||||
@ -1090,8 +1100,8 @@ export class Wallet {
|
|||||||
if (!currentCoin) {
|
if (!currentCoin) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) != 0) {
|
if (Amounts.cmp(specCoin.currentAmount, currentCoin.currentAmount) !== 0) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sp;
|
return sp;
|
||||||
@ -1135,7 +1145,7 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Only create speculative signature if we don't already have one for this proposal
|
// 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 { exchangeUrl, cds } = res;
|
||||||
const payCoinInfo = await this.cryptoApi.signDeposit(proposal.contractTerms, cds);
|
const payCoinInfo = await this.cryptoApi.signDeposit(proposal.contractTerms, cds);
|
||||||
this.speculativePayData = {
|
this.speculativePayData = {
|
||||||
@ -1250,7 +1260,7 @@ export class Wallet {
|
|||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
if (coin.status === CoinStatus.TainedByTip) {
|
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) {
|
if (!tip) {
|
||||||
throw Error(`inconsistent DB: tip for coin pub ${coin.coinPub} not found.`);
|
throw Error(`inconsistent DB: tip for coin pub ${coin.coinPub} not found.`);
|
||||||
}
|
}
|
||||||
@ -1263,8 +1273,8 @@ export class Wallet {
|
|||||||
c.status = CoinStatus.Fresh;
|
c.status = CoinStatus.Fresh;
|
||||||
}
|
}
|
||||||
return c;
|
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
|
// Show notifications only for accepted tips
|
||||||
this.badge.showNotification();
|
this.badge.showNotification();
|
||||||
}
|
}
|
||||||
@ -1724,6 +1734,7 @@ export class Wallet {
|
|||||||
const ret: ReserveCreationInfo = {
|
const ret: ReserveCreationInfo = {
|
||||||
earliestDepositExpiration,
|
earliestDepositExpiration,
|
||||||
exchangeInfo,
|
exchangeInfo,
|
||||||
|
exchangeVersion: exchangeInfo.protocolVersion || "unknown",
|
||||||
isAudited,
|
isAudited,
|
||||||
isTrusted,
|
isTrusted,
|
||||||
numOfferedDenoms: possibleDenoms.length,
|
numOfferedDenoms: possibleDenoms.length,
|
||||||
@ -1731,11 +1742,10 @@ export class Wallet {
|
|||||||
selectedDenoms,
|
selectedDenoms,
|
||||||
trustedAuditorPubs,
|
trustedAuditorPubs,
|
||||||
versionMatch,
|
versionMatch,
|
||||||
|
walletVersion: WALLET_PROTOCOL_VERSION,
|
||||||
wireFees,
|
wireFees,
|
||||||
wireInfo,
|
wireInfo,
|
||||||
withdrawFee: acc,
|
withdrawFee: acc,
|
||||||
exchangeVersion: exchangeInfo.protocolVersion || "unknown",
|
|
||||||
walletVersion: WALLET_PROTOCOL_VERSION,
|
|
||||||
};
|
};
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -1779,7 +1789,7 @@ export class Wallet {
|
|||||||
.indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex,
|
.indexJoinLeft(Stores.denominations.exchangeBaseUrlIndex,
|
||||||
(e) => e.exchangeBaseUrl)
|
(e) => e.exchangeBaseUrl)
|
||||||
.fold((cd: JoinLeftResult<CoinRecord, DenominationRecord>,
|
.fold((cd: JoinLeftResult<CoinRecord, DenominationRecord>,
|
||||||
suspendedCoins: CoinRecord[]) => {
|
suspendedCoins: CoinRecord[]) => {
|
||||||
if ((!cd.right) || (!cd.right.isOffered)) {
|
if ((!cd.right) || (!cd.right.isOffered)) {
|
||||||
return Array.prototype.concat(suspendedCoins, [cd.left]);
|
return Array.prototype.concat(suspendedCoins, [cd.left]);
|
||||||
}
|
}
|
||||||
@ -1922,8 +1932,7 @@ export class Wallet {
|
|||||||
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
|
this.q().iterIndex(Stores.denominations.exchangeBaseUrlIndex,
|
||||||
exchangeInfo.baseUrl)
|
exchangeInfo.baseUrl)
|
||||||
.fold((x: DenominationRecord,
|
.fold((x: DenominationRecord,
|
||||||
acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc),
|
acc: typeof existingDenoms) => (acc[x.denomPub] = x, acc), {})
|
||||||
{})
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const newDenoms: typeof existingDenoms = {};
|
const newDenoms: typeof existingDenoms = {};
|
||||||
@ -2432,9 +2441,9 @@ export class Wallet {
|
|||||||
for (const tip of tips) {
|
for (const tip of tips) {
|
||||||
history.push({
|
history.push({
|
||||||
detail: {
|
detail: {
|
||||||
merchantDomain: tip.merchantDomain,
|
|
||||||
amount: tip.amount,
|
|
||||||
accepted: tip.accepted,
|
accepted: tip.accepted,
|
||||||
|
amount: tip.amount,
|
||||||
|
merchantDomain: tip.merchantDomain,
|
||||||
tipId: tip.tipId,
|
tipId: tip.tipId,
|
||||||
},
|
},
|
||||||
timestamp: tip.timestamp,
|
timestamp: tip.timestamp,
|
||||||
@ -2760,8 +2769,8 @@ export class Wallet {
|
|||||||
H_wire: coinsReturnRecord.contractTerms.H_wire,
|
H_wire: coinsReturnRecord.contractTerms.H_wire,
|
||||||
coin_pub: c.coinPaySig.coin_pub,
|
coin_pub: c.coinPaySig.coin_pub,
|
||||||
coin_sig: c.coinPaySig.coin_sig,
|
coin_sig: c.coinPaySig.coin_sig,
|
||||||
denom_pub: c.coinPaySig.denom_pub,
|
|
||||||
contribution: c.coinPaySig.contribution,
|
contribution: c.coinPaySig.contribution,
|
||||||
|
denom_pub: c.coinPaySig.denom_pub,
|
||||||
h_contract_terms: coinsReturnRecord.contractTermsHash,
|
h_contract_terms: coinsReturnRecord.contractTermsHash,
|
||||||
merchant_pub: coinsReturnRecord.contractTerms.merchant_pub,
|
merchant_pub: coinsReturnRecord.contractTerms.merchant_pub,
|
||||||
pay_deadline: coinsReturnRecord.contractTerms.pay_deadline,
|
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
|
* 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.
|
* 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]);
|
let tipRecord = await this.q().get(Stores.tips, [tipId, merchantDomain]);
|
||||||
if (!tipRecord) {
|
if (!tipRecord) {
|
||||||
await this.updateExchangeFromUrl(exchangeUrl);
|
await this.updateExchangeFromUrl(exchangeUrl);
|
||||||
@ -2973,9 +2987,9 @@ export class Wallet {
|
|||||||
await this.q().put(Stores.tips, tipRecord).finish();
|
await this.q().put(Stores.tips, tipRecord).finish();
|
||||||
}
|
}
|
||||||
// Planchets in the form that the merchant expects
|
// Planchets in the form that the merchant expects
|
||||||
const planchetDetail: TipPlanchetDetail[]= tipRecord.planchets.map((p) => ({
|
const planchetDetail: TipPlanchetDetail[] = tipRecord.planchets.map((p) => ({
|
||||||
denom_pub_hash: p.denomPubHash,
|
|
||||||
coin_ev: p.coinEv,
|
coin_ev: p.coinEv,
|
||||||
|
denom_pub_hash: p.denomPubHash,
|
||||||
}));
|
}));
|
||||||
return planchetDetail;
|
return planchetDetail;
|
||||||
}
|
}
|
||||||
@ -2985,7 +2999,7 @@ export class Wallet {
|
|||||||
* These coins will not appear in the wallet yet.
|
* These coins will not appear in the wallet yet.
|
||||||
*/
|
*/
|
||||||
async processTipResponse(merchantDomain: string, tipId: string, response: TipResponse): Promise<void> {
|
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) {
|
if (!tipRecord) {
|
||||||
throw Error("tip not found");
|
throw Error("tip not found");
|
||||||
}
|
}
|
||||||
@ -2995,18 +3009,18 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < tipRecord.planchets.length; i++) {
|
for (let i = 0; i < tipRecord.planchets.length; i++) {
|
||||||
let planchet = tipRecord.planchets[i];
|
const planchet = tipRecord.planchets[i];
|
||||||
let preCoin = {
|
const preCoin = {
|
||||||
coinPub: planchet.coinPub,
|
|
||||||
coinPriv: planchet.coinPriv,
|
|
||||||
coinEv: planchet.coinEv,
|
|
||||||
coinValue: planchet.coinValue,
|
|
||||||
reservePub: response.reserve_pub,
|
|
||||||
denomPub: planchet.denomPub,
|
|
||||||
blindingKey: planchet.blindingKey,
|
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,
|
exchangeBaseUrl: tipRecord.exchangeUrl,
|
||||||
isFromTip: true,
|
isFromTip: true,
|
||||||
|
reservePub: response.reserve_pub,
|
||||||
|
withdrawSig: response.reserve_sigs[i].reserve_sig,
|
||||||
};
|
};
|
||||||
await this.q().put(Stores.precoins, preCoin);
|
await this.q().put(Stores.precoins, preCoin);
|
||||||
this.processPreCoin(preCoin);
|
this.processPreCoin(preCoin);
|
||||||
@ -3082,8 +3096,8 @@ export class Wallet {
|
|||||||
const gcProposal = (d: ProposalRecord, n: number) => {
|
const gcProposal = (d: ProposalRecord, n: number) => {
|
||||||
// Delete proposal after 60 minutes or 5 minutes before pay deadline,
|
// Delete proposal after 60 minutes or 5 minutes before pay deadline,
|
||||||
// whatever comes first.
|
// whatever comes first.
|
||||||
let deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
|
const deadlinePayMilli = getTalerStampSec(d.contractTerms.pay_deadline)! * 1000;
|
||||||
let deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
|
const deadlineExpireMilli = nowMilli + (1000 * 60 * 60);
|
||||||
return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
|
return d.timestamp < Math.min(deadlinePayMilli, deadlineExpireMilli);
|
||||||
};
|
};
|
||||||
await this.q().deleteIf(Stores.proposals, gcProposal).finish();
|
await this.q().deleteIf(Stores.proposals, gcProposal).finish();
|
||||||
@ -3096,7 +3110,7 @@ export class Wallet {
|
|||||||
}
|
}
|
||||||
activeExchanges.push(d.baseUrl);
|
activeExchanges.push(d.baseUrl);
|
||||||
return false;
|
return false;
|
||||||
}
|
};
|
||||||
|
|
||||||
await this.q().deleteIf(Stores.exchanges, gcExchange).finish();
|
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.
|
// Messages are already documented in wxApi.
|
||||||
/* tslint:disable:completed-docs */
|
/* 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.
|
* Message type information.
|
||||||
@ -29,7 +32,7 @@ import * as types from "../types";
|
|||||||
export interface MessageMap {
|
export interface MessageMap {
|
||||||
"balances": {
|
"balances": {
|
||||||
request: { };
|
request: { };
|
||||||
response: types.WalletBalance;
|
response: walletTypes.WalletBalance;
|
||||||
};
|
};
|
||||||
"dump-db": {
|
"dump-db": {
|
||||||
request: { };
|
request: { };
|
||||||
@ -55,7 +58,7 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"create-reserve": {
|
"create-reserve": {
|
||||||
request: {
|
request: {
|
||||||
amount: types.AmountJson;
|
amount: AmountJson;
|
||||||
exchange: string
|
exchange: string
|
||||||
};
|
};
|
||||||
response: void;
|
response: void;
|
||||||
@ -70,11 +73,11 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"confirm-pay": {
|
"confirm-pay": {
|
||||||
request: { proposalId: number; };
|
request: { proposalId: number; };
|
||||||
response: types.ConfirmPayResult;
|
response: walletTypes.ConfirmPayResult;
|
||||||
};
|
};
|
||||||
"check-pay": {
|
"check-pay": {
|
||||||
request: { proposalId: number; };
|
request: { proposalId: number; };
|
||||||
response: types.CheckPayResult;
|
response: walletTypes.CheckPayResult;
|
||||||
};
|
};
|
||||||
"query-payment": {
|
"query-payment": {
|
||||||
request: { };
|
request: { };
|
||||||
@ -82,31 +85,31 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"exchange-info": {
|
"exchange-info": {
|
||||||
request: { baseUrl: string };
|
request: { baseUrl: string };
|
||||||
response: types.ExchangeRecord;
|
response: dbTypes.ExchangeRecord;
|
||||||
};
|
};
|
||||||
"currency-info": {
|
"currency-info": {
|
||||||
request: { name: string };
|
request: { name: string };
|
||||||
response: types.CurrencyRecord;
|
response: dbTypes.CurrencyRecord;
|
||||||
};
|
};
|
||||||
"hash-contract": {
|
"hash-contract": {
|
||||||
request: { contract: object };
|
request: { contract: object };
|
||||||
response: string;
|
response: string;
|
||||||
};
|
};
|
||||||
"save-proposal": {
|
"save-proposal": {
|
||||||
request: { proposal: types.ProposalRecord };
|
request: { proposal: dbTypes.ProposalRecord };
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"reserve-creation-info": {
|
"reserve-creation-info": {
|
||||||
request: { baseUrl: string, amount: types.AmountJson };
|
request: { baseUrl: string, amount: AmountJson };
|
||||||
response: types.ReserveCreationInfo;
|
response: walletTypes.ReserveCreationInfo;
|
||||||
};
|
};
|
||||||
"get-history": {
|
"get-history": {
|
||||||
request: { };
|
request: { };
|
||||||
response: types.HistoryRecord[];
|
response: walletTypes.HistoryRecord[];
|
||||||
};
|
};
|
||||||
"get-proposal": {
|
"get-proposal": {
|
||||||
request: { proposalId: number };
|
request: { proposalId: number };
|
||||||
response: types.ProposalRecord | undefined;
|
response: dbTypes.ProposalRecord | undefined;
|
||||||
};
|
};
|
||||||
"get-coins": {
|
"get-coins": {
|
||||||
request: { exchangeBaseUrl: string };
|
request: { exchangeBaseUrl: string };
|
||||||
@ -118,23 +121,23 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"get-currencies": {
|
"get-currencies": {
|
||||||
request: { };
|
request: { };
|
||||||
response: types.CurrencyRecord[];
|
response: dbTypes.CurrencyRecord[];
|
||||||
};
|
};
|
||||||
"update-currency": {
|
"update-currency": {
|
||||||
request: { currencyRecord: types.CurrencyRecord };
|
request: { currencyRecord: dbTypes.CurrencyRecord };
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"get-exchanges": {
|
"get-exchanges": {
|
||||||
request: { };
|
request: { };
|
||||||
response: types.ExchangeRecord[];
|
response: dbTypes.ExchangeRecord[];
|
||||||
};
|
};
|
||||||
"get-reserves": {
|
"get-reserves": {
|
||||||
request: { exchangeBaseUrl: string };
|
request: { exchangeBaseUrl: string };
|
||||||
response: types.ReserveRecord[];
|
response: dbTypes.ReserveRecord[];
|
||||||
};
|
};
|
||||||
"get-payback-reserves": {
|
"get-payback-reserves": {
|
||||||
request: { };
|
request: { };
|
||||||
response: types.ReserveRecord[];
|
response: dbTypes.ReserveRecord[];
|
||||||
};
|
};
|
||||||
"withdraw-payback-reserve": {
|
"withdraw-payback-reserve": {
|
||||||
request: { reservePub: string };
|
request: { reservePub: string };
|
||||||
@ -142,11 +145,11 @@ export interface MessageMap {
|
|||||||
};
|
};
|
||||||
"get-precoins": {
|
"get-precoins": {
|
||||||
request: { exchangeBaseUrl: string };
|
request: { exchangeBaseUrl: string };
|
||||||
response: types.PreCoinRecord[];
|
response: dbTypes.PreCoinRecord[];
|
||||||
};
|
};
|
||||||
"get-denoms": {
|
"get-denoms": {
|
||||||
request: { exchangeBaseUrl: string };
|
request: { exchangeBaseUrl: string };
|
||||||
response: types.DenominationRecord[];
|
response: dbTypes.DenominationRecord[];
|
||||||
};
|
};
|
||||||
"payback-coin": {
|
"payback-coin": {
|
||||||
request: { coinPub: string };
|
request: { coinPub: string };
|
||||||
@ -189,23 +192,23 @@ export interface MessageMap {
|
|||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"get-full-refund-fees": {
|
"get-full-refund-fees": {
|
||||||
request: { refundPermissions: types.RefundPermission[] };
|
request: { refundPermissions: talerTypes.RefundPermission[] };
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"get-tip-planchets": {
|
"get-tip-planchets": {
|
||||||
request: types.GetTipPlanchetsRequest;
|
request: walletTypes.GetTipPlanchetsRequest;
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"process-tip-response": {
|
"process-tip-response": {
|
||||||
request: types.ProcessTipResponseRequest;
|
request: walletTypes.ProcessTipResponseRequest;
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"accept-tip": {
|
"accept-tip": {
|
||||||
request: types.AcceptTipRequest;
|
request: walletTypes.AcceptTipRequest;
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"get-tip-status": {
|
"get-tip-status": {
|
||||||
request: types.TipStatusRequest;
|
request: walletTypes.TipStatusRequest;
|
||||||
response: void;
|
response: void;
|
||||||
};
|
};
|
||||||
"clear-notification": {
|
"clear-notification": {
|
||||||
|
@ -29,7 +29,8 @@ import URI = require("urijs");
|
|||||||
import wxApi = require("./wxApi");
|
import wxApi = require("./wxApi");
|
||||||
|
|
||||||
import { getTalerStampSec } from "../helpers";
|
import { getTalerStampSec } from "../helpers";
|
||||||
import { TipToken, QueryPaymentResult } from "../types";
|
import { TipToken } from "../talerTypes";
|
||||||
|
import { QueryPaymentResult } from "../walletTypes";
|
||||||
|
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
@ -272,7 +273,12 @@ function talerPay(msg: any): Promise<any> {
|
|||||||
const merchantDomain = new URI(document.location.href).origin();
|
const merchantDomain = new URI(document.location.href).origin();
|
||||||
let walletResp;
|
let walletResp;
|
||||||
try {
|
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) {
|
} catch (e) {
|
||||||
wxApi.logAndDisplayError({
|
wxApi.logAndDisplayError({
|
||||||
message: e.message,
|
message: e.message,
|
||||||
@ -283,12 +289,12 @@ function talerPay(msg: any): Promise<any> {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
let planchets = walletResp;
|
const planchets = walletResp;
|
||||||
|
|
||||||
if (!planchets) {
|
if (!planchets) {
|
||||||
wxApi.logAndDisplayError({
|
wxApi.logAndDisplayError({
|
||||||
message: "processing tip failed",
|
|
||||||
detail: walletResp,
|
detail: walletResp,
|
||||||
|
message: "processing tip failed",
|
||||||
name: "tipping-failed",
|
name: "tipping-failed",
|
||||||
sameTab: true,
|
sameTab: true,
|
||||||
});
|
});
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
CurrencyRecord,
|
CurrencyRecord,
|
||||||
} from "../../types";
|
} from "../../dbTypes";
|
||||||
|
|
||||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||||
import {
|
import {
|
||||||
|
@ -25,7 +25,7 @@ import {
|
|||||||
AuditorRecord,
|
AuditorRecord,
|
||||||
CurrencyRecord,
|
CurrencyRecord,
|
||||||
ExchangeForCurrencyRecord,
|
ExchangeForCurrencyRecord,
|
||||||
} from "../../types";
|
} from "../../dbTypes";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getCurrencies,
|
getCurrencies,
|
||||||
|
@ -24,12 +24,15 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import * as i18n from "../../i18n";
|
import * as i18n from "../../i18n";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CheckPayResult,
|
|
||||||
ContractTerms,
|
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
ProposalRecord,
|
ProposalRecord,
|
||||||
} from "../../types";
|
} from "../../dbTypes";
|
||||||
|
import { ContractTerms } from "../../talerTypes";
|
||||||
|
import {
|
||||||
|
CheckPayResult,
|
||||||
|
} from "../../walletTypes";
|
||||||
|
|
||||||
import { renderAmount } from "../renderHtml";
|
import { renderAmount } from "../renderHtml";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
|
@ -24,13 +24,17 @@
|
|||||||
|
|
||||||
import { canonicalizeBaseUrl } from "../../helpers";
|
import { canonicalizeBaseUrl } from "../../helpers";
|
||||||
import * as i18n from "../../i18n";
|
import * as i18n from "../../i18n";
|
||||||
|
|
||||||
|
import { AmountJson } from "../../amounts";
|
||||||
|
import * as Amounts from "../../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
CreateReserveResponse,
|
|
||||||
CurrencyRecord,
|
CurrencyRecord,
|
||||||
|
} from "../../dbTypes";
|
||||||
|
import {
|
||||||
|
CreateReserveResponse,
|
||||||
ReserveCreationInfo,
|
ReserveCreationInfo,
|
||||||
} from "../../types";
|
} from "../../walletTypes";
|
||||||
|
|
||||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||||
import {
|
import {
|
||||||
@ -40,7 +44,10 @@ import {
|
|||||||
getReserveCreationInfo,
|
getReserveCreationInfo,
|
||||||
} from "../wxApi";
|
} from "../wxApi";
|
||||||
|
|
||||||
import { renderAmount, WithdrawDetailView } from "../renderHtml";
|
import {
|
||||||
|
WithdrawDetailView,
|
||||||
|
renderAmount,
|
||||||
|
} from "../renderHtml";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
@ -78,8 +85,6 @@ class EventTrigger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface ExchangeSelectionProps {
|
interface ExchangeSelectionProps {
|
||||||
suggestedExchangeUrl: string;
|
suggestedExchangeUrl: string;
|
||||||
amount: AmountJson;
|
amount: AmountJson;
|
||||||
@ -273,7 +278,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
|||||||
if (rci.versionMatch.currentCmp === -1) {
|
if (rci.versionMatch.currentCmp === -1) {
|
||||||
return (
|
return (
|
||||||
<p className="errorbox">
|
<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>).
|
protocol version (<span>{rci.exchangeVersion}</span>).
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
@ -281,7 +287,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
|||||||
if (rci.versionMatch.currentCmp === 1) {
|
if (rci.versionMatch.currentCmp === 1) {
|
||||||
return (
|
return (
|
||||||
<p className="errorbox">
|
<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>).
|
protocol version than your wallet (protocol version <span>{rci.walletVersion}</span>).
|
||||||
</p>
|
</p>
|
||||||
);
|
);
|
||||||
@ -429,8 +436,8 @@ class ExchangeSelection extends ImplicitStateComponent<ExchangeSelectionProps> {
|
|||||||
amount_fraction: amount.fraction,
|
amount_fraction: amount.fraction,
|
||||||
amount_value: amount.value,
|
amount_value: amount.value,
|
||||||
exchange: resp.exchange,
|
exchange: resp.exchange,
|
||||||
reserve_pub: resp.reservePub,
|
|
||||||
exchange_wire_details: JSON.stringify(filteredWireDetails),
|
exchange_wire_details: JSON.stringify(filteredWireDetails),
|
||||||
|
reserve_pub: resp.reservePub,
|
||||||
};
|
};
|
||||||
const url = new URI(callback_url).addQuery(q);
|
const url = new URI(callback_url).addQuery(q);
|
||||||
if (!url.is("absolute")) {
|
if (!url.is("absolute")) {
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
} from "../../types";
|
} from "../../dbTypes";
|
||||||
|
|
||||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||||
import { renderAmount } from "../renderHtml";
|
import { renderAmount } from "../renderHtml";
|
||||||
|
@ -26,13 +26,15 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
import * as i18n from "../../i18n";
|
import * as i18n from "../../i18n";
|
||||||
|
|
||||||
|
import { AmountJson } from "../../amounts";
|
||||||
|
import * as Amounts from "../../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
HistoryRecord,
|
HistoryRecord,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
WalletBalanceEntry,
|
WalletBalanceEntry,
|
||||||
} from "../../types";
|
} from "../../walletTypes";
|
||||||
|
|
||||||
import { abbrev, renderAmount } from "../renderHtml";
|
import { abbrev, renderAmount } from "../renderHtml";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
@ -407,7 +409,8 @@ function formatHistoryItem(historyItem: HistoryRecord) {
|
|||||||
const url = tipPageUrl.query(params).href();
|
const url = tipPageUrl.query(params).href();
|
||||||
return (
|
return (
|
||||||
<i18n.Translate wrap="p">
|
<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>
|
<span> </span>
|
||||||
{ d.accepted ? null : <span>You did not accept the tip yet.</span> }
|
{ d.accepted ? null : <span>You did not accept the tip yet.</span> }
|
||||||
</i18n.Translate>
|
</i18n.Translate>
|
||||||
|
@ -26,7 +26,10 @@ import * as React from "react";
|
|||||||
import * as ReactDOM from "react-dom";
|
import * as ReactDOM from "react-dom";
|
||||||
import URI = require("urijs");
|
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 { AmountDisplay } from "../renderHtml";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
@ -36,14 +39,14 @@ interface RefundStatusViewProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface RefundStatusViewState {
|
interface RefundStatusViewState {
|
||||||
purchase?: types.PurchaseRecord;
|
purchase?: dbTypes.PurchaseRecord;
|
||||||
refundFees?: types.AmountJson;
|
refundFees?: AmountJson;
|
||||||
gotResult: boolean;
|
gotResult: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RefundDetailProps {
|
interface RefundDetailProps {
|
||||||
purchase: types.PurchaseRecord;
|
purchase: dbTypes.PurchaseRecord;
|
||||||
fullRefundFees: types.AmountJson;
|
fullRefundFees: AmountJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
|
const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
|
||||||
@ -59,13 +62,13 @@ const RefundDetail = ({purchase, fullRefundFees}: RefundDetailProps) => {
|
|||||||
throw Error("invariant");
|
throw Error("invariant");
|
||||||
}
|
}
|
||||||
|
|
||||||
let amountPending = types.Amounts.getZero(currency);
|
let amountPending = Amounts.getZero(currency);
|
||||||
for (const k of pendingKeys) {
|
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) {
|
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;
|
const hasPending = amountPending.fraction !== 0 || amountPending.value !== 0;
|
||||||
|
@ -25,12 +25,13 @@
|
|||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { AmountJson } from "../../amounts";
|
||||||
|
import * as Amounts from "../../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
} from "../../types";
|
} from "../../walletTypes";
|
||||||
|
|
||||||
import * as i18n from "../../i18n";
|
import * as i18n from "../../i18n";
|
||||||
|
|
||||||
|
@ -33,9 +33,13 @@ import {
|
|||||||
getTipStatus,
|
getTipStatus,
|
||||||
} from "../wxApi";
|
} 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 {
|
interface TipDisplayProps {
|
||||||
merchantDomain: string;
|
merchantDomain: string;
|
||||||
@ -54,7 +58,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async update() {
|
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 });
|
this.setState({ tipStatus });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +77,7 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
|||||||
renderExchangeInfo(ts: TipStatus) {
|
renderExchangeInfo(ts: TipStatus) {
|
||||||
const rci = ts.rci;
|
const rci = ts.rci;
|
||||||
if (!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;
|
const totalCost = Amounts.add(rci.overhead, rci.withdrawFee).amount;
|
||||||
return (
|
return (
|
||||||
@ -102,7 +106,9 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
|||||||
className="pure-button pure-button-primary"
|
className="pure-button pure-button-primary"
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => this.accept()}>
|
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
|
Accept tip
|
||||||
</button>
|
</button>
|
||||||
{" "}
|
{" "}
|
||||||
@ -119,7 +125,8 @@ class TipDisplay extends React.Component<TipDisplayProps, TipDisplayState> {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Tip Received!</h2>
|
<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
|
{ts.tip.accepted
|
||||||
? <p>You've accepted this tip! <a href={ts.tip.nextUrl}>Go back to merchant</a></p>
|
? <p>You've accepted this tip! <a href={ts.tip.nextUrl}>Go back to merchant</a></p>
|
||||||
: this.renderButtons()
|
: this.renderButtons()
|
||||||
@ -135,9 +142,9 @@ async function main() {
|
|||||||
const url = new URI(document.location.href);
|
const url = new URI(document.location.href);
|
||||||
const query: any = URI.parseQuery(url.query());
|
const query: any = URI.parseQuery(url.query());
|
||||||
|
|
||||||
let merchantDomain = query.merchant_domain;
|
const merchantDomain = query.merchant_domain;
|
||||||
let tipId = query.tip_id;
|
const tipId = query.tip_id;
|
||||||
let props: TipDisplayProps = { tipId, merchantDomain };
|
const props: TipDisplayProps = { tipId, merchantDomain };
|
||||||
|
|
||||||
ReactDOM.render(<TipDisplay {...props} />,
|
ReactDOM.render(<TipDisplay {...props} />,
|
||||||
document.getElementById("container")!);
|
document.getElementById("container")!);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import { getTalerStampDate } from "../../helpers";
|
import { getTalerStampDate } from "../../helpers";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
CoinStatus,
|
CoinStatus,
|
||||||
@ -29,7 +30,7 @@ import {
|
|||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
PreCoinRecord,
|
PreCoinRecord,
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
} from "../../types";
|
} from "../../dbTypes";
|
||||||
|
|
||||||
import { ImplicitStateComponent, StateHolder } from "../components";
|
import { ImplicitStateComponent, StateHolder } from "../components";
|
||||||
import {
|
import {
|
||||||
|
@ -24,12 +24,16 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
|
import * as Amounts from "../amounts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
Amounts,
|
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
|
} from "../dbTypes";
|
||||||
|
import {
|
||||||
ReserveCreationInfo,
|
ReserveCreationInfo,
|
||||||
} from "../types";
|
} from "../walletTypes";
|
||||||
|
|
||||||
|
|
||||||
import { ImplicitStateComponent } from "./components";
|
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 {
|
export function WithdrawDetailView(props: {rci: ReserveCreationInfo | null}): JSX.Element {
|
||||||
const rci = props.rci;
|
const rci = props.rci;
|
||||||
return (
|
return (
|
||||||
@ -259,6 +265,9 @@ interface ExpanderTextProps {
|
|||||||
text: string;
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show a heading with a toggle to show/hide the expandable content.
|
||||||
|
*/
|
||||||
export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
|
export class ExpanderText extends ImplicitStateComponent<ExpanderTextProps> {
|
||||||
private expanded = this.makeState<boolean>(false);
|
private expanded = this.makeState<boolean>(false);
|
||||||
private textArea: any = undefined;
|
private textArea: any = undefined;
|
||||||
|
@ -22,26 +22,31 @@
|
|||||||
/**
|
/**
|
||||||
* Imports.
|
* Imports.
|
||||||
*/
|
*/
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
import {
|
import {
|
||||||
AmountJson,
|
|
||||||
CheckPayResult,
|
|
||||||
CoinRecord,
|
CoinRecord,
|
||||||
ConfirmPayResult,
|
|
||||||
CurrencyRecord,
|
CurrencyRecord,
|
||||||
DenominationRecord,
|
DenominationRecord,
|
||||||
ExchangeRecord,
|
ExchangeRecord,
|
||||||
PreCoinRecord,
|
PreCoinRecord,
|
||||||
PurchaseRecord,
|
PurchaseRecord,
|
||||||
QueryPaymentResult,
|
|
||||||
RefundPermission,
|
|
||||||
ReserveCreationInfo,
|
|
||||||
ReserveRecord,
|
ReserveRecord,
|
||||||
|
} from "../dbTypes";
|
||||||
|
import {
|
||||||
|
CheckPayResult,
|
||||||
|
ConfirmPayResult,
|
||||||
|
QueryPaymentResult,
|
||||||
|
ReserveCreationInfo,
|
||||||
SenderWireInfos,
|
SenderWireInfos,
|
||||||
TipResponse,
|
|
||||||
TipPlanchetDetail,
|
|
||||||
TipStatus,
|
TipStatus,
|
||||||
WalletBalance,
|
WalletBalance,
|
||||||
} from "../types";
|
} from "../walletTypes";
|
||||||
|
|
||||||
|
import {
|
||||||
|
RefundPermission,
|
||||||
|
TipPlanchetDetail,
|
||||||
|
TipResponse,
|
||||||
|
} from "../talerTypes";
|
||||||
|
|
||||||
import { MessageMap, MessageType } from "./messages";
|
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.
|
* 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 });
|
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> {
|
export function getTipStatus(merchantDomain: string, tipId: string): Promise<TipStatus> {
|
||||||
return callBackend("get-tip-status", { merchantDomain, tipId });
|
return callBackend("get-tip-status", { merchantDomain, tipId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a tip as accepted by the user.
|
||||||
|
*/
|
||||||
export function acceptTip(merchantDomain: string, tipId: string): Promise<TipStatus> {
|
export function acceptTip(merchantDomain: string, tipId: string): Promise<TipStatus> {
|
||||||
return callBackend("accept-tip", { merchantDomain, tipId });
|
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> {
|
export function processTipResponse(merchantDomain: string, tipId: string, tipResponse: TipResponse): Promise<void> {
|
||||||
return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse });
|
return callBackend("process-tip-response", { merchantDomain, tipId, tipResponse });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear notifications that the wallet shows to the user.
|
||||||
|
*/
|
||||||
export function clearNotification(): Promise<void> {
|
export function clearNotification(): Promise<void> {
|
||||||
return callBackend("clear-notification", { });
|
return callBackend("clear-notification", { });
|
||||||
}
|
}
|
||||||
|
@ -30,18 +30,21 @@ import {
|
|||||||
Index,
|
Index,
|
||||||
Store,
|
Store,
|
||||||
} from "../query";
|
} from "../query";
|
||||||
|
|
||||||
|
import { AmountJson } from "../amounts";
|
||||||
|
|
||||||
|
import { ProposalRecord } from "../dbTypes";
|
||||||
import {
|
import {
|
||||||
AcceptTipRequest,
|
AcceptTipRequest,
|
||||||
AmountJson,
|
|
||||||
ConfirmReserveRequest,
|
ConfirmReserveRequest,
|
||||||
CreateReserveRequest,
|
CreateReserveRequest,
|
||||||
GetTipPlanchetsRequest,
|
GetTipPlanchetsRequest,
|
||||||
Notifier,
|
Notifier,
|
||||||
ProcessTipResponseRequest,
|
ProcessTipResponseRequest,
|
||||||
ProposalRecord,
|
|
||||||
ReturnCoinsRequest,
|
ReturnCoinsRequest,
|
||||||
TipStatusRequest,
|
TipStatusRequest,
|
||||||
} from "../types";
|
} from "../walletTypes";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Stores,
|
Stores,
|
||||||
WALLET_DB_VERSION,
|
WALLET_DB_VERSION,
|
||||||
@ -335,7 +338,12 @@ function handleMessage(sender: MessageSender,
|
|||||||
}
|
}
|
||||||
case "get-tip-planchets": {
|
case "get-tip-planchets": {
|
||||||
const req = GetTipPlanchetsRequest.checked(detail);
|
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": {
|
case "clear-notification": {
|
||||||
return needsWallet().clearNotification();
|
return needsWallet().clearNotification();
|
||||||
@ -702,11 +710,10 @@ export async function wxMain() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Clear notifications both when the popop opens,
|
// Clear notifications both when the popop opens,
|
||||||
// as well when it closes.
|
// as well when it closes.
|
||||||
chrome.runtime.onConnect.addListener((port) => {
|
chrome.runtime.onConnect.addListener((port) => {
|
||||||
if (port.name == "popup") {
|
if (port.name === "popup") {
|
||||||
if (currentWallet) {
|
if (currentWallet) {
|
||||||
currentWallet.clearNotification();
|
currentWallet.clearNotification();
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,10 @@
|
|||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// <reference path="../decl/node.d.ts" />
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import {readFileSync} from "fs";
|
import { readFileSync } from "fs";
|
||||||
import {execSync} from "child_process";
|
import { execSync } from "child_process";
|
||||||
import * as ts from "typescript";
|
import * as ts from "typescript";
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es6",
|
"target": "es6",
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
@ -7,8 +8,8 @@
|
|||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"lib": [
|
"lib": [
|
||||||
"ES6",
|
"es6",
|
||||||
"DOM"
|
"dom"
|
||||||
],
|
],
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
@ -23,6 +24,7 @@
|
|||||||
"decl/chrome/chrome.d.ts",
|
"decl/chrome/chrome.d.ts",
|
||||||
"decl/jed.d.ts",
|
"decl/jed.d.ts",
|
||||||
"decl/urijs.d.ts",
|
"decl/urijs.d.ts",
|
||||||
|
"src/amounts.ts",
|
||||||
"src/checkable.ts",
|
"src/checkable.ts",
|
||||||
"src/crypto/cryptoApi-test.ts",
|
"src/crypto/cryptoApi-test.ts",
|
||||||
"src/crypto/cryptoApi.ts",
|
"src/crypto/cryptoApi.ts",
|
||||||
@ -34,6 +36,7 @@
|
|||||||
"src/crypto/nodeWorker.ts",
|
"src/crypto/nodeWorker.ts",
|
||||||
"src/crypto/nodeWorkerEntry.ts",
|
"src/crypto/nodeWorkerEntry.ts",
|
||||||
"src/crypto/startWorker.js",
|
"src/crypto/startWorker.js",
|
||||||
|
"src/dbTypes.ts",
|
||||||
"src/helpers-test.ts",
|
"src/helpers-test.ts",
|
||||||
"src/helpers.ts",
|
"src/helpers.ts",
|
||||||
"src/http.ts",
|
"src/http.ts",
|
||||||
@ -42,18 +45,13 @@
|
|||||||
"src/libtoolVersion-test.ts",
|
"src/libtoolVersion-test.ts",
|
||||||
"src/libtoolVersion.ts",
|
"src/libtoolVersion.ts",
|
||||||
"src/logging.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/query.ts",
|
||||||
|
"src/talerTypes.ts",
|
||||||
"src/timer.ts",
|
"src/timer.ts",
|
||||||
"src/types-test.ts",
|
"src/types-test.ts",
|
||||||
"src/types.ts",
|
|
||||||
"src/wallet-test.ts",
|
"src/wallet-test.ts",
|
||||||
"src/wallet.ts",
|
"src/wallet.ts",
|
||||||
|
"src/walletTypes.ts",
|
||||||
"src/webex/background.ts",
|
"src/webex/background.ts",
|
||||||
"src/webex/chromeBadge.ts",
|
"src/webex/chromeBadge.ts",
|
||||||
"src/webex/components.ts",
|
"src/webex/components.ts",
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
],
|
],
|
||||||
"jsRules": {},
|
"jsRules": {},
|
||||||
"rules": {
|
"rules": {
|
||||||
|
"arrow-parens": false,
|
||||||
"max-line-length": {
|
"max-line-length": {
|
||||||
"options": [120]
|
"options": [120]
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user