remove some type literal and pretty

This commit is contained in:
Sebastian 2023-06-16 09:40:45 -03:00
parent d97f440f25
commit 444c5427f4
No known key found for this signature in database
GPG Key ID: 173909D1A5F66069
11 changed files with 140 additions and 83 deletions

View File

@ -243,7 +243,9 @@ export interface TalerCryptoInterface {
signRefund(req: SignRefundRequest): Promise<SignRefundResponse>; signRefund(req: SignRefundRequest): Promise<SignRefundResponse>;
signDeletePurse(req: SignDeletePurseRequest): Promise<SignDeletePurseResponse>; signDeletePurse(
req: SignDeletePurseRequest,
): Promise<SignDeletePurseResponse>;
} }
/** /**
@ -1695,7 +1697,7 @@ export const nativeCryptoR: TalerCryptoInterfaceR = {
}); });
return { return {
sig: sigResp.sig, sig: sigResp.sig,
} };
}, },
}; };

View File

@ -271,7 +271,6 @@ export interface SignRefundResponse {
sig: string; sig: string;
} }
export interface SignDeletePurseRequest { export interface SignDeletePurseRequest {
pursePriv: string; pursePriv: string;
} }

View File

@ -120,13 +120,7 @@ export interface TopupReserveWithDemobankArgs {
export async function topupReserveWithDemobank( export async function topupReserveWithDemobank(
args: TopupReserveWithDemobankArgs, args: TopupReserveWithDemobankArgs,
) { ) {
const { const { http, bankAccessApiBaseUrl, amount, exchangeInfo, reservePub } = args;
http,
bankAccessApiBaseUrl,
amount,
exchangeInfo,
reservePub,
} = args;
const bankHandle: BankServiceHandle = { const bankHandle: BankServiceHandle = {
bankAccessApiBaseUrl: bankAccessApiBaseUrl, bankAccessApiBaseUrl: bankAccessApiBaseUrl,
http, http,

View File

@ -62,11 +62,7 @@ import { InternalWalletState } from "../../internal-wallet-state.js";
import { assertUnreachable } from "../../util/assertUnreachable.js"; import { assertUnreachable } from "../../util/assertUnreachable.js";
import { checkLogicInvariant } from "../../util/invariants.js"; import { checkLogicInvariant } from "../../util/invariants.js";
import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js"; import { GetReadOnlyAccess, GetReadWriteAccess } from "../../util/query.js";
import { import { makeCoinAvailable, makeTombstoneId, TombstoneTag } from "../common.js";
makeCoinAvailable,
makeTombstoneId,
TombstoneTag,
} from "../common.js";
import { getExchangeDetails } from "../exchanges.js"; import { getExchangeDetails } from "../exchanges.js";
import { extractContractData } from "../pay-merchant.js"; import { extractContractData } from "../pay-merchant.js";
import { provideBackupState } from "./state.js"; import { provideBackupState } from "./state.js";

View File

@ -464,7 +464,7 @@ export type ParsedTombstone =
tag: TombstoneTag.DeleteWithdrawalGroup; tag: TombstoneTag.DeleteWithdrawalGroup;
withdrawalGroupId: string; withdrawalGroupId: string;
} }
| { tag: TombstoneTag.DeleteRefund; refundGroupId: string } | { tag: TombstoneTag.DeleteRefund; refundGroupId: string };
export function constructTombstone(p: ParsedTombstone): TombstoneIdStr { export function constructTombstone(p: ParsedTombstone): TombstoneIdStr {
switch (p.tag) { switch (p.tag) {

View File

@ -495,8 +495,7 @@ async function waitForRefreshOnDepositGroup(
if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) { if (refreshGroup.operationStatus === RefreshOperationStatus.Finished) {
newOpState = DepositOperationStatus.Aborted; newOpState = DepositOperationStatus.Aborted;
} else if ( } else if (
refreshGroup.operationStatus === refreshGroup.operationStatus === RefreshOperationStatus.Failed
RefreshOperationStatus.Failed
) { ) {
newOpState = DepositOperationStatus.Aborted; newOpState = DepositOperationStatus.Aborted;
} }

View File

@ -208,14 +208,13 @@ async function longpollKycStatus(
const transitionInfo = await ws.db const transitionInfo = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations]) .mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const peerIni = await tx.peerPullPaymentInitiations.get( const peerIni = await tx.peerPullPaymentInitiations.get(pursePub);
pursePub,
);
if (!peerIni) { if (!peerIni) {
return; return;
} }
if ( if (
peerIni.status !== PeerPullPaymentInitiationStatus.PendingMergeKycRequired peerIni.status !==
PeerPullPaymentInitiationStatus.PendingMergeKycRequired
) { ) {
return; return;
} }
@ -254,10 +253,7 @@ async function processPeerPullCreditAbortingDeletePurse(
const sigResp = await ws.cryptoApi.signDeletePurse({ const sigResp = await ws.cryptoApi.signDeletePurse({
pursePriv, pursePriv,
}); });
const purseUrl = new URL( const purseUrl = new URL(`purses/${pursePub}`, peerPullIni.exchangeBaseUrl);
`purses/${pursePub}`,
peerPullIni.exchangeBaseUrl,
);
const resp = await ws.http.fetch(purseUrl.href, { const resp = await ws.http.fetch(purseUrl.href, {
method: "DELETE", method: "DELETE",
headers: { headers: {
@ -517,9 +513,7 @@ async function processPeerPullCreditKycRequired(
const { transitionInfo, result } = await ws.db const { transitionInfo, result } = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations]) .mktx((x) => [x.peerPullPaymentInitiations])
.runReadWrite(async (tx) => { .runReadWrite(async (tx) => {
const peerInc = await tx.peerPullPaymentInitiations.get( const peerInc = await tx.peerPullPaymentInitiations.get(pursePub);
pursePub,
);
if (!peerInc) { if (!peerInc) {
return { return {
transitionInfo: undefined, transitionInfo: undefined,
@ -532,7 +526,8 @@ async function processPeerPullCreditKycRequired(
requirementRow: kycPending.requirement_row, requirementRow: kycPending.requirement_row,
}; };
peerInc.kycUrl = kycStatus.kyc_url; peerInc.kycUrl = kycStatus.kyc_url;
peerInc.status = PeerPullPaymentInitiationStatus.PendingMergeKycRequired; peerInc.status =
PeerPullPaymentInitiationStatus.PendingMergeKycRequired;
const newTxState = computePeerPullCreditTransactionState(peerInc); const newTxState = computePeerPullCreditTransactionState(peerInc);
await tx.peerPullPaymentInitiations.put(peerInc); await tx.peerPullPaymentInitiations.put(peerInc);
// We'll remove this eventually! New clients should rely on the // We'll remove this eventually! New clients should rely on the

View File

@ -45,14 +45,24 @@ import {
PreparePayResultType, PreparePayResultType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../internal-wallet-state.js"; import { InternalWalletState } from "../internal-wallet-state.js";
import { confirmPay, preparePayForUri, startRefundQueryForUri } from "./pay-merchant.js"; import {
confirmPay,
preparePayForUri,
startRefundQueryForUri,
} from "./pay-merchant.js";
import { getBalances } from "./balance.js"; import { getBalances } from "./balance.js";
import { checkLogicInvariant } from "../util/invariants.js"; import { checkLogicInvariant } from "../util/invariants.js";
import { acceptWithdrawalFromUri } from "./withdraw.js"; import { acceptWithdrawalFromUri } from "./withdraw.js";
import { updateExchangeFromUrl } from "./exchanges.js"; import { updateExchangeFromUrl } from "./exchanges.js";
import { initiatePeerPullPayment } from "./pay-peer-pull-credit.js"; import { initiatePeerPullPayment } from "./pay-peer-pull-credit.js";
import { preparePeerPullDebit, confirmPeerPullDebit } from "./pay-peer-pull-debit.js"; import {
import { preparePeerPushCredit, confirmPeerPushCredit } from "./pay-peer-push-credit.js"; preparePeerPullDebit,
confirmPeerPullDebit,
} from "./pay-peer-pull-debit.js";
import {
preparePeerPushCredit,
confirmPeerPushCredit,
} from "./pay-peer-push-credit.js";
import { initiatePeerPushDebit } from "./pay-peer-push-debit.js"; import { initiatePeerPushDebit } from "./pay-peer-push-debit.js";
const logger = new Logger("operations/testing.ts"); const logger = new Logger("operations/testing.ts");

View File

@ -15,6 +15,8 @@
*/ */
import test, { ExecutionContext } from "ava"; import test, { ExecutionContext } from "ava";
import { import {
AmountMode,
OperationType,
calculatePlanFormAvailableCoins, calculatePlanFormAvailableCoins,
selectCoinForOperation, selectCoinForOperation,
} from "./coinSelection.js"; } from "./coinSelection.js";
@ -24,6 +26,7 @@ import {
AmountJson, AmountJson,
Amounts, Amounts,
Duration, Duration,
TransactionAmountMode,
TransactionType, TransactionType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
@ -67,10 +70,15 @@ test("get effective 2", (t) => {
[kudos(2), 5], [kudos(2), 5],
[kudos(5), 5], [kudos(5), 5],
]; ];
const result = selectCoinForOperation("credit", kudos(2), "net", { const result = selectCoinForOperation(
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), OperationType.Credit,
exchanges: {}, kudos(2),
}); AmountMode.Net,
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
},
);
expect(t, result.coins).deep.equal(["KUDOS:2"]); expect(t, result.coins).deep.equal(["KUDOS:2"]);
t.assert(result.refresh === undefined); t.assert(result.refresh === undefined);
}); });
@ -80,10 +88,15 @@ test("get raw 4", (t) => {
[kudos(2), 5], [kudos(2), 5],
[kudos(5), 5], [kudos(5), 5],
]; ];
const result = selectCoinForOperation("credit", kudos(4), "gross", { const result = selectCoinForOperation(
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), OperationType.Credit,
exchanges: {}, kudos(4),
}); AmountMode.Gross,
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
},
);
expect(t, result.coins).deep.equal(["KUDOS:2", "KUDOS:2"]); expect(t, result.coins).deep.equal(["KUDOS:2", "KUDOS:2"]);
t.assert(result.refresh === undefined); t.assert(result.refresh === undefined);
@ -94,10 +107,15 @@ test("send effective 6", (t) => {
[kudos(2), 5], [kudos(2), 5],
[kudos(5), 5], [kudos(5), 5],
]; ];
const result = selectCoinForOperation("debit", kudos(6), "gross", { const result = selectCoinForOperation(
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), OperationType.Debit,
exchanges: {}, kudos(6),
}); AmountMode.Gross,
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
},
);
expect(t, result.coins).deep.equal(["KUDOS:5"]); expect(t, result.coins).deep.equal(["KUDOS:5"]);
t.assert(result.refresh?.selected === "KUDOS:2"); t.assert(result.refresh?.selected === "KUDOS:2");
@ -108,10 +126,15 @@ test("send raw 6", (t) => {
[kudos(2), 5], [kudos(2), 5],
[kudos(5), 5], [kudos(5), 5],
]; ];
const result = selectCoinForOperation("debit", kudos(6), "gross", { const result = selectCoinForOperation(
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), OperationType.Debit,
exchanges: {}, kudos(6),
}); AmountMode.Gross,
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
},
);
expect(t, result.coins).deep.equal(["KUDOS:5"]); expect(t, result.coins).deep.equal(["KUDOS:5"]);
t.assert(result.refresh?.selected === "KUDOS:2"); t.assert(result.refresh?.selected === "KUDOS:2");
@ -122,10 +145,15 @@ test("send raw 20 (not enough)", (t) => {
[kudos(2), 1], [kudos(2), 1],
[kudos(5), 2], [kudos(5), 2],
]; ];
const result = selectCoinForOperation("debit", kudos(20), "gross", { const result = selectCoinForOperation(
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), OperationType.Debit,
exchanges: {}, kudos(20),
}); AmountMode.Gross,
{
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: {},
},
);
expect(t, result.coins).deep.equal(["KUDOS:5", "KUDOS:5", "KUDOS:2"]); expect(t, result.coins).deep.equal(["KUDOS:5", "KUDOS:5", "KUDOS:2"]);
t.assert(result.refresh === undefined); t.assert(result.refresh === undefined);
@ -147,7 +175,7 @@ test("deposit effective 2 ", (t) => {
const result = calculatePlanFormAvailableCoins( const result = calculatePlanFormAvailableCoins(
TransactionType.Deposit, TransactionType.Deposit,
kudos(2), kudos(2),
"effective", TransactionAmountMode.Effective,
{ {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: { exchanges: {
@ -173,7 +201,7 @@ test("deposit raw 2 ", (t) => {
const result = calculatePlanFormAvailableCoins( const result = calculatePlanFormAvailableCoins(
TransactionType.Deposit, TransactionType.Deposit,
kudos(2), kudos(2),
"raw", TransactionAmountMode.Raw,
{ {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: { exchanges: {
@ -199,7 +227,7 @@ test("withdraw raw 21 ", (t) => {
const result = calculatePlanFormAvailableCoins( const result = calculatePlanFormAvailableCoins(
TransactionType.Withdrawal, TransactionType.Withdrawal,
kudos(21), kudos(21),
"raw", TransactionAmountMode.Raw,
{ {
list: coinList.map(([v, t]) => defaultFeeConfig(v, t)), list: coinList.map(([v, t]) => defaultFeeConfig(v, t)),
exchanges: { exchanges: {

View File

@ -46,6 +46,7 @@ import {
PayCoinSelection, PayCoinSelection,
PayMerchantInsufficientBalanceDetails, PayMerchantInsufficientBalanceDetails,
strcmp, strcmp,
TransactionAmountMode,
TransactionType, TransactionType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { import {
@ -818,7 +819,7 @@ function getCoinsFilter(req: GetPlanForOperationRequest): CoinsFilter {
export function calculatePlanFormAvailableCoins( export function calculatePlanFormAvailableCoins(
transactionType: TransactionType, transactionType: TransactionType,
amount: AmountJson, amount: AmountJson,
mode: "effective" | "raw", mode: TransactionAmountMode,
availableCoins: AvailableCoins, availableCoins: AvailableCoins,
) { ) {
const operationType = getOperationType(transactionType); const operationType = getOperationType(transactionType);
@ -828,7 +829,9 @@ export function calculatePlanFormAvailableCoins(
usableCoins = selectCoinForOperation( usableCoins = selectCoinForOperation(
operationType, operationType,
amount, amount,
mode === "effective" ? "net" : "gross", mode === TransactionAmountMode.Effective
? AmountMode.Net
: AmountMode.Gross,
availableCoins, availableCoins,
); );
break; break;
@ -839,11 +842,11 @@ export function calculatePlanFormAvailableCoins(
//are from that exchange //are from that exchange
const wireFee = Object.values(availableCoins.exchanges)[0].wireFee!; const wireFee = Object.values(availableCoins.exchanges)[0].wireFee!;
if (mode === "effective") { if (mode === TransactionAmountMode.Effective) {
usableCoins = selectCoinForOperation( usableCoins = selectCoinForOperation(
operationType, operationType,
amount, amount,
"gross", AmountMode.Gross,
availableCoins, availableCoins,
); );
@ -857,7 +860,7 @@ export function calculatePlanFormAvailableCoins(
usableCoins = selectCoinForOperation( usableCoins = selectCoinForOperation(
operationType, operationType,
adjustedAmount, adjustedAmount,
"net", AmountMode.Net,
availableCoins, availableCoins,
); );
@ -913,6 +916,27 @@ export async function getPlanForOperation(
); );
} }
/**
* If the operation going to be plan subtracts
* or adds amount in the wallet db
*/
export enum OperationType {
Credit = "credit",
Debit = "debit",
}
/**
* How the amount should be interpreted
* net = without fee
* gross = with fee
*
* Net value is always lower than gross
*/
export enum AmountMode {
Net = "net",
Gross = "gross",
}
/** /**
* *
* @param op defined which fee are we taking into consideration: deposits or withdraw * @param op defined which fee are we taking into consideration: deposits or withdraw
@ -922,9 +946,9 @@ export async function getPlanForOperation(
* @returns * @returns
*/ */
export function selectCoinForOperation( export function selectCoinForOperation(
op: "debit" | "credit", op: OperationType,
limit: AmountJson, limit: AmountJson,
mode: "net" | "gross", mode: AmountMode,
coins: AvailableCoins, coins: AvailableCoins,
): SelectedCoins { ): SelectedCoins {
const result: SelectedCoins = { const result: SelectedCoins = {
@ -951,8 +975,11 @@ export function selectCoinForOperation(
iterateDenoms: while (denomIdx < coins.list.length) { iterateDenoms: while (denomIdx < coins.list.length) {
const denom = coins.list[denomIdx]; const denom = coins.list[denomIdx];
let total = let total =
op === "credit" ? Number.MAX_SAFE_INTEGER : denom.totalAvailable ?? 0; op === OperationType.Credit
const opFee = op === "credit" ? denom.denomWithdraw : denom.denomDeposit; ? Number.MAX_SAFE_INTEGER
: denom.totalAvailable ?? 0;
const opFee =
op === OperationType.Credit ? denom.denomWithdraw : denom.denomDeposit;
const contribution = Amounts.sub(denom.value, opFee).amount; const contribution = Amounts.sub(denom.value, opFee).amount;
if (Amounts.isZero(contribution)) { if (Amounts.isZero(contribution)) {
@ -969,7 +996,7 @@ export function selectCoinForOperation(
contribution, contribution,
).amount; ).amount;
const progress = mode === "gross" ? nextValue : nextContribution; const progress = mode === AmountMode.Gross ? nextValue : nextContribution;
if (Amounts.cmp(progress, limit) === 1) { if (Amounts.cmp(progress, limit) === 1) {
//the current coin is more than we need, try next denom //the current coin is more than we need, try next denom
@ -1008,14 +1035,15 @@ export function selectCoinForOperation(
// we made it // we made it
return result; return result;
} }
if (op === "credit") { if (op === OperationType.Credit) {
//doing withdraw there is no way to cover the gap //doing withdraw there is no way to cover the gap
return result; return result;
} }
//tried all the coins but there is a gap //tried all the coins but there is a gap
//doing deposit we can try refreshing coins //doing deposit we can try refreshing coins
const total = mode === "gross" ? result.totalValue : result.totalContribution; const total =
mode === AmountMode.Gross ? result.totalValue : result.totalContribution;
const gap = Amounts.sub(limit, total).amount; const gap = Amounts.sub(limit, total).amount;
//about recursive calls //about recursive calls
@ -1027,7 +1055,7 @@ export function selectCoinForOperation(
refreshIteration: while (refreshIdx < coins.list.length) { refreshIteration: while (refreshIdx < coins.list.length) {
const d = coins.list[refreshIdx]; const d = coins.list[refreshIdx];
const denomContribution = const denomContribution =
mode === "gross" mode === AmountMode.Gross
? Amounts.sub(d.value, d.denomRefresh).amount ? Amounts.sub(d.value, d.denomRefresh).amount
: Amounts.sub(d.value, d.denomDeposit, d.denomRefresh).amount; : Amounts.sub(d.value, d.denomDeposit, d.denomRefresh).amount;
@ -1038,7 +1066,7 @@ export function selectCoinForOperation(
} }
const changeCost = selectCoinForOperation( const changeCost = selectCoinForOperation(
"credit", OperationType.Credit,
changeAfterDeposit, changeAfterDeposit,
mode, mode,
coins, coins,
@ -1067,7 +1095,7 @@ export function selectCoinForOperation(
refreshIdx++; refreshIdx++;
} }
if (choice) { if (choice) {
if (mode === "gross") { if (mode === AmountMode.Gross) {
result.totalValue = Amounts.add(result.totalValue, gap).amount; result.totalValue = Amounts.add(result.totalValue, gap).amount;
result.totalContribution = Amounts.add( result.totalContribution = Amounts.add(
result.totalContribution, result.totalContribution,
@ -1096,9 +1124,9 @@ export function selectCoinForOperation(
} }
type CompareCoinsFunction = (d1: CoinInfo, d2: CoinInfo) => -1 | 0 | 1; type CompareCoinsFunction = (d1: CoinInfo, d2: CoinInfo) => -1 | 0 | 1;
function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction { function buildRankingForCoins(op: OperationType): CompareCoinsFunction {
function getFee(d: CoinInfo) { function getFee(d: CoinInfo) {
return op === "credit" ? d.denomWithdraw : d.denomDeposit; return op === OperationType.Credit ? d.denomWithdraw : d.denomDeposit;
} }
//different exchanges may have different wireFee //different exchanges may have different wireFee
//ranking should take the relative contribution in the exchange //ranking should take the relative contribution in the exchange
@ -1116,28 +1144,32 @@ function buildRankingForCoins(op: "debit" | "credit"): CompareCoinsFunction {
}; };
} }
function getOperationType(txType: TransactionType): "credit" | "debit" { function getOperationType(txType: TransactionType): OperationType {
const operationType = const operationType =
txType === TransactionType.Withdrawal txType === TransactionType.Withdrawal
? ("credit" as const) ? OperationType.Credit
: txType === TransactionType.Deposit : txType === TransactionType.Deposit
? ("debit" as const) ? OperationType.Debit
: undefined; : undefined;
if (!operationType) { if (!operationType) {
throw Error(`operation type ${txType} not supported`); throw Error(`operation type ${txType} not yet supported`);
} }
return operationType; return operationType;
} }
function getAmountsWithFee( function getAmountsWithFee(
op: "debit" | "credit", op: OperationType,
value: AmountJson, value: AmountJson,
contribution: AmountJson, contribution: AmountJson,
details: any, details: any,
): GetPlanForOperationResponse { ): GetPlanForOperationResponse {
return { return {
rawAmount: Amounts.stringify(op === "credit" ? value : contribution), rawAmount: Amounts.stringify(
effectiveAmount: Amounts.stringify(op === "credit" ? contribution : value), op === OperationType.Credit ? value : contribution,
),
effectiveAmount: Amounts.stringify(
op === OperationType.Credit ? contribution : value,
),
details, details,
}; };
} }
@ -1202,7 +1234,7 @@ interface CoinsFilter {
*/ */
async function getAvailableCoins( async function getAvailableCoins(
ws: InternalWalletState, ws: InternalWalletState,
op: "credit" | "debit", op: OperationType,
currency: string, currency: string,
filters: CoinsFilter = {}, filters: CoinsFilter = {},
): Promise<AvailableCoins> { ): Promise<AvailableCoins> {
@ -1286,7 +1318,7 @@ async function getAvailableCoins(
let creditDeadline = AbsoluteTime.never(); let creditDeadline = AbsoluteTime.never();
let debitDeadline = AbsoluteTime.never(); let debitDeadline = AbsoluteTime.never();
//4.- filter coins restricted by age //4.- filter coins restricted by age
if (op === "credit") { if (op === OperationType.Credit) {
const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll( const ds = await tx.denominations.indexes.byExchangeBaseUrl.getAll(
exchangeBaseUrl, exchangeBaseUrl,
); );

View File

@ -458,7 +458,9 @@ export function isWithdrawableDenom(
): boolean { ): boolean {
const now = AbsoluteTime.now(); const now = AbsoluteTime.now();
const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart); const start = AbsoluteTime.fromProtocolTimestamp(d.stampStart);
const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(d.stampExpireWithdraw); const withdrawExpire = AbsoluteTime.fromProtocolTimestamp(
d.stampExpireWithdraw,
);
const started = AbsoluteTime.cmp(now, start) >= 0; const started = AbsoluteTime.cmp(now, start) >= 0;
let lastPossibleWithdraw: AbsoluteTime; let lastPossibleWithdraw: AbsoluteTime;
if (denomselAllowLate) { if (denomselAllowLate) {