diff --git a/packages/suite/src/support/messages.ts b/packages/suite/src/support/messages.ts
index 6116d280951..245d8ef59fd 100644
--- a/packages/suite/src/support/messages.ts
+++ b/packages/suite/src/support/messages.ts
@@ -1416,6 +1416,24 @@ export default defineMessages({
defaultMessage: 'View details',
id: 'TR_TRADING_VIEW_DETAILS',
},
+ TR_TRADING_OTC_INFO_BUY: {
+ defaultMessage:
+ 'For purchases over {minimumFiat} {fiatSymbol}, consider using our OTC partner:',
+ id: 'TR_TRADING_OTC_INFO_BUY',
+ },
+ TR_TRADING_OTC_INFO_SELL: {
+ defaultMessage:
+ 'For sales over {minimumFiat} {fiatSymbol}, consider using our OTC partner:',
+ id: 'TR_TRADING_OTC_INFO_SELL',
+ },
+ TR_TRADING_OTC_LINK_BUY: {
+ defaultMessage: 'Buy with Mercuryo',
+ id: 'TR_TRADING_OTC_LINK_BUY',
+ },
+ TR_TRADING_OTC_LINK_SELL: {
+ defaultMessage: 'Sell with Mercuryo',
+ id: 'TR_TRADING_OTC_LINK_SELL',
+ },
TR_ADDRESS_MODAL_CLIPBOARD: {
defaultMessage: 'Copy address',
id: 'TR_ADDRESS_MODAL_CLIPBOARD',
diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx
index a420556b1b9..8d613efaa82 100644
--- a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx
+++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOffer.tsx
@@ -28,6 +28,7 @@ import {
import { TradingFormOfferCryptoAmount } from 'src/views/wallet/trading/common/TradingForm/TradingFormOfferCryptoAmount';
import { TradingFormOfferFiatAmount } from 'src/views/wallet/trading/common/TradingForm/TradingFormOfferFiatAmount';
import { TradingFormOfferItem } from 'src/views/wallet/trading/common/TradingForm/TradingFormOfferItem';
+import { TradingFormOfferOTC } from 'src/views/wallet/trading/common/TradingForm/TradingFormOfferOTC';
import { TradingFormOffersSwitcher } from 'src/views/wallet/trading/common/TradingForm/TradingFormOffersSwitcher';
const getSelectedQuote = (
@@ -158,6 +159,7 @@ export const TradingFormOffer = () => {
>
+ {(type === 'buy' || type === 'sell') && }
);
};
diff --git a/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOfferOTC.tsx b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOfferOTC.tsx
new file mode 100644
index 00000000000..4a398758da1
--- /dev/null
+++ b/packages/suite/src/views/wallet/trading/common/TradingForm/TradingFormOfferOTC.tsx
@@ -0,0 +1,95 @@
+import { useEffect, useState } from 'react';
+
+import { FiatCurrencyCode } from 'invity-api';
+
+import { TradingOTC, TradingTradeBuySellType, invityAPI } from '@suite-common/trading';
+import { localizeNumber } from '@suite-common/wallet-utils';
+import { Banner, Text } from '@trezor/components';
+import { spacings } from '@trezor/theme';
+
+import { Translation, TrezorLink } from 'src/components/suite';
+import { useSelector } from 'src/hooks/suite';
+import { useTradingFormContext } from 'src/hooks/wallet/trading/form/useTradingCommonForm';
+import { selectLanguage } from 'src/reducers/suite/suiteReducer';
+import { isTradingBuyContext } from 'src/utils/wallet/trading/tradingTypingUtils';
+
+export const TradingFormOfferOTC = () => {
+ const context = useTradingFormContext();
+ const locale = useSelector(selectLanguage);
+
+ const { amountInCrypto } = context.getValues();
+ const fiatAmount = isTradingBuyContext(context)
+ ? context.getValues().fiatInput
+ : context.getValues().outputs[0].fiat;
+ const currencySelect = isTradingBuyContext(context)
+ ? context.getValues().currencySelect.value
+ : context.getValues().outputs[0].currency.value;
+ const [otcData, setOtcData] = useState(null);
+ const apiKey = invityAPI.getCurrentApiKey();
+
+ useEffect(() => {
+ if (!apiKey) return;
+
+ const getOtcData = async () => {
+ const otcData = await invityAPI.getOTCData();
+
+ if (!otcData) return;
+
+ setOtcData(otcData);
+ };
+
+ getOtcData();
+ }, [apiKey]);
+
+ const isCurrencyAllowed = otcData?.allowedCurrencies?.includes(
+ currencySelect as FiatCurrencyCode,
+ );
+
+ if (
+ !otcData ||
+ amountInCrypto ||
+ !isCurrencyAllowed ||
+ !fiatAmount ||
+ Number(fiatAmount) <= Number(otcData.minimumFiat)
+ ) {
+ return null;
+ }
+
+ const apiUrl = new URL(otcData.apiUrl);
+ const params = new URLSearchParams({
+ widget_id: otcData.idWidget,
+ otc_id: otcData.idOtcUser,
+ fiat_amount: fiatAmount,
+ fiat_currency: currencySelect,
+ type: context.type,
+ });
+
+ apiUrl.search = params.toString();
+
+ return (
+
+
+ {' '}
+
+
+
+
+
+ );
+};
diff --git a/suite-common/trading/src/invityAPI.ts b/suite-common/trading/src/invityAPI.ts
index 91a2af43177..4978bf73bb9 100644
--- a/suite-common/trading/src/invityAPI.ts
+++ b/suite-common/trading/src/invityAPI.ts
@@ -27,6 +27,7 @@ import type {
import type {
InvityServerEnvironment,
InvityServers,
+ TradingOTC,
TradingPaymentMethodType,
TradingTradeType,
TradingType,
@@ -84,6 +85,9 @@ class InvityAPI {
private readonly SELL_FIAT_CONFIRM = '/api/v3/sell/fiat/confirm';
private readonly SELL_FIAT_WATCH_TRADE = '/api/v3/sell/fiat/watch/{{counter}}';
+ // otc service
+ private readonly OTC_INFO = '/api/v1/otc';
+
private static accountDescriptor: string;
private static apiKey: string;
@@ -433,6 +437,18 @@ class InvityAPI {
return { error: error.toString() };
}
};
+
+ getOTCData = async (): Promise => {
+ try {
+ const response = await this.request(this.OTC_INFO, {}, 'GET');
+
+ if (response) {
+ return response;
+ }
+ } catch (error) {
+ console.error('[getOTCData]', error);
+ }
+ };
}
export const invityAPI = new InvityAPI();
diff --git a/suite-common/trading/src/types.ts b/suite-common/trading/src/types.ts
index bd1f905655e..5f8a43a1280 100644
--- a/suite-common/trading/src/types.ts
+++ b/suite-common/trading/src/types.ts
@@ -86,3 +86,11 @@ export type TradingTransaction =
| TradingTransactionBuy
| TradingTransactionSell
| TradingTransactionExchange;
+
+export type TradingOTC = {
+ idWidget: string;
+ idOtcUser: string;
+ apiUrl: string;
+ minimumFiat: string;
+ allowedCurrencies: FiatCurrencyCode[];
+};