Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(trading): otc market banner #17202

Merged
merged 1 commit into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions packages/suite/src/support/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = (
Expand Down Expand Up @@ -158,6 +159,7 @@ export const TradingFormOffer = () => {
>
<Translation id={tradingGetSectionActionLabel(type)} />
</Button>
{(type === 'buy' || type === 'sell') && <TradingFormOfferOTC />}
</Column>
);
};
Original file line number Diff line number Diff line change
@@ -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<TradingTradeBuySellType>();
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<TradingOTC | null>(null);
const apiKey = invityAPI.getCurrentApiKey();

useEffect(() => {
if (!apiKey) return;

const getOtcData = async () => {
const otcData = await invityAPI.getOTCData();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetching the data every time user opens buy/sell form seems a bit like a waste of resources to me

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I will add it on loading trading section in new refactored trading part? What do you think? 😁


if (!otcData) return;

setOtcData(otcData);
};

getOtcData();
}, [apiKey]);

const isCurrencyAllowed = otcData?.allowedCurrencies?.includes(
currencySelect as FiatCurrencyCode,
);

if (
!otcData ||
amountInCrypto ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would make sense to convert it? so that you can also show it if user wants to buy 1 btc?

!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 (
<Banner variant="info">
<Text margin={{ bottom: spacings.xxs }}>
<Translation
id={
context.type === 'buy'
? 'TR_TRADING_OTC_INFO_BUY'
: 'TR_TRADING_OTC_INFO_SELL'
}
values={{
minimumFiat: localizeNumber(otcData.minimumFiat, locale),
fiatSymbol: currencySelect.toUpperCase(),
}}
/>{' '}
<TrezorLink href={apiUrl.toString()} target="_blank" typographyStyle="hint">
<Translation
id={
context.type === 'buy'
? 'TR_TRADING_OTC_LINK_BUY'
: 'TR_TRADING_OTC_LINK_SELL'
}
/>
</TrezorLink>
</Text>
</Banner>
);
};
16 changes: 16 additions & 0 deletions suite-common/trading/src/invityAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import type {
import type {
InvityServerEnvironment,
InvityServers,
TradingOTC,
TradingPaymentMethodType,
TradingTradeType,
TradingType,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -433,6 +437,18 @@ class InvityAPI {
return { error: error.toString() };
}
};

getOTCData = async (): Promise<TradingOTC | undefined> => {
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();
8 changes: 8 additions & 0 deletions suite-common/trading/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,11 @@ export type TradingTransaction =
| TradingTransactionBuy
| TradingTransactionSell
| TradingTransactionExchange;

export type TradingOTC = {
idWidget: string;
idOtcUser: string;
apiUrl: string;
minimumFiat: string;
allowedCurrencies: FiatCurrencyCode[];
};
Loading