Skip to content

Commit 94a3379

Browse files
TaprootFreakdavidleomayxlamnbernd2022Danswar
authored
Release: develop -> main (#2917)
* feat(liquidity-management): add swap command to DfxDexAdapter (#2882) * feat(liquidity-management): add swap command to DfxDexAdapter Add new 'swap' command for cross-asset liquidity acquisition via DEX. Unlike 'purchase' (same asset), 'swap' allows converting one asset to another, e.g., USDC → EURC via Uniswap. - Add SWAP to DfxDexAdapterCommands enum - Implement swap() method with source asset from params - Add param validation for sourceAsset parameter - Reuse checkSellPurchaseCompletion for order tracking Usage: Action params {"sourceAsset": "USDC"} with rule targetAsset EURC will swap available USDC to EURC via the configured DEX pool. * refactor: consolidate SWAP into PURCHASE/SELL with tradeAsset parameter - Remove SWAP command from DfxDexAdapterCommands enum - Add optional tradeAsset parameter to PURCHASE and SELL commands - Add resolveTradeAsset() helper for cross-asset operations - Add validateTradeAssetParams() for parameter validation When tradeAsset is provided, the command uses it as the source asset for cross-asset operations (e.g., USDC → EURC via DEX). When not provided, behavior remains unchanged (same-asset operation). This creates a symmetric API where both PURCHASE and SELL support cross-asset operations through a single optional parameter. * refactor: simplify tradeAsset - only for PURCHASE, not SELL - Remove tradeAsset support from SELL (sellLiquidity not implemented for any asset) - Simplify return type to Asset instead of Awaited<ReturnType<...>> - Keep SELL with original simple implementation * perf: avoid unnecessary async call when no tradeAsset provided * style: fix prettier formatting * feat: cleanup + added sell * fix: fixed swap amounts --------- Co-authored-by: David May <david.leo.may@gmail.com> * [DEV-4424] Implemented forwarded pay-in return (#2913) * [DEV-4424] Implemented forwarded pay-in return * [DEV-4424] Fix tests * feat(aml): add NAME_TOO_SHORT check for bank payout validation (#2879) * feat(aml): add NAME_TOO_SHORT check for bank payout validation Add AML check to reject transactions where the user's name contains fewer than 4 letters. Banks require a minimum name length for processing. Changes: - Add AmlError.NAME_TOO_SHORT with CRUCIAL type and FAIL status - Add AmlReason.NAME_TOO_SHORT - Add countLetters() helper to count only alphabetic characters - Add name length validation in getAmlErrors() after NAME_MISSING check - Update SiftAmlDeclineMap and TransactionReasonMapper The check uses verifiedName, bankData.name, or completeName (in that order) and counts only letters (including European special characters like ä, ö, ü, ß). * feat(i18n): add name_too_short chargeback reason translations Add email translations for NAME_TOO_SHORT AML reason in all 6 languages: - EN, DE, FR, IT, ES, PT This text is shown to users when their transaction is refunded due to the account holder name having fewer than 4 letters, which banks require for processing payouts. * fix: use Unicode property escape for letter counting Replace hardcoded character list with \p{L} Unicode property escape to correctly count letters in all languages (Scandinavian, Polish, Czech, Turkish, etc.). Before: 'Åse Ødegård' counted as 6 letters (å, Ø ignored) After: 'Åse Ødegård' counted as 10 letters (correct) * fix: use Sepolia blockchain for REALU on DEV/LOC (#2915) * fix: use Sepolia blockchain for REALU on DEV/LOC On DEV and LOC environments, REALU transactions were being broadcast to Ethereum Mainnet instead of Sepolia testnet. This caused the sell confirm flow to fail since actual tokens are on Sepolia. Change tokenBlockchain to be environment-aware: use Sepolia on DEV/LOC, Ethereum on PROD/STG. * fix: support Sepolia for EIP-7702 delegation on DEV/LOC Update isDelegationSupportedForRealUnit() to accept Sepolia blockchain on DEV/LOC environments. Without this, the EIP-7702 transfer would fail with 'delegation not supported' error even though the asset blockchain is correctly set to Sepolia. * fix: update buy simulation to use Sepolia REALU on DEV/LOC Since TransactionRequests are now created with Sepolia REALU asset, the buy simulation must also search for Sepolia REALU targetId. - Remove mainnet REALU lookup (no longer needed) - Search for Sepolia REALU targetId instead of mainnet - Update tests to match new behavior * fix: always use Mainnet asset for REALU price lookups Price data is only available for Mainnet REALU asset. Add separate getMainnetRealuAsset() method for price-related functions while keeping getRealuAsset() environment-based for transactions. - getRealUnitPrice() now uses Mainnet asset - getHistoricalPrice() now uses Mainnet asset - Transaction methods (buy/sell) still use environment-based asset * refactor RealUnitDevService variables. * chore: revert fetch prices only from mainnet. --------- Co-authored-by: TuanLamNguyen <xnguyen.lam@gmail.com> * fix(bank-data): transform empty string names to null in DTOs (#2918) Empty strings in bankData.name bypass nullish coalescing fallback logic because ?? treats "" as a present value. This causes incorrect behavior in AML name resolution where empty strings are used instead of actual user data. Add @Transform decorator to name field in CreateBankDataDto and UpdateBankDataDto that trims whitespace and converts empty strings to null, preventing invalid data at the source. Closes #2911 * Update XT/DEURO liquidity minimum from 4300 to 10000 (#2920) --------- Co-authored-by: TaprootFreak <142087526+TaprootFreak@users.noreply.github.com> Co-authored-by: David May <david.leo.may@gmail.com> Co-authored-by: David May <85513542+davidleomay@users.noreply.github.com> Co-authored-by: TuanLamNguyen <xnguyen.lam@gmail.com> Co-authored-by: bernd2022 <104787072+bernd2022@users.noreply.github.com> Co-authored-by: Danswar <48102227+Danswar@users.noreply.github.com>
2 parents 280a4bd + 029d05c commit 94a3379

25 files changed

Lines changed: 353 additions & 90 deletions

File tree

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @typedef {import('typeorm').MigrationInterface} MigrationInterface
3+
* @typedef {import('typeorm').QueryRunner} QueryRunner
4+
*/
5+
6+
/**
7+
* @class
8+
* @implements {MigrationInterface}
9+
*/
10+
module.exports = class UpdateXtDeuroLiquidityMinimum1768315830503 {
11+
name = 'UpdateXtDeuroLiquidityMinimum1768315830503'
12+
13+
/**
14+
* @param {QueryRunner} queryRunner
15+
*/
16+
async up(queryRunner) {
17+
// Update XT/DEURO liquidity rule minimum from 4300 to 10000
18+
await queryRunner.query(`
19+
UPDATE "dbo"."liquidity_management_rule"
20+
SET "minimal" = 10000
21+
WHERE "id" = 295
22+
`);
23+
}
24+
25+
/**
26+
* @param {QueryRunner} queryRunner
27+
*/
28+
async down(queryRunner) {
29+
// Revert XT/DEURO liquidity rule minimum back to 4300
30+
await queryRunner.query(`
31+
UPDATE "dbo"."liquidity_management_rule"
32+
SET "minimal" = 4300
33+
WHERE "id" = 295
34+
`);
35+
}
36+
}

src/integration/blockchain/shared/evm/delegation/eip7702-delegation.service.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { GetConfig } from 'src/config/config';
2+
import { Config, Environment, GetConfig } from 'src/config/config';
33
import { Blockchain } from 'src/integration/blockchain/shared/enums/blockchain.enum';
44
import { Asset } from 'src/shared/models/asset/asset.entity';
55
import { DfxLogger } from 'src/shared/services/dfx-logger';
@@ -84,7 +84,10 @@ export class Eip7702DelegationService {
8484
* RealUnit app supports eth_sign (unlike MetaMask), so EIP-7702 works
8585
*/
8686
isDelegationSupportedForRealUnit(blockchain: Blockchain): boolean {
87-
return blockchain === Blockchain.ETHEREUM && CHAIN_CONFIG[blockchain] !== undefined;
87+
const expectedBlockchain = [Environment.DEV, Environment.LOC].includes(Config.environment)
88+
? Blockchain.SEPOLIA
89+
: Blockchain.ETHEREUM;
90+
return blockchain === expectedBlockchain && CHAIN_CONFIG[blockchain] !== undefined;
8891
}
8992

9093
/**

src/integration/sift/dto/sift.dto.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,6 +1037,7 @@ export const SiftAmlDeclineMap: { [method in AmlReason]: DeclineCategory } = {
10371037
[AmlReason.BANK_RELEASE_PENDING]: DeclineCategory.OTHER,
10381038
[AmlReason.VIRTUAL_IBAN_USER_MISMATCH]: DeclineCategory.RISKY,
10391039
[AmlReason.INTERMEDIARY_WITHOUT_SENDER]: DeclineCategory.RISKY,
1040+
[AmlReason.NAME_TOO_SHORT]: DeclineCategory.OTHER,
10401041
};
10411042

10421043
export interface ScoreRsponse {

src/shared/i18n/de/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "Wir konnten dich unter deiner angegebenen Telefonnummer nicht erreichen",
9696
"manual_check_ip_country_phone": "Wir konnten dich unter deiner angegebenen Telefonnummer nicht erreichen",
9797
"merge_incomplete": "Die Email Bestätigung wurde nicht akzeptiert",
98-
"intermediary_without_sender": "Die Absenderbank (Wise/Revolut) hat nur den Banknamen übermittelt, nicht aber den Namen des Kontoinhabers. DFX kann daher den tatsächlichen Absender nicht verifizieren und die Transaktion nicht verarbeiten."
98+
"intermediary_without_sender": "Die Absenderbank (Wise/Revolut) hat nur den Banknamen übermittelt, nicht aber den Namen des Kontoinhabers. DFX kann daher den tatsächlichen Absender nicht verifizieren und die Transaktion nicht verarbeiten.",
99+
"name_too_short": "Dein Name ist zu kurz für die Bankverarbeitung. Banken benötigen mindestens 4 Buchstaben im Namen des Kontoinhabers."
99100
},
100101
"kyc_start": "Du kannst den KYC Prozess hier starten:<br>[url:{urlText}]"
101102
},

src/shared/i18n/en/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "We were unable to reach you at the phone number you provided",
9696
"manual_check_ip_country_phone": "We were unable to reach you at the phone number you provided",
9797
"merge_incomplete": "The email confirmation was not accepted",
98-
"intermediary_without_sender": "The sender bank (Wise/Revolut) only transmitted the bank name, not the account holder's name. DFX is therefore unable to verify the actual sender and cannot process the transaction."
98+
"intermediary_without_sender": "The sender bank (Wise/Revolut) only transmitted the bank name, not the account holder's name. DFX is therefore unable to verify the actual sender and cannot process the transaction.",
99+
"name_too_short": "Your name is too short for bank processing. Banks require at least 4 letters in the account holder name."
99100
},
100101
"kyc_start": "You can start the KYC process here:<br>[url:{urlText}]"
101102
},

src/shared/i18n/es/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "No hemos podido contactar con usted al número de teléfono que nos facilitó",
9696
"manual_check_ip_country_phone": "No hemos podido contactar con usted al número de teléfono que nos facilitó",
9797
"merge_incomplete": "El correo electrónico de confirmación no fue aceptado",
98-
"intermediary_without_sender": "El banco emisor (Wise/Revolut) solo transmitió el nombre del banco, no el nombre del titular de la cuenta. Por lo tanto, DFX no puede verificar el remitente real y no puede procesar la transacción."
98+
"intermediary_without_sender": "El banco emisor (Wise/Revolut) solo transmitió el nombre del banco, no el nombre del titular de la cuenta. Por lo tanto, DFX no puede verificar el remitente real y no puede procesar la transacción.",
99+
"name_too_short": "Tu nombre es demasiado corto para el procesamiento bancario. Los bancos requieren al menos 4 letras en el nombre del titular de la cuenta."
99100
},
100101
"kyc_start": "Puede iniciar el proceso KYC aquí:<br>[url:{urlText}]"
101102
},

src/shared/i18n/fr/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "Nous n'avons pas réussi à vous joindre au numéro de téléphone que vous avez fourni",
9696
"manual_check_ip_country_phone": "Nous n'avons pas réussi à vous joindre au numéro de téléphone que vous avez fourni",
9797
"merge_incomplete": "L'e-mail de confirmation n'a pas été accepté",
98-
"intermediary_without_sender": "La banque émettrice (Wise/Revolut) n'a transmis que le nom de la banque, et non le nom du titulaire du compte. DFX ne peut donc pas vérifier l'expéditeur réel et ne peut pas traiter la transaction."
98+
"intermediary_without_sender": "La banque émettrice (Wise/Revolut) n'a transmis que le nom de la banque, et non le nom du titulaire du compte. DFX ne peut donc pas vérifier l'expéditeur réel et ne peut pas traiter la transaction.",
99+
"name_too_short": "Votre nom est trop court pour le traitement bancaire. Les banques exigent au moins 4 lettres dans le nom du titulaire du compte."
99100
},
100101
"kyc_start": "Vous pouvez commencer le processus KYC ici:<br>[url:{urlText}]"
101102
},

src/shared/i18n/it/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "Non siamo riusciti a contattarti al numero di telefono che ci hai fornito",
9696
"manual_check_ip_country_phone": "Non siamo riusciti a contattarti al numero di telefono che ci hai fornito",
9797
"merge_incomplete": "L'e-mail di conferma non è stata accettata",
98-
"intermediary_without_sender": "La banca mittente (Wise/Revolut) ha trasmesso solo il nome della banca, non il nome del titolare del conto. DFX non può quindi verificare il mittente effettivo e non può elaborare la transazione."
98+
"intermediary_without_sender": "La banca mittente (Wise/Revolut) ha trasmesso solo il nome della banca, non il nome del titolare del conto. DFX non può quindi verificare il mittente effettivo e non può elaborare la transazione.",
99+
"name_too_short": "Il tuo nome è troppo corto per l'elaborazione bancaria. Le banche richiedono almeno 4 lettere nel nome del titolare del conto."
99100
},
100101
"kyc_start": "Potete iniziare il processo KYC qui:<br>[url:{urlText}]"
101102
},

src/shared/i18n/pt/mail.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@
9595
"manual_check_ip_phone": "We were unable to reach you at the phone number you provided",
9696
"manual_check_ip_country_phone": "We were unable to reach you at the phone number you provided",
9797
"merge_incomplete": "The email confirmation was not accepted",
98-
"intermediary_without_sender": "O banco remetente (Wise/Revolut) transmitiu apenas o nome do banco, não o nome do titular da conta. Portanto, a DFX não pode verificar o remetente real e não pode processar a transação."
98+
"intermediary_without_sender": "O banco remetente (Wise/Revolut) transmitiu apenas o nome do banco, não o nome do titular da conta. Portanto, a DFX não pode verificar o remetente real e não pode processar a transação.",
99+
"name_too_short": "O seu nome é muito curto para o processamento bancário. Os bancos exigem pelo menos 4 letras no nome do titular da conta."
99100
},
100101
"kyc_start": "You can start the KYC process here:<br>[url:{urlText}]"
101102
},

src/subdomains/core/aml/enums/aml-error.enum.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export enum AmlError {
2626
INVALID_KYC_TYPE = 'InvalidKycType',
2727
NO_VERIFIED_NAME = 'NoVerifiedName',
2828
NAME_MISSING = 'NameMissing',
29+
NAME_TOO_SHORT = 'NameTooShort',
2930
VERIFIED_COUNTRY_NOT_ALLOWED = 'VerifiedCountryNotAllowed',
3031
IBAN_COUNTRY_FATF_NOT_ALLOWED = 'IbanCountryFatfNotAllowed',
3132
TX_COUNTRY_NOT_ALLOWED = 'TxCountryNotAllowed',
@@ -151,6 +152,11 @@ export const AmlErrorResult: {
151152
amlCheck: CheckStatus.PENDING,
152153
amlReason: AmlReason.KYC_DATA_NEEDED,
153154
},
155+
[AmlError.NAME_TOO_SHORT]: {
156+
type: AmlErrorType.CRUCIAL,
157+
amlCheck: CheckStatus.FAIL,
158+
amlReason: AmlReason.NAME_TOO_SHORT,
159+
},
154160
[AmlError.VERIFIED_COUNTRY_NOT_ALLOWED]: {
155161
type: AmlErrorType.CRUCIAL,
156162
amlCheck: CheckStatus.GSHEET,

0 commit comments

Comments
 (0)