Compare commits

..
Author SHA1 Message Date
Rui db3e7331b8 Fix path to chainLogo 2023-12-06 13:35:08 -08:00
Jared Vu 9979bc8a16 Prevent Spread row from moving when orderbook updates (#186)
* Add dummy rows to ensure even row count

* Reverse bids for horizontal layout

* disable pointer-events for dummy rows
2023-12-06 10:26:47 -08:00
aleka a958a59ea2 Display more markets 12/06 (#178)
* add more markets 12/06
2023-12-06 11:38:49 -05:00
Jared Vu 2bf9e66da5 Fix inputs for comma locales (#171)
* Fix for comma decimal separator locales

* Accept tickSizeDecimals 0

* Use FloatValue as value for Size Input

* Use formattedValue to handle prefix/suffix

* Use MustBigNumber.toString()
2023-12-05 16:22:41 -08:00
Bill 2980144e40 Enable CCTP via TestFlags (#185)
* Enable CCTP via Env Var

* Use test flags instead
2023-12-05 14:48:47 -08:00
aleka 5387fe8632 Display more markets 12/05 (#177)
* add more markets 12/05
2023-12-05 09:52:12 -05:00
Bill 1132ea3bbd Do not init nobleClient if nobleValidatorUrl does not exist (#184) 2023-12-04 11:19:29 -08:00
aleka 9188e08a4f prevent 1password fill (#183) 2023-12-04 11:06:49 -05:00
aleka 941ef7f9dd Display more markets 12/04 (#176)
* add more markets 11/30

* add more markets 12/01

* add more markets 12/04
2023-12-04 10:53:25 -05:00
14 changed files with 193 additions and 162 deletions
+15 -15
View File
@@ -42,7 +42,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -116,7 +116,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -189,7 +189,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -262,7 +262,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -335,7 +335,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -410,7 +410,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -483,7 +483,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet", "dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -558,7 +558,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -637,7 +637,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -713,7 +713,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -789,7 +789,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -865,7 +865,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -941,7 +941,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -1008,7 +1008,7 @@
"ethereumChainId": "5", "ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4", "dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api", "squidIntegratorId": "dYdX-api",
"isMainNet": false, "isMainNet": false,
"tokens": { "tokens": {
@@ -1084,7 +1084,7 @@
"ethereumChainId": "1", "ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]", "dydxChainId": "[mainnet chain id]",
"chainName": "dYdX Chain", "chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png", "chainLogo": "/dydx-chain.png",
"squidIntegratorId": "[mainnet squid integrator id]", "squidIntegratorId": "[mainnet squid integrator id]",
"isMainNet": true, "isMainNet": true,
"tokens": { "tokens": {
-2
View File
@@ -22,7 +22,6 @@ import { NotificationsProvider } from '@/hooks/useNotifications';
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications'; import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
import { RestrictionProvider } from '@/hooks/useRestrictions'; import { RestrictionProvider } from '@/hooks/useRestrictions';
import { SubaccountProvider } from '@/hooks/useSubaccount'; import { SubaccountProvider } from '@/hooks/useSubaccount';
import { TestFlagsProvider } from '@/hooks/useTestFlags';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute'; import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
@@ -123,7 +122,6 @@ const providers = [
wrapProvider(QueryClientProvider, { client: queryClient }), wrapProvider(QueryClientProvider, { client: queryClient }),
wrapProvider(GrazProvider), wrapProvider(GrazProvider),
wrapProvider(WagmiConfig, { config }), wrapProvider(WagmiConfig, { config }),
wrapProvider(TestFlagsProvider),
wrapProvider(LocaleProvider), wrapProvider(LocaleProvider),
wrapProvider(RestrictionProvider), wrapProvider(RestrictionProvider),
wrapProvider(DydxProvider), wrapProvider(DydxProvider),
+30 -20
View File
@@ -10,7 +10,7 @@ import {
USD_DECIMALS, USD_DECIMALS,
} from '@/constants/numbers'; } from '@/constants/numbers';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers'; import { BIG_NUMBERS } from '@/lib/numbers';
import { useLocaleSeparators } from '@/hooks'; import { useLocaleSeparators } from '@/hooks';
export enum InputType { export enum InputType {
@@ -29,31 +29,38 @@ type StyleProps = {
type ElementProps = { type ElementProps = {
type?: InputType; type?: InputType;
value?: string | number | null; value?: string | number | null;
allowNegative?: boolean;
decimals?: number;
disabled?: boolean; disabled?: boolean;
id?: string; id?: string;
max?: number;
onBlur?: () => void; onBlur?: () => void;
onChange?:
| Dispatch<SetStateAction<string>>
| React.ReactEventHandler<HTMLInputElement>
| ((values: NumberFormatValues, e: SourceInfo) => void);
onFocus?: () => void; onFocus?: () => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
placeholder?: string; placeholder?: string;
}; };
export type InputProps = ElementProps & StyleProps; type ConditionalProps =
| {
allowNegative?: boolean;
decimals?: number;
max?: number;
onChange?: (values: NumberFormatValues, e: SourceInfo) => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
}
| {
allowNegative?: never;
decimals?: never;
max?: never;
onChange?: Dispatch<SetStateAction<string>> | React.ReactEventHandler<HTMLInputElement>;
onInput?: (e: SyntheticInputEvent) => void;
};
export type InputProps = ElementProps & StyleProps & ConditionalProps;
export const Input = forwardRef<HTMLInputElement, InputProps>( export const Input = forwardRef<HTMLInputElement, InputProps>(
( (
@@ -129,6 +136,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
placeholder={placeholder} placeholder={placeholder}
value={value} value={value}
// Other // Other
data-1p-ignore // prevent 1Password fill
{...otherProps} {...otherProps}
/> />
) : ( ) : (
@@ -137,6 +145,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref={ref as React.Ref<typeof NumericFormat<unknown>>} ref={ref as React.Ref<typeof NumericFormat<unknown>>}
id={id} id={id}
// NumericFormat // NumericFormat
valueIsNumericString
allowNegative={allowNegative} allowNegative={allowNegative}
decimalScale={decimals} decimalScale={decimals}
decimalSeparator={LOCALE_DECIMAL_SEPARATOR} decimalSeparator={LOCALE_DECIMAL_SEPARATOR}
@@ -160,7 +169,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
? undefined ? undefined
: Number(formattedValue.replace(',', '.')); : Number(formattedValue.replace(',', '.'));
onInput?.({ value, floatValue, formattedValue }); onInput?.({ value, floatValue, formattedValue, ...e });
}} }}
// Native // Native
disabled={disabled} disabled={disabled}
@@ -168,6 +177,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
value={formattedValue} value={formattedValue}
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
data-1p-ignore // prevent 1Password fill
{...otherProps} {...otherProps}
/> />
)} )}
+15
View File
@@ -46,4 +46,19 @@ export const MARKETS_TO_DISPLAY = [
'SHIB-USD', 'SHIB-USD',
'COMP-USD', 'COMP-USD',
'LDO-USD', 'LDO-USD',
'NEAR-USD',
'APT-USD',
'SUI-USD',
'DOT-USD',
'ETC-USD',
'ARB-USD',
'CRV-USD',
'BLUR-USD',
'FIL-USD',
'XLM-USD',
'PEPE-USD',
'WLD-USD',
'SEI-USD',
'LTC-USD',
'BCH-USD',
]; ];
+1 -1
View File
@@ -8,7 +8,7 @@ import {
MARKETS_TO_DISPLAY, MARKETS_TO_DISPLAY,
} from '@/constants/markets'; } from '@/constants/markets';
import { testFlags } from '@/hooks/useTestFlags'; import { testFlags } from '@/lib/testFlags';
import { getAssets } from '@/state/assetsSelectors'; import { getAssets } from '@/state/assetsSelectors';
import { getPerpetualMarkets } from '@/state/perpetualsSelectors'; import { getPerpetualMarkets } from '@/state/perpetualsSelectors';
-53
View File
@@ -1,53 +0,0 @@
import { createContext, useContext, useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import queryString from 'query-string';
class TestFlags {
public queryParams: queryString.ParsedQuery<string>;
constructor() {
this.queryParams = {};
}
setQueryParams(flags: queryString.ParsedQuery<string>) {
this.queryParams = flags;
}
get displayInitializingMarkets() {
return !!this.queryParams.displayInitializingMarkets;
}
/**
* @description We are temporarily displaying a subset of ACTIVE markets, this flag will allow you to see all ACTIVE markets
*/
get displayAllMarkets() {
return !!this.queryParams.displayAllMarkets;
}
}
export const testFlags = new TestFlags();
const useTestFlagsContext = () => {
const [queryParams, setQueryParams] = useState<queryString.ParsedQuery<string>>({});
const location = useLocation();
useEffect(() => {
const parsedQueryParams = queryString.parse(location.search.substring(-1));
testFlags.setQueryParams(parsedQueryParams);
setQueryParams(parsedQueryParams);
}, []);
return {
...queryParams,
};
};
type TestFlagsContextType = ReturnType<typeof useTestFlagsContext>;
const TestFlagsContext = createContext<TestFlagsContextType>({} as TestFlagsContextType);
TestFlagsContext.displayName = 'TestFlags';
export const TestFlagsProvider = ({ ...props }) => (
<TestFlagsContext.Provider value={useTestFlagsContext()} {...props} />
);
export const useTestFlags = () => useContext(TestFlagsContext);
+3 -6
View File
@@ -11,22 +11,19 @@ export const useTradeFormInputs = () => {
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues; const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
useEffect(() => { useEffect(() => {
const floatValue = parseFloat(triggerPriceInput);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
value: floatValue, value: triggerPriceInput,
field: TradeInputField.triggerPrice, field: TradeInputField.triggerPrice,
}); });
}, [triggerPriceInput]); }, [triggerPriceInput]);
useEffect(() => { useEffect(() => {
const floatValue = parseFloat(limitPriceInput); abacusStateManager.setTradeValue({ value: limitPriceInput, field: TradeInputField.limitPrice });
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
}, [limitPriceInput]); }, [limitPriceInput]);
useEffect(() => { useEffect(() => {
const floatValue = parseFloat(trailingPercentInput);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
value: floatValue, value: trailingPercentInput,
field: TradeInputField.trailingPercent, field: TradeInputField.trailingPercent,
}); });
}, [trailingPercentInput]); }, [trailingPercentInput]);
+5 -14
View File
@@ -33,6 +33,8 @@ import type { RootStore } from '@/state/_store';
import { setTradeFormInputs } from '@/state/inputs'; import { setTradeFormInputs } from '@/state/inputs';
import { getInputTradeOptions, getTransferInputs } from '@/state/inputsSelectors'; import { getInputTradeOptions, getTransferInputs } from '@/state/inputsSelectors';
import { testFlags } from '@/lib/testFlags';
import AbacusRest from './rest'; import AbacusRest from './rest';
import AbacusAnalytics from './analytics'; import AbacusAnalytics from './analytics';
import AbacusWebsocket from './websocket'; import AbacusWebsocket from './websocket';
@@ -82,7 +84,8 @@ class AbacusStateManager {
); );
const appConfigs = AbacusAppConfig.Companion.forWeb; const appConfigs = AbacusAppConfig.Companion.forWeb;
if (!isMainnet) appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2DepositOnly; if (!isMainnet || testFlags.withCCTP)
appConfigs.squidVersion = AbacusAppConfig.SquidVersion.V2DepositOnly;
this.stateManager = new AsyncAbacusStateManager( this.stateManager = new AsyncAbacusStateManager(
'', '',
@@ -187,7 +190,7 @@ class AbacusStateManager {
if (nobleWallet) { if (nobleWallet) {
this.chainTransactions.setNobleWallet(nobleWallet); this.chainTransactions.setNobleWallet(nobleWallet);
} }
} };
setTransfersSourceAddress = (evmAddress: string) => { setTransfersSourceAddress = (evmAddress: string) => {
this.stateManager.sourceAddress = evmAddress; this.stateManager.sourceAddress = evmAddress;
@@ -232,18 +235,6 @@ class AbacusStateManager {
this.abacusFormatter.setLocaleSeparators({ group, decimal }); this.abacusFormatter.setLocaleSeparators({ group, decimal });
}; };
setTransferStatus = ({
hash,
fromChainId,
toChainId,
}: {
hash: string;
fromChainId?: string;
toChainId?: string;
}) => {
this.stateManager.transferStatus(hash, fromChainId, toChainId);
};
// ------ Transactions ------ // // ------ Transactions ------ //
placeOrder = ( placeOrder = (
+1 -1
View File
@@ -10,7 +10,7 @@ import {
} from '@/constants/websocket'; } from '@/constants/websocket';
import { lastSuccessfulWebsocketRequestByOrigin } from '@/hooks/useAnalytics'; import { lastSuccessfulWebsocketRequestByOrigin } from '@/hooks/useAnalytics';
import { testFlags } from '@/hooks/useTestFlags'; import { testFlags } from '@/lib/testFlags';
import { subscriptionsByChannelId } from '@/lib/tradingView/dydxfeed/cache'; import { subscriptionsByChannelId } from '@/lib/tradingView/dydxfeed/cache';
import { mapCandle } from '@/lib/tradingView/utils'; import { mapCandle } from '@/lib/tradingView/utils';
+31
View File
@@ -0,0 +1,31 @@
class TestFlags {
public queryParams: { [key: string]: string };
constructor() {
this.queryParams = {};
const hash = window.location.hash;
const queryIndex = hash.indexOf('?');
if (queryIndex === -1) return
const queryParamsString = hash.substring(queryIndex + 1);
const params = new URLSearchParams(queryParamsString);
for (const [key, value] of params) {
this.queryParams[key] = value;
}
}
get displayInitializingMarkets() {
return !!this.queryParams.displayInitializingMarkets;
}
get displayAllMarkets() {
return !!this.queryParams.displayAllMarkets;
}
get withCCTP() {
return !!this.queryParams.withCCTP;
}
}
export const testFlags = new TestFlags();
+1 -1
View File
@@ -4,7 +4,7 @@ import { shallowEqual, useSelector } from 'react-redux';
import { STRING_KEYS } from '@/constants/localization'; import { STRING_KEYS } from '@/constants/localization';
import { MARKETS_TO_DISPLAY } from '@/constants/markets'; import { MARKETS_TO_DISPLAY } from '@/constants/markets';
import { useBreakpoints, useStringGetter } from '@/hooks'; import { useBreakpoints, useStringGetter } from '@/hooks';
import { testFlags } from '@/hooks/useTestFlags'; import { testFlags } from '@/lib/testFlags';
import { breakpoints } from '@/styles'; import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins'; import { layoutMixins } from '@/styles/layoutMixins';
+2 -2
View File
@@ -206,7 +206,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ triggerPriceInput: value })); dispatch(setTradeFormInputs({ triggerPriceInput: value }));
}, },
value: triggerPriceInput ?? '', value: triggerPriceInput ?? '',
decimals: tickSizeDecimals || USD_DECIMALS, decimals: tickSizeDecimals ?? USD_DECIMALS,
}); });
} }
@@ -226,7 +226,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ limitPriceInput: value })); dispatch(setTradeFormInputs({ limitPriceInput: value }));
}, },
value: limitPriceInput, value: limitPriceInput,
decimals: tickSizeDecimals || USD_DECIMALS, decimals: tickSizeDecimals ?? USD_DECIMALS,
}); });
} }
+20 -8
View File
@@ -49,7 +49,7 @@ export const TradeSizeInputs = () => {
useSelector(getCurrentMarketConfig, shallowEqual) || {}; useSelector(getCurrentMarketConfig, shallowEqual) || {};
const { size, usdcSize, leverage, input: lastEditedInput } = inputTradeSizeData || {}; const { size, usdcSize, leverage, input: lastEditedInput } = inputTradeSizeData || {};
const { needsLeverage } = currentTradeInputOptions || {}; const { needsLeverage } = currentTradeInputOptions || {};
const decimals = stepSizeDecimals || TOKEN_DECIMALS; const decimals = stepSizeDecimals ?? TOKEN_DECIMALS;
const { amountInput, usdAmountInput, leverageInput } = useSelector( const { amountInput, usdAmountInput, leverageInput } = useSelector(
getTradeFormInputs, getTradeFormInputs,
@@ -70,22 +70,34 @@ export const TradeSizeInputs = () => {
} }
}, [size, usdcSize, leverage, lastEditedInput]); }, [size, usdcSize, leverage, lastEditedInput]);
const onSizeInput = ({ value, floatValue }: { value: string; floatValue?: number }) => { const onSizeInput = ({
dispatch(setTradeFormInputs({ amountInput: value })); floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ amountInput: MustBigNumber(floatValue).toString() }));
const newAmount = MustBigNumber(floatValue).toFixed(decimals); const newAmount = MustBigNumber(floatValue).toFixed(decimals);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
value: value === '' || newAmount === 'NaN' ? null : newAmount, value: formattedValue === '' || newAmount === 'NaN' ? null : newAmount,
field: TradeInputField.size, field: TradeInputField.size,
}); });
}; };
const onUSDCInput = ({ value, floatValue }: { value: string; floatValue?: number }) => { const onUSDCInput = ({
dispatch(setTradeFormInputs({ usdAmountInput: value })); floatValue,
const newUsdcAmount = MustBigNumber(floatValue).toFixed(); formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ usdAmountInput: MustBigNumber(floatValue).toString() }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS);
abacusStateManager.setTradeValue({ abacusStateManager.setTradeValue({
value: value === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount, value: formattedValue === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
field: TradeInputField.usdcSize, field: TradeInputField.usdcSize,
}); });
}; };
+69 -39
View File
@@ -42,7 +42,11 @@ type StyleProps = {
className?: string; className?: string;
}; };
type RowData = OrderbookLine & { side: 'bid' | 'ask'; mine?: number }; type RowData = Pick<OrderbookLine, 'depth' | 'offset' | 'price' | 'size'> & {
side: 'bid' | 'ask';
mine?: number;
key: string;
};
const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => { const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number }) => {
const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual); const orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
@@ -53,8 +57,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
return useMemo(() => { return useMemo(() => {
const asks = (orderbook?.asks?.toArray() ?? []) const asks = (orderbook?.asks?.toArray() ?? [])
.map( .map(
(row: OrderbookLine) => (row: OrderbookLine, idx: number) =>
({ ({
key: `ask-${idx}`,
side: 'ask', side: 'ask',
mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size, mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size,
...row, ...row,
@@ -64,8 +69,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
const bids = (orderbook?.bids?.toArray() ?? []) const bids = (orderbook?.bids?.toArray() ?? [])
.map( .map(
(row: OrderbookLine) => (row: OrderbookLine, idx: number) =>
({ ({
key: `bid-${idx}`,
side: 'bid', side: 'bid',
mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size, mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size,
...row, ...row,
@@ -97,15 +103,44 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
} }
const spread = const spread =
asks[0] && bids[0] ? MustBigNumber(asks[0]?.price ?? 0).minus(bids[0]?.price ?? 0) : null; asks[0]?.price && bids[0]?.price ? MustBigNumber(asks[0].price).minus(bids[0].price) : null;
const spreadPercent = orderbook?.spreadPercent; const spreadPercent = orderbook?.spreadPercent;
const histogramRange = Math.max( const histogramRange = Math.max(
Number(bids[bids.length - 1]?.depth), isNaN(Number(bids[bids.length - 1]?.depth)) ? 0 : Number(bids[bids.length - 1]?.depth),
Number(asks[asks.length - 1]?.depth) isNaN(Number(asks[asks.length - 1]?.depth)) ? 0 : Number(asks[asks.length - 1]?.depth)
); );
// Ensure asks and bids are of length maxRowsPerSide by adding empty rows.
let idx = asks.length - 1;
while (asks.length < maxRowsPerSide) {
idx += 1;
asks.push({
key: `ask-${idx}`,
side: 'ask',
size: 0,
price: 0,
offset: 0,
depth: 0,
});
}
idx = bids.length - 1;
while (bids.length < maxRowsPerSide) {
idx += 1;
bids.push({
key: `bid-${idx}`,
side: 'bid',
size: 0,
price: 0,
offset: 0,
depth: 0,
});
}
return { asks, bids, spread, spreadPercent, histogramRange, hasOrderbook: !!orderbook }; return { asks, bids, spread, spreadPercent, histogramRange, hasOrderbook: !!orderbook };
}, [orderbook, openOrdersBySideAndPrice]); }, [orderbook, openOrdersBySideAndPrice]);
}; };
@@ -142,31 +177,33 @@ const OrderbookTable = ({
getCellValue: (row: RowData) => row.size, getCellValue: (row: RowData) => row.size,
label: stringGetter({ key: STRING_KEYS.ORDERBOOK_ORDER_SIZE }), label: stringGetter({ key: STRING_KEYS.ORDERBOOK_ORDER_SIZE }),
tag: symbol, tag: symbol,
renderCell: (row: RowData) => ( renderCell: (row: RowData) =>
<Styled.HistogramOutput row.size > 0 && (
highlightText <Styled.HistogramOutput
type={OutputType.Asset} highlightText
value={row.size} type={OutputType.Asset}
fractionDigits={stepSizeDecimals} value={row.size}
histogramSide={histogramSide === 'left' && 'left'} fractionDigits={stepSizeDecimals}
useGrouping={false} histogramSide={histogramSide === 'left' && 'left'}
/> useGrouping={false}
), />
),
}, },
{ {
columnKey: 'price', columnKey: 'price',
getCellValue: (row: RowData) => row.price, getCellValue: (row: RowData) => row.price,
label: stringGetter({ key: STRING_KEYS.PRICE }), label: stringGetter({ key: STRING_KEYS.PRICE }),
tag: 'USD', tag: 'USD',
renderCell: (row: RowData) => ( renderCell: (row: RowData) =>
<OrderbookTradesOutput row.price > 0 && (
highlightText <OrderbookTradesOutput
type={OutputType.Number} highlightText
value={row.price} type={OutputType.Number}
fractionDigits={tickSizeDecimals} value={row.price}
useGrouping={false} fractionDigits={tickSizeDecimals}
/> useGrouping={false}
), />
),
}, },
{ {
columnKey: 'subaccount-orders', columnKey: 'subaccount-orders',
@@ -194,12 +231,13 @@ const OrderbookTable = ({
label="Orderbook" label="Orderbook"
data={data} data={data}
columns={columns} columns={columns}
getRowKey={(row: RowData) => `${row.side}-${row.price}`} getRowKey={(row: RowData) => row.key}
getRowAttributes={(row: RowData) => ({ getRowAttributes={(row: RowData) => ({
'data-side': row.side, 'data-side': row.side,
style: { style: {
'--histogram-bucket-size': row.size, '--histogram-bucket-size': row.size,
'--histogram-bucket-depth': row.depth, '--histogram-bucket-depth': row.depth,
'--tr-pointerEvents': row.price ? 'auto' : 'none',
}, },
})} })}
onRowAction={onRowAction} onRowAction={onRowAction}
@@ -238,19 +276,10 @@ export const Orderbook = ({
maxRowsPerSide, maxRowsPerSide,
}); });
const [showRowsPerSide, setShowRowsPerSide] = useState(0);
// Make rows visible one by one so avoid jumps in initial scroll position
useEffect(() => {
if (showRowsPerSide < maxRowsPerSide) {
setShowRowsPerSide((showRows) => showRows + 1);
}
}, [showRowsPerSide]);
const data = useMemo( const data = useMemo(
() => () =>
[ [
...bids.slice(0, showRowsPerSide).reverse(), ...bids.reverse(),
{ {
key: 'spread', key: 'spread',
slotCustomRow: (props) => ( slotCustomRow: (props) => (
@@ -267,9 +296,9 @@ export const Orderbook = ({
</Styled.SpreadTableRow> </Styled.SpreadTableRow>
), ),
} as CustomRowConfig, } as CustomRowConfig,
...asks.slice(0, showRowsPerSide), ...asks,
].reverse(), ].reverse(),
[asks, bids, spread, spreadPercent, showRowsPerSide, isTablet] [asks, bids, spread, spreadPercent, isTablet]
); );
const onRowAction = useCallback( const onRowAction = useCallback(
@@ -325,7 +354,7 @@ export const Orderbook = ({
</Styled.Header> </Styled.Header>
<Styled.SplitOrderbook> <Styled.SplitOrderbook>
<OrderbookTable data={asks} histogramSide="right" {...orderbookTableProps} /> <OrderbookTable data={asks} histogramSide="right" {...orderbookTableProps} />
<OrderbookTable data={bids} histogramSide="left" {...orderbookTableProps} /> <OrderbookTable data={bids.reverse()} histogramSide="left" {...orderbookTableProps} />
</Styled.SplitOrderbook> </Styled.SplitOrderbook>
</Styled.HorizontalLayout> </Styled.HorizontalLayout>
); );
@@ -495,6 +524,7 @@ Styled.OrderbookTable = styled(OrderbookTradesTable)<StyleProps>`
tr { tr {
--histogram-bucket-depth: 0; --histogram-bucket-depth: 0;
pointer-events: var(--tr-pointerEvents);
&[data-side='bid'] { &[data-side='bid'] {
--accent-color: var(--color-positive); --accent-color: var(--color-positive);