diff --git a/src/integration/sift/dto/sift.dto.ts b/src/integration/sift/dto/sift.dto.ts index 843a139f0b..d657d12cb6 100644 --- a/src/integration/sift/dto/sift.dto.ts +++ b/src/integration/sift/dto/sift.dto.ts @@ -1038,6 +1038,7 @@ export const SiftAmlDeclineMap: { [method in AmlReason]: DeclineCategory } = { [AmlReason.VIRTUAL_IBAN_USER_MISMATCH]: DeclineCategory.RISKY, [AmlReason.INTERMEDIARY_WITHOUT_SENDER]: DeclineCategory.RISKY, [AmlReason.NAME_TOO_SHORT]: DeclineCategory.OTHER, + [AmlReason.ASSET_INPUT_NOT_ALLOWED]: DeclineCategory.INVALID, }; export interface ScoreRsponse { diff --git a/src/subdomains/core/aml/enums/aml-error.enum.ts b/src/subdomains/core/aml/enums/aml-error.enum.ts index b566c6bd56..e8839c8210 100644 --- a/src/subdomains/core/aml/enums/aml-error.enum.ts +++ b/src/subdomains/core/aml/enums/aml-error.enum.ts @@ -67,6 +67,7 @@ export enum AmlError { TRADE_APPROVAL_DATE_MISSING = 'TradeApprovalDateMissing', BANK_TX_CUSTOMER_NAME_MISSING = 'BankTxCustomerNameMissing', FORCE_MANUAL_CHECK = 'ForceManualCheck', + ASSET_INPUT_NOT_ALLOWED = 'AssetInputNotAllowed', } export const DelayResultError = [ @@ -328,4 +329,9 @@ export const AmlErrorResult: { amlCheck: CheckStatus.PENDING, amlReason: AmlReason.MANUAL_CHECK, }, + [AmlError.ASSET_INPUT_NOT_ALLOWED]: { + type: AmlErrorType.CRUCIAL, + amlCheck: CheckStatus.FAIL, + amlReason: AmlReason.ASSET_INPUT_NOT_ALLOWED, + }, }; diff --git a/src/subdomains/core/aml/enums/aml-reason.enum.ts b/src/subdomains/core/aml/enums/aml-reason.enum.ts index 5f55419276..1bdeadcb68 100644 --- a/src/subdomains/core/aml/enums/aml-reason.enum.ts +++ b/src/subdomains/core/aml/enums/aml-reason.enum.ts @@ -40,6 +40,7 @@ export enum AmlReason { VIRTUAL_IBAN_USER_MISMATCH = 'VirtualIbanUserMismatch', INTERMEDIARY_WITHOUT_SENDER = 'IntermediaryWithoutSender', NAME_TOO_SHORT = 'NameTooShort', + ASSET_INPUT_NOT_ALLOWED = 'AssetInputNotAllowed', } export const KycAmlReasons = [ diff --git a/src/subdomains/core/aml/services/aml-helper.service.ts b/src/subdomains/core/aml/services/aml-helper.service.ts index cf4a777d22..de2bbc2dab 100644 --- a/src/subdomains/core/aml/services/aml-helper.service.ts +++ b/src/subdomains/core/aml/services/aml-helper.service.ts @@ -1,5 +1,5 @@ import { Config, Environment } from 'src/config/config'; -import { Active } from 'src/shared/models/active'; +import { Active, isAsset } from 'src/shared/models/active'; import { Country } from 'src/shared/models/country/country.entity'; import { DisabledProcess, Process } from 'src/shared/services/process.service'; import { Util } from 'src/shared/utils/util'; @@ -52,6 +52,8 @@ export class AmlHelperService { ) return errors; + if (isAsset(inputAsset) && inputAsset.name === 'REALU') errors.push(AmlError.ASSET_INPUT_NOT_ALLOWED); + if ( !DisabledProcess(Process.TRADE_APPROVAL_DATE) && !entity.userData.tradeApprovalDate && diff --git a/src/subdomains/supporting/log/log-job.service.ts b/src/subdomains/supporting/log/log-job.service.ts index dc6e98587a..0ff2287301 100644 --- a/src/subdomains/supporting/log/log-job.service.ts +++ b/src/subdomains/supporting/log/log-job.service.ts @@ -454,10 +454,16 @@ export class LogJobService { const cryptoInput = [Blockchain.MONERO, Blockchain.LIGHTNING, Blockchain.ZANO].includes(curr.blockchain) ? 0 : pendingPayIns.reduce((sum, tx) => sum + (tx.asset.id === curr.id ? tx.amount : 0), 0); - const exchangeOrder = pendingExchangeOrders.reduce( - (sum, tx) => sum + (tx.pipeline.rule.targetAsset.id === curr.id ? tx.inputAmount : 0), - 0, - ); + const exchangeOrder = pendingExchangeOrders.reduce((sum, tx) => { + if (tx.pipeline.rule.targetAsset.id !== curr.id) return sum; + + // for transfer/deposit: only count when action.system matches the target asset's exchange + // (funds leaving this exchange, balance decreased). Skip when funds arrive from another + // exchange, as the destination balance already reflects those funds before order completion. + if (tx.action.command !== 'withdraw' && tx.action.system !== (curr.blockchain as string)) return sum; + + return sum + tx.inputAmount; + }, 0); const bridgeOrder = pendingBridgeOrders.reduce( (sum, tx) => sum + (tx.pipeline.rule.targetAsset.id === curr.id ? tx.inputAmount : 0), 0, diff --git a/src/subdomains/supporting/payment/dto/transaction.dto.ts b/src/subdomains/supporting/payment/dto/transaction.dto.ts index 2f7876c026..72a0fc48e1 100644 --- a/src/subdomains/supporting/payment/dto/transaction.dto.ts +++ b/src/subdomains/supporting/payment/dto/transaction.dto.ts @@ -115,6 +115,7 @@ export const TransactionReasonMapper: { [AmlReason.VIRTUAL_IBAN_USER_MISMATCH]: TransactionReason.UNKNOWN, [AmlReason.INTERMEDIARY_WITHOUT_SENDER]: TransactionReason.BANK_NOT_ALLOWED, [AmlReason.NAME_TOO_SHORT]: TransactionReason.KYC_DATA_NEEDED, + [AmlReason.ASSET_INPUT_NOT_ALLOWED]: TransactionReason.ASSET_NOT_AVAILABLE, }; export class UnassignedTransactionDto {