From 294fb75f185b6eb123ae4c8fc3291006e538167e Mon Sep 17 00:00:00 2001
From: Yannick <52333989+Yannick1712@users.noreply.github.com>
Date: Mon, 2 Mar 2026 12:16:56 +0100
Subject: [PATCH 1/2] [NOTASK] mail texts input not allowed (#3290)
* [NOTASK] mail texts input not allowed
* [NOTASK] change text
---
src/shared/i18n/de/mail.json | 5 +++--
src/shared/i18n/en/mail.json | 3 ++-
src/shared/i18n/es/mail.json | 3 ++-
src/shared/i18n/fr/mail.json | 3 ++-
src/shared/i18n/it/mail.json | 3 ++-
src/shared/i18n/pt/mail.json | 3 ++-
6 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/src/shared/i18n/de/mail.json b/src/shared/i18n/de/mail.json
index aaa27b0fe0..ee4cdc2066 100644
--- a/src/shared/i18n/de/mail.json
+++ b/src/shared/i18n/de/mail.json
@@ -71,7 +71,7 @@
"min_deposit_not_reached": "Die Mindesteinzahlungsgrenze wurde nicht erreicht",
"name_check_without_kyc": "Ihre Transaktion wurde wegen fehlender KYC storniert
Aufgrund von Namensübereinstimmungen ist es nicht möglich, die Transaktion ohne KYC zu bearbeiten. Siehe hier für weitere Details:
[url:{urlText}]",
"annual_limit_without_kyc": "Du hast das Jahreslimit ohne Verifizierung (KYC) überschritten",
- "asset_currently_not_available": "Der Vermögenswert ist derzeit nicht für den Handel mit DFX verfügbar",
+ "asset_currently_not_available": "Das Asset ist derzeit nicht für den Handel mit DFX verfügbar",
"staking_discontinued": "Staking-Einzahlungen sind nicht mehr möglich",
"bank_not_allowed": "Die von dir verwendete Absenderbank ist nicht erlaubt. Bitte nutze eine andere Bank für deine Einzahlung.",
"country_not_allowed": "Deine Einzahlung kann leider nicht bearbeitet werden,
weil das Geld aus einem Land stammt welches wir nicht unterstützen können. Mehr Informationen:
https://www.fatf-gafi.org/en/countries/black-and-grey-lists.html",
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "Wir konnten dich unter deiner angegebenen Telefonnummer nicht erreichen",
"merge_incomplete": "Die Email Bestätigung wurde nicht akzeptiert",
"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.",
- "name_too_short": "Dein Name ist zu kurz für die Bankverarbeitung. Banken benötigen mindestens 4 Buchstaben im Namen des Kontoinhabers."
+ "name_too_short": "Dein Name ist zu kurz für die Bankverarbeitung. Banken benötigen mindestens 4 Buchstaben im Namen des Kontoinhabers.",
+ "asset_input_not_allowed": "Das Asset ist derzeit nicht für den Handel mit DFX verfügbar"
},
"kyc_start": "Du kannst den KYC Prozess hier starten:
[url:{urlText}]"
},
diff --git a/src/shared/i18n/en/mail.json b/src/shared/i18n/en/mail.json
index db40214c22..c311bc8d6d 100644
--- a/src/shared/i18n/en/mail.json
+++ b/src/shared/i18n/en/mail.json
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "We were unable to reach you at the phone number you provided",
"merge_incomplete": "The email confirmation was not accepted",
"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.",
- "name_too_short": "Your name is too short for bank processing. Banks require at least 4 letters in the account holder name."
+ "name_too_short": "Your name is too short for bank processing. Banks require at least 4 letters in the account holder name.",
+ "asset_input_not_allowed": "The asset is currently not available for trading with DFX"
},
"kyc_start": "You can start the KYC process here:
[url:{urlText}]"
},
diff --git a/src/shared/i18n/es/mail.json b/src/shared/i18n/es/mail.json
index d1a3aa8c6e..22fa89af89 100644
--- a/src/shared/i18n/es/mail.json
+++ b/src/shared/i18n/es/mail.json
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "No hemos podido contactar con usted al número de teléfono que nos facilitó",
"merge_incomplete": "El correo electrónico de confirmación no fue aceptado",
"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.",
- "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."
+ "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.",
+ "asset_input_not_allowed": "Este activo no está actualmente disponible para comerciar con DFX"
},
"kyc_start": "Puede iniciar el proceso KYC aquí:
[url:{urlText}]"
},
diff --git a/src/shared/i18n/fr/mail.json b/src/shared/i18n/fr/mail.json
index a4c7ef800d..d61645fecc 100644
--- a/src/shared/i18n/fr/mail.json
+++ b/src/shared/i18n/fr/mail.json
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "Nous n'avons pas réussi à vous joindre au numéro de téléphone que vous avez fourni",
"merge_incomplete": "L'e-mail de confirmation n'a pas été accepté",
"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.",
- "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."
+ "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.",
+ "asset_input_not_allowed": "Cet actif n'est actuellement pas disponible à l'échange avec DFX"
},
"kyc_start": "Vous pouvez commencer le processus KYC ici:
[url:{urlText}]"
},
diff --git a/src/shared/i18n/it/mail.json b/src/shared/i18n/it/mail.json
index 1f341d742b..edfbf0cb8f 100644
--- a/src/shared/i18n/it/mail.json
+++ b/src/shared/i18n/it/mail.json
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "Non siamo riusciti a contattarti al numero di telefono che ci hai fornito",
"merge_incomplete": "L'e-mail di conferma non è stata accettata",
"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.",
- "name_too_short": "Il tuo nome è troppo corto per l'elaborazione bancaria. Le banche richiedono almeno 4 lettere nel nome del titolare del conto."
+ "name_too_short": "Il tuo nome è troppo corto per l'elaborazione bancaria. Le banche richiedono almeno 4 lettere nel nome del titolare del conto.",
+ "asset_input_not_allowed": "L'asset non è al momento disponibile per lo scambio su DFX"
},
"kyc_start": "Potete iniziare il processo KYC qui:
[url:{urlText}]"
},
diff --git a/src/shared/i18n/pt/mail.json b/src/shared/i18n/pt/mail.json
index 192b00f9a9..dafefe0831 100644
--- a/src/shared/i18n/pt/mail.json
+++ b/src/shared/i18n/pt/mail.json
@@ -96,7 +96,8 @@
"manual_check_ip_country_phone": "We were unable to reach you at the phone number you provided",
"merge_incomplete": "The email confirmation was not accepted",
"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.",
- "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."
+ "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.",
+ "asset_input_not_allowed": "The asset is currently not available for trading with DFX"
},
"kyc_start": "You can start the KYC process here:
[url:{urlText}]"
},
From 1cca66ba88273b5a568bd55bd5c482cb01e07e8f Mon Sep 17 00:00:00 2001
From: bernd2022 <104787072+bernd2022@users.noreply.github.com>
Date: Mon, 2 Mar 2026 15:52:22 +0100
Subject: [PATCH 2/2] fix: support ICP transaction hash lookup via Rosetta API
(#3308)
* fix: support ICP transaction hash lookup via Ledger API v2
Resolve external TxHashes (64 hex chars, e.g. from Binance) in
isTxComplete by querying the ICP Ledger API v2. Adds configurable
ICP_LEDGER_API_URL env variable with sensible default.
* refactor: switch to Rosetta API and check operation status
Use the standard ICP Rosetta /search/transactions endpoint instead of
the Ledger API v2. Verify that all operations have status COMPLETED.
Env variable renamed to ICP_ROSETTA_API_URL. Extract RosettaTransaction
interface into icp.dto.ts.
---
src/config/config.ts | 1 +
src/integration/blockchain/icp/dto/icp.dto.ts | 8 +++++
src/integration/blockchain/icp/icp-client.ts | 34 +++++++++++++++++--
.../blockchain/icp/services/icp.service.ts | 5 +--
4 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/src/config/config.ts b/src/config/config.ts
index 854a174749..ac51243357 100644
--- a/src/config/config.ts
+++ b/src/config/config.ts
@@ -985,6 +985,7 @@ export class Configuration {
},
internetComputer: {
internetComputerHost: 'https://ic0.app',
+ internetComputerRosettaApiUrl: process.env.ICP_ROSETTA_API_URL ?? 'https://rosetta-api.internetcomputer.org',
internetComputerWalletSeed: process.env.ICP_WALLET_SEED,
internetComputerLedgerCanisterId: 'ryjl3-tyaaa-aaaaa-aaaba-cai',
transferFee: 0.0001,
diff --git a/src/integration/blockchain/icp/dto/icp.dto.ts b/src/integration/blockchain/icp/dto/icp.dto.ts
index caf0cc504f..ea4870a1bc 100644
--- a/src/integration/blockchain/icp/dto/icp.dto.ts
+++ b/src/integration/blockchain/icp/dto/icp.dto.ts
@@ -65,6 +65,14 @@ export interface CandidIcrcGetTransactionsResponse {
transactions: CandidIcrcTransaction[];
}
+// --- Rosetta API response types ---
+
+export interface RosettaTransaction {
+ transaction: {
+ operations: { status: string }[];
+ };
+}
+
// --- Typed raw ledger interfaces (for Actor.createActor results) ---
export interface IcpNativeRawLedger {
diff --git a/src/integration/blockchain/icp/icp-client.ts b/src/integration/blockchain/icp/icp-client.ts
index 3c9c9d9a39..8afae40f51 100644
--- a/src/integration/blockchain/icp/icp-client.ts
+++ b/src/integration/blockchain/icp/icp-client.ts
@@ -5,6 +5,7 @@ import { Principal } from '@dfinity/principal';
import { Config, GetConfig } from 'src/config/config';
import { Asset } from 'src/shared/models/asset/asset.entity';
import { DfxLogger } from 'src/shared/services/dfx-logger';
+import { HttpService } from 'src/shared/services/http.service';
import { Util } from 'src/shared/utils/util';
import { BlockchainTokenBalance } from '../shared/dto/blockchain-token-balance.dto';
import { BlockchainSignedTransactionResponse } from '../shared/dto/signed-transaction-reponse.dto';
@@ -17,6 +18,7 @@ import {
IcpTransfer,
IcpTransferQueryResult,
IcrcRawLedger,
+ RosettaTransaction,
} from './dto/icp.dto';
import { InternetComputerWallet } from './icp-wallet';
import { icpNativeLedgerIdlFactory, icrcLedgerIdlFactory } from './icp.idl';
@@ -26,21 +28,31 @@ export class InternetComputerClient extends BlockchainClient {
private readonly logger = new DfxLogger(InternetComputerClient);
private readonly host: string;
+ private readonly rosettaApiUrl: string;
private readonly seed: string;
private readonly wallet: InternetComputerWallet;
private readonly agent: HttpAgent;
private readonly nativeLedger: IcrcLedgerCanister;
private readonly transferFee: number;
+ private readonly http: HttpService;
private readonly nativeRawLedger: IcpNativeRawLedger;
private readonly icrcRawLedgers: Map = new Map();
- constructor() {
+ constructor(http: HttpService) {
super();
- const { internetComputerHost, internetComputerWalletSeed, internetComputerLedgerCanisterId, transferFee } =
- GetConfig().blockchain.internetComputer;
+ this.http = http;
+
+ const {
+ internetComputerHost,
+ internetComputerRosettaApiUrl,
+ internetComputerWalletSeed,
+ internetComputerLedgerCanisterId,
+ transferFee,
+ } = GetConfig().blockchain.internetComputer;
this.host = internetComputerHost;
+ this.rosettaApiUrl = internetComputerRosettaApiUrl;
this.seed = internetComputerWalletSeed;
this.transferFee = transferFee;
@@ -247,6 +259,11 @@ export class InternetComputerClient extends BlockchainClient {
async isTxComplete(txId: string): Promise {
try {
+ // TxHash from external sources: 64 hex chars
+ if (/^[a-f0-9]{64}$/i.test(txId)) {
+ return await this.isTxHashComplete(txId);
+ }
+
// Token txIds have format "canisterId:blockIndex"
const parts = txId.split(':');
if (parts.length === 2) {
@@ -266,6 +283,17 @@ export class InternetComputerClient extends BlockchainClient {
}
}
+ private async isTxHashComplete(txHash: string): Promise {
+ const url = `${this.rosettaApiUrl}/search/transactions`;
+ const response = await this.http.post<{ transactions: RosettaTransaction[] }>(url, {
+ network_identifier: { blockchain: 'Internet Computer', network: '00000000000000020101' },
+ transaction_identifier: { hash: txHash },
+ });
+
+ const tx = response.transactions[0];
+ return tx?.transaction.operations.every((op) => op.status === 'COMPLETED') ?? false;
+ }
+
// --- Send native coin ---
async sendNativeCoinFromDex(toAddress: string, amount: number): Promise {
diff --git a/src/integration/blockchain/icp/services/icp.service.ts b/src/integration/blockchain/icp/services/icp.service.ts
index 1443d55f4e..480e015270 100644
--- a/src/integration/blockchain/icp/services/icp.service.ts
+++ b/src/integration/blockchain/icp/services/icp.service.ts
@@ -3,6 +3,7 @@ import { Injectable } from '@nestjs/common';
import { secp256k1 } from '@noble/curves/secp256k1';
import { sha256 } from '@noble/hashes/sha2';
import { Asset } from 'src/shared/models/asset/asset.entity';
+import { HttpService } from 'src/shared/services/http.service';
import { Util } from 'src/shared/utils/util';
import nacl from 'tweetnacl';
import { WalletAccount } from '../../shared/evm/domain/wallet-account';
@@ -15,10 +16,10 @@ import { InternetComputerClient } from '../icp-client';
export class InternetComputerService extends BlockchainService {
private readonly client: InternetComputerClient;
- constructor() {
+ constructor(private readonly http: HttpService) {
super();
- this.client = new InternetComputerClient();
+ this.client = new InternetComputerClient(this.http);
}
getDefaultClient(): InternetComputerClient {