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 { 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}]" },