289 lines
7.6 KiB
TypeScript
289 lines
7.6 KiB
TypeScript
// Quantum Translation Service
|
|
// FX & risk translation for legacy protocols
|
|
|
|
import prisma from '@/shared/database/prisma';
|
|
import { Decimal } from '@prisma/client/runtime/library';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import { logger } from '@/infrastructure/monitoring/logger';
|
|
|
|
|
|
export interface QuantumTranslationRequest {
|
|
legacyProtocol: 'SWIFT' | 'ISO20022' | 'ACH' | 'SEPA' | 'PRIVATE_BANK';
|
|
transactionData: any;
|
|
amount: string;
|
|
currencyCode: string;
|
|
}
|
|
|
|
export interface QuantumTranslationResult {
|
|
translationId: string;
|
|
fxRate: Decimal;
|
|
riskScore: Decimal;
|
|
quantumCompatibleAmount: Decimal;
|
|
quantumCompatibleCurrency: string;
|
|
protocolMapping: any;
|
|
}
|
|
|
|
export class QuantumTranslationService {
|
|
/**
|
|
* Translate legacy transaction to quantum-compatible format
|
|
* Handles FX conversion and risk assessment
|
|
*/
|
|
async translateLegacyTransaction(
|
|
request: QuantumTranslationRequest
|
|
): Promise<QuantumTranslationResult> {
|
|
logger.info('QPS: Translating legacy transaction', {
|
|
legacyProtocol: request.legacyProtocol,
|
|
});
|
|
|
|
const translationId = `QPS-TRANS-${uuidv4()}`;
|
|
|
|
// Step 1: Get protocol mapping
|
|
const protocolMapping = await this.getProtocolMapping(request.legacyProtocol);
|
|
|
|
// Step 2: Translate FX
|
|
const fxTranslation = await this.translateFx({
|
|
amount: request.amount,
|
|
currencyCode: request.currencyCode,
|
|
legacyProtocol: request.legacyProtocol,
|
|
});
|
|
|
|
// Step 3: Translate risk
|
|
const riskTranslation = await this.translateRisk({
|
|
transactionData: request.transactionData,
|
|
legacyProtocol: request.legacyProtocol,
|
|
});
|
|
|
|
// Step 4: Create quantum-compatible format
|
|
const quantumCompatibleAmount = fxTranslation.quantumAmount;
|
|
const quantumCompatibleCurrency = fxTranslation.quantumCurrency;
|
|
|
|
// Step 5: Save translation record
|
|
const translation = await prisma.quantum_translations.create({
|
|
data: {
|
|
id: uuidv4(),
|
|
translationId,
|
|
legacyProtocol: request.legacyProtocol,
|
|
legacyAmount: new Decimal(request.amount),
|
|
legacyCurrency: request.currencyCode,
|
|
quantumAmount: quantumCompatibleAmount,
|
|
quantumCurrency: quantumCompatibleCurrency,
|
|
fxRate: fxTranslation.fxRate,
|
|
riskScore: riskTranslation.riskScore,
|
|
protocolMapping: protocolMapping as any,
|
|
transactionData: request.transactionData as any,
|
|
status: 'completed',
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
},
|
|
});
|
|
|
|
logger.info('QPS: Translation completed', {
|
|
translationId,
|
|
quantumAmount: quantumCompatibleAmount.toString(),
|
|
});
|
|
|
|
return {
|
|
translationId,
|
|
fxRate: fxTranslation.fxRate,
|
|
riskScore: riskTranslation.riskScore,
|
|
quantumCompatibleAmount,
|
|
quantumCompatibleCurrency,
|
|
protocolMapping,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get protocol mapping for legacy protocol
|
|
*/
|
|
private async getProtocolMapping(
|
|
legacyProtocol: string
|
|
): Promise<any> {
|
|
const mapping = await prisma.legacy_protocol_mappings.findFirst({
|
|
where: { legacyProtocol, status: 'active' },
|
|
});
|
|
|
|
if (mapping) {
|
|
return mapping.mappingConfig;
|
|
}
|
|
|
|
// Default mappings
|
|
const defaultMappings: Record<string, any> = {
|
|
SWIFT: {
|
|
messageType: 'MT103',
|
|
quantumFormat: 'ISO20022',
|
|
fieldMapping: {
|
|
sender: 'sourceBankId',
|
|
receiver: 'destinationBankId',
|
|
amount: 'amount',
|
|
currency: 'currencyCode',
|
|
},
|
|
},
|
|
ISO20022: {
|
|
messageType: 'pacs.008',
|
|
quantumFormat: 'ISO20022',
|
|
fieldMapping: {
|
|
debtor: 'sourceBankId',
|
|
creditor: 'destinationBankId',
|
|
amount: 'amount',
|
|
currency: 'currencyCode',
|
|
},
|
|
},
|
|
ACH: {
|
|
messageType: 'ACH',
|
|
quantumFormat: 'ISO20022',
|
|
fieldMapping: {
|
|
originator: 'sourceBankId',
|
|
receiver: 'destinationBankId',
|
|
amount: 'amount',
|
|
currency: 'currencyCode',
|
|
},
|
|
},
|
|
SEPA: {
|
|
messageType: 'pain.001',
|
|
quantumFormat: 'ISO20022',
|
|
fieldMapping: {
|
|
debtor: 'sourceBankId',
|
|
creditor: 'destinationBankId',
|
|
amount: 'amount',
|
|
currency: 'currencyCode',
|
|
},
|
|
},
|
|
PRIVATE_BANK: {
|
|
messageType: 'PRIVATE',
|
|
quantumFormat: 'ISO20022',
|
|
fieldMapping: {
|
|
from: 'sourceBankId',
|
|
to: 'destinationBankId',
|
|
value: 'amount',
|
|
currency: 'currencyCode',
|
|
},
|
|
},
|
|
};
|
|
|
|
return defaultMappings[legacyProtocol] || {};
|
|
}
|
|
|
|
/**
|
|
* Translate FX for legacy protocol
|
|
*/
|
|
private async translateFx(data: {
|
|
amount: string;
|
|
currencyCode: string;
|
|
legacyProtocol: string;
|
|
}): Promise<{
|
|
fxRate: Decimal;
|
|
quantumAmount: Decimal;
|
|
quantumCurrency: string;
|
|
}> {
|
|
// In production, would fetch real-time FX rates
|
|
// For now, use 1:1 for same currency, or apply protocol-specific rates
|
|
const amount = new Decimal(data.amount);
|
|
|
|
// Default: quantum currency is same as legacy currency
|
|
let quantumCurrency = data.currencyCode;
|
|
let fxRate = new Decimal(1);
|
|
|
|
// Protocol-specific FX adjustments
|
|
switch (data.legacyProtocol) {
|
|
case 'SWIFT':
|
|
// SWIFT typically uses spot rates
|
|
fxRate = new Decimal(1); // Placeholder
|
|
break;
|
|
case 'ISO20022':
|
|
// ISO20022 uses reference rates
|
|
fxRate = new Decimal(1); // Placeholder
|
|
break;
|
|
case 'ACH':
|
|
// ACH uses domestic rates
|
|
fxRate = new Decimal(1); // Placeholder
|
|
break;
|
|
case 'SEPA':
|
|
// SEPA uses EUR rates
|
|
if (data.currencyCode !== 'EUR') {
|
|
// Convert to EUR
|
|
quantumCurrency = 'EUR';
|
|
fxRate = new Decimal(0.85); // Example EUR rate
|
|
}
|
|
break;
|
|
default:
|
|
fxRate = new Decimal(1);
|
|
}
|
|
|
|
const quantumAmount = amount.times(fxRate);
|
|
|
|
return {
|
|
fxRate,
|
|
quantumAmount,
|
|
quantumCurrency,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Translate risk for legacy protocol
|
|
*/
|
|
private async translateRisk(data: {
|
|
transactionData: any;
|
|
legacyProtocol: string;
|
|
}): Promise<{ riskScore: Decimal }> {
|
|
// Base risk score
|
|
let riskScore = new Decimal(0.1); // Low risk by default
|
|
|
|
// Protocol-specific risk adjustments
|
|
switch (data.legacyProtocol) {
|
|
case 'SWIFT':
|
|
// SWIFT has lower risk (established network)
|
|
riskScore = new Decimal(0.05);
|
|
break;
|
|
case 'ISO20022':
|
|
// ISO20022 has standardized risk
|
|
riskScore = new Decimal(0.08);
|
|
break;
|
|
case 'ACH':
|
|
// ACH has domestic risk
|
|
riskScore = new Decimal(0.12);
|
|
break;
|
|
case 'SEPA':
|
|
// SEPA has EU risk
|
|
riskScore = new Decimal(0.06);
|
|
break;
|
|
case 'PRIVATE_BANK':
|
|
// Private bank has higher risk
|
|
riskScore = new Decimal(0.15);
|
|
break;
|
|
}
|
|
|
|
// Additional risk factors from transaction data
|
|
if (data.transactionData.amount > 1000000) {
|
|
riskScore = riskScore.plus(0.1); // Large amount increases risk
|
|
}
|
|
|
|
return { riskScore };
|
|
}
|
|
|
|
/**
|
|
* Get translation by ID
|
|
*/
|
|
async getTranslation(translationId: string) {
|
|
return await prisma.quantum_translations.findUnique({
|
|
where: { translationId },
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get translations by protocol
|
|
*/
|
|
async getTranslationsByProtocol(
|
|
legacyProtocol: string,
|
|
limit: number = 100
|
|
) {
|
|
return await prisma.quantum_translations.findMany({
|
|
where: { legacyProtocol },
|
|
orderBy: { createdAt: 'desc' },
|
|
take: limit,
|
|
});
|
|
}
|
|
}
|
|
|
|
export const quantumTranslationService = new QuantumTranslationService();
|
|
|