refactor confirmation tiers to shared crypto-agnostic types and validation
This commit is contained in:
@@ -12,7 +12,7 @@ import {
|
||||
} from '../types/Config';
|
||||
import { MoneroNetwork } from '../types/MoneroNetwork';
|
||||
import { MoneroWalletConfig } from '../types/MoneroWalletConfig';
|
||||
import { MoneroConfirmationTier } from '../types/MoneroConfirmationTier';
|
||||
import { ConfirmationTier } from '../types/ConfirmationTier';
|
||||
import { NodeEnv } from '../types/NodeEnv';
|
||||
import { ShopFiatCurrency } from '../types/ShopFiatCurrency';
|
||||
import { PaymentMethod } from '../modules/payment/types/PaymentMethod';
|
||||
@@ -178,7 +178,7 @@ export const getShopSettingsConfig = (): ShopSettingsConfig => ({
|
||||
shopName: env('SHOP_NAME'),
|
||||
shopFiatCurrency: env('SHOP_FIAT_CURRENCY') as ShopFiatCurrency,
|
||||
monero: {
|
||||
confirmationTiers: JSON.parse(env('MONERO_CONFIRMATION_TIERS')) as MoneroConfirmationTier[]
|
||||
confirmationTiers: JSON.parse(env('MONERO_CONFIRMATION_TIERS')) as ConfirmationTier[]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { IsBoolean, IsEnum, IsNotEmpty, IsNumber, IsString, Max, Min, validateSy
|
||||
import { NodeEnv } from '../types/NodeEnv';
|
||||
import { ShopFiatCurrency } from '../types/ShopFiatCurrency';
|
||||
import { IsBase64 } from '../validation/decorators/isBase64';
|
||||
import { IsMoneroConfirmationTiers } from '../validation/decorators/isMoneroConfirmationTiers';
|
||||
import { IsConfirmationTiers } from '../validation/decorators/isConfirmationTiers';
|
||||
import { MoneroNetwork } from '../types/MoneroNetwork';
|
||||
|
||||
class EnvironmentVariables {
|
||||
@@ -265,7 +265,7 @@ class EnvironmentVariables {
|
||||
|
||||
@IsNotEmpty()
|
||||
@IsString()
|
||||
@IsMoneroConfirmationTiers()
|
||||
@IsConfirmationTiers()
|
||||
MONERO_CONFIRMATION_TIERS: string;
|
||||
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { Config } from '../../../types/Config';
|
||||
import { getErrorMessage } from '../../../utils/getErrorMessage';
|
||||
import { convertFiatToXmr } from '../../../utils/monero/convertFiatToXmr';
|
||||
import { convertXmrToXmrAtomic } from '../../../utils/monero/convertXmrToXmrAtomic';
|
||||
import { resolveMinConfirmations } from '../../../utils/monero/resolveMinConfirmations';
|
||||
import { resolveMinConfirmations } from '../../../utils/confirmation/resolveMinConfirmations';
|
||||
import { MoneroWalletRpcClient } from '../../moneroWallet/services/MoneroWalletRpcClient';
|
||||
import { ExchangeRateService } from '../../exchangeRate/services/ExchangeRateService';
|
||||
import { Invoice } from '../entities/Invoice';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { MoneroConfirmationTier } from '../../../types/MoneroConfirmationTier';
|
||||
import { ConfirmationTier } from '../../../types/ConfirmationTier';
|
||||
|
||||
export interface ShopSettingsMoneroView {
|
||||
confirmationTiers: MoneroConfirmationTier[];
|
||||
confirmationTiers: ConfirmationTier[];
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
export interface BitcoinConfirmationTier {
|
||||
upToTotalFiat?: string;
|
||||
minConfirmations: number;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MoneroConfirmationTier } from './MoneroConfirmationTier';
|
||||
import { ConfirmationTier } from './ConfirmationTier';
|
||||
import { MoneroWalletConfig } from './MoneroWalletConfig';
|
||||
import { NodeEnv } from './NodeEnv';
|
||||
import { PaymentMethod } from '../modules/payment/types/PaymentMethod';
|
||||
@@ -55,7 +55,7 @@ export interface ShopSettingsConfig {
|
||||
shopName: string;
|
||||
shopFiatCurrency: ShopFiatCurrency;
|
||||
monero: {
|
||||
confirmationTiers: MoneroConfirmationTier[];
|
||||
confirmationTiers: ConfirmationTier[];
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export interface MoneroConfirmationTier {
|
||||
export interface ConfirmationTier {
|
||||
upToTotalFiat?: string;
|
||||
minConfirmations: number;
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
import Decimal from 'decimal.js';
|
||||
import type { MoneroConfirmationTier } from '../../types/MoneroConfirmationTier';
|
||||
import type { ConfirmationTier } from '../../types/ConfirmationTier';
|
||||
|
||||
export const resolveMinConfirmations = (totalFiat: number, tiers: MoneroConfirmationTier[]): number => {
|
||||
export const resolveMinConfirmations = (totalFiat: number, tiers: ConfirmationTier[]): number => {
|
||||
for (const tier of tiers) {
|
||||
if (tier.upToTotalFiat === undefined) {
|
||||
return tier.minConfirmations;
|
||||
+5
-5
@@ -1,13 +1,13 @@
|
||||
import { validateSync } from 'class-validator';
|
||||
import { IsMoneroConfirmationTiers } from './isMoneroConfirmationTiers';
|
||||
import { IsConfirmationTiers } from './isConfirmationTiers';
|
||||
|
||||
class TestDto {
|
||||
@IsMoneroConfirmationTiers()
|
||||
MONERO_CONFIRMATION_TIERS: string;
|
||||
@IsConfirmationTiers()
|
||||
CONFIRMATION_TIERS: string;
|
||||
}
|
||||
|
||||
const validateTiers = (value: string) => {
|
||||
const dto = Object.assign(new TestDto(), { MONERO_CONFIRMATION_TIERS: value });
|
||||
const dto = Object.assign(new TestDto(), { CONFIRMATION_TIERS: value });
|
||||
|
||||
return validateSync(dto);
|
||||
};
|
||||
@@ -15,7 +15,7 @@ const validateTiers = (value: string) => {
|
||||
const validTiers =
|
||||
'[{"upToTotalFiat":"25","minConfirmations":0},{"upToTotalFiat":"250","minConfirmations":5},{"minConfirmations":10}]';
|
||||
|
||||
describe('IsMoneroConfirmationTiers', () => {
|
||||
describe('IsConfirmationTiers', () => {
|
||||
it('accepts valid default tiers', () => {
|
||||
expect(validateTiers(validTiers)).toHaveLength(0);
|
||||
});
|
||||
+10
-10
@@ -1,5 +1,5 @@
|
||||
import { Validate, ValidatorConstraint, type ValidatorConstraintInterface } from 'class-validator';
|
||||
import type { MoneroConfirmationTier } from '../../types/MoneroConfirmationTier';
|
||||
import type { ConfirmationTier } from '../../types/ConfirmationTier';
|
||||
|
||||
const isPositiveDecimalString = (value: string): boolean => {
|
||||
const trimmed = value.trim();
|
||||
@@ -16,12 +16,12 @@ const isPositiveDecimalString = (value: string): boolean => {
|
||||
const isMinConfirmations = (value: unknown): boolean =>
|
||||
typeof value === 'number' && Number.isInteger(value) && value >= 0;
|
||||
|
||||
const isMoneroConfirmationTier = (value: unknown): value is MoneroConfirmationTier => {
|
||||
const isConfirmationTier = (value: unknown): value is ConfirmationTier => {
|
||||
if (typeof value !== 'object' || value === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const tier = value as MoneroConfirmationTier;
|
||||
const tier = value as ConfirmationTier;
|
||||
|
||||
if (!isMinConfirmations(tier.minConfirmations)) {
|
||||
return false;
|
||||
@@ -34,7 +34,7 @@ const isMoneroConfirmationTier = (value: unknown): value is MoneroConfirmationTi
|
||||
return typeof tier.upToTotalFiat === 'string' && isPositiveDecimalString(tier.upToTotalFiat);
|
||||
};
|
||||
|
||||
const isValidMoneroConfirmationTiersJson = (raw: string): boolean => {
|
||||
const isValidConfirmationTiersJson = (raw: string): boolean => {
|
||||
let parsed: unknown;
|
||||
|
||||
try {
|
||||
@@ -43,7 +43,7 @@ const isValidMoneroConfirmationTiersJson = (raw: string): boolean => {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isMoneroConfirmationTier)) {
|
||||
if (!Array.isArray(parsed) || parsed.length === 0 || !parsed.every(isConfirmationTier)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -75,19 +75,19 @@ const isValidMoneroConfirmationTiersJson = (raw: string): boolean => {
|
||||
return true;
|
||||
};
|
||||
|
||||
@ValidatorConstraint({ name: 'isMoneroConfirmationTiers' })
|
||||
class IsMoneroConfirmationTiersConstraint implements ValidatorConstraintInterface {
|
||||
@ValidatorConstraint({ name: 'isConfirmationTiers' })
|
||||
class IsConfirmationTiersConstraint implements ValidatorConstraintInterface {
|
||||
validate(value: unknown): boolean {
|
||||
if (typeof value !== 'string' || !value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isValidMoneroConfirmationTiersJson(value);
|
||||
return isValidConfirmationTiersJson(value);
|
||||
}
|
||||
|
||||
defaultMessage(): string {
|
||||
return '$property must be a non-empty JSON array of Monero confirmation tiers; minConfirmations must be 0 (tx-detected) or an integer >= 1, 0 may appear only once and not on the catch-all tier, non-final tiers need a positive upToTotalFiat in shop fiat currency, and the last tier must be a catch-all without upToTotalFiat';
|
||||
return '$property must be a non-empty JSON array of confirmation tiers; minConfirmations must be 0 (tx-detected) or an integer >= 1, 0 may appear only once and not on the catch-all tier, non-final tiers need a positive upToTotalFiat in shop fiat currency, and the last tier must be a catch-all without upToTotalFiat';
|
||||
}
|
||||
}
|
||||
|
||||
export const IsMoneroConfirmationTiers = () => Validate(IsMoneroConfirmationTiersConstraint);
|
||||
export const IsConfirmationTiers = () => Validate(IsConfirmationTiersConstraint);
|
||||
Reference in New Issue
Block a user