Compare commits

...
Author SHA1 Message Date
Bill He ce5b6d92d5 Update noble tesnet rpc 2023-12-12 11:29:55 -05:00
Jared Vu 4a044da38f Size inputs (#191)
* use formattedValue to update redux state

* Separate updateLeverage and onLeverageInput

* Remove console.log
2023-12-12 07:11:51 -08:00
Jared Vu fbbfbc6580 Bump v4-abacus and v4-localization (#189) 2023-12-08 15:41:07 -08:00
Rui 2714c2b084 Fix path to chainLogo (#187) 2023-12-07 13:03:44 -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
18 changed files with 246 additions and 213 deletions
+2 -2
View File
@@ -39,9 +39,9 @@
"@cosmjs/proto-signing": "^0.31.0",
"@cosmjs/stargate": "^0.31.0",
"@cosmjs/tendermint-rpc": "^0.31.0",
"@dydxprotocol/v4-abacus": "^1.1.4",
"@dydxprotocol/v4-abacus": "^1.1.16",
"@dydxprotocol/v4-client-js": "^1.0.6",
"@dydxprotocol/v4-localization": "^1.0.17",
"@dydxprotocol/v4-localization": "^1.0.18",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-checkbox": "^1.0.4",
+8 -8
View File
@@ -27,14 +27,14 @@ dependencies:
specifier: ^0.31.0
version: 0.31.0
'@dydxprotocol/v4-abacus':
specifier: ^1.1.4
version: 1.1.4
specifier: ^1.1.16
version: 1.1.16
'@dydxprotocol/v4-client-js':
specifier: ^1.0.6
version: 1.0.6
'@dydxprotocol/v4-localization':
specifier: ^1.0.17
version: 1.0.17
specifier: ^1.0.18
version: 1.0.18
'@ethersproject/providers':
specifier: ^5.7.2
version: 5.7.2
@@ -988,8 +988,8 @@ packages:
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
dev: true
/@dydxprotocol/v4-abacus@1.1.4:
resolution: {integrity: sha512-gml6qheFsPShE9p3FmzFeStYM9ZVhgMq0K0+y12V7pObnMKbumkOIISgCzc47idkah0GMNhiqwi9faD5SjOy/A==}
/@dydxprotocol/v4-abacus@1.1.16:
resolution: {integrity: sha512-R9ROWx70f0a4lHxAPP/iJd9qnLhzL1SG8vIowfNg2VvWLk6f+f1HwsgMhDd07H328RqY016GNrRmnigeyVcz/g==}
dev: false
/@dydxprotocol/v4-client-js@1.0.6:
@@ -1020,8 +1020,8 @@ packages:
- utf-8-validate
dev: false
/@dydxprotocol/v4-localization@1.0.17:
resolution: {integrity: sha512-fybNpi1ono1IdwFQJTszsHbHmGti2BdhTo1nz9KOmKh3tmXdhWObKb64Ve6/6Fjk6h/9+DeZ5ZG4UbYyYMyhCA==}
/@dydxprotocol/v4-localization@1.0.18:
resolution: {integrity: sha512-790ajXfGzqRs7JVA3vISCg0Vh4EEN7SlcI8u04iryPcBp1WwkQ5TFGllF32+YPs1+mgjMRhwrg6jP8zEGMQFyw==}
dev: false
/@dydxprotocol/v4-proto@0.4.1:
+29 -29
View File
@@ -42,7 +42,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -71,7 +71,7 @@
"https://validator.v4dev.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4dev.dydx.exchange"
},
"links": {
@@ -116,7 +116,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -145,7 +145,7 @@
"http://54.92.118.111"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -189,7 +189,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -218,7 +218,7 @@
"http://validator.v4dev4.dydx.exchange"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -262,7 +262,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -291,7 +291,7 @@
"http://18.223.78.50"
],
"0xsquid": "https://testnet.api.0xsquid.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -335,7 +335,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -365,7 +365,7 @@
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -410,7 +410,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -440,7 +440,7 @@
"https://validator.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -483,7 +483,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydxprotocol-testnet",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -513,7 +513,7 @@
"https://validator-uswest1.v4staging.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love"
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/"
},
"links": {
"tos": "https://dydx.exchange/v4-terms",
@@ -558,7 +558,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -591,7 +591,7 @@
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -637,7 +637,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -666,7 +666,7 @@
"https://validator.v4testnet.dydx.exchange"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -713,7 +713,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -742,7 +742,7 @@
"https://dydx-testnet.nodefleet.org"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -789,7 +789,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -818,7 +818,7 @@
"https://test-dydx.kingnodes.com"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -865,7 +865,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -894,7 +894,7 @@
"https://dydx-rpc.liquify.com/api=8878132/dydx"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -941,7 +941,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -970,7 +970,7 @@
"https://dydx-testnet-rpc.polkachu.com/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -1008,7 +1008,7 @@
"ethereumChainId": "5",
"dydxChainId": "dydx-testnet-4",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "dYdX-api",
"isMainNet": false,
"tokens": {
@@ -1037,7 +1037,7 @@
"https://dydx-testnet-full-rpc.public.blastapi.io/"
],
"0xsquid": "https://testnet.api.squidrouter.com",
"nobleValidator": "https://rpc.testnet.noble.strange.love",
"nobleValidator": "https://noble-testnet-rpc.polkachu.com/",
"faucet": "https://faucet.v4testnet.dydx.exchange"
},
"links": {
@@ -1084,7 +1084,7 @@
"ethereumChainId": "1",
"dydxChainId": "[mainnet chain id]",
"chainName": "dYdX Chain",
"chainLogo": "dydx-chain.png",
"chainLogo": "/dydx-chain.png",
"squidIntegratorId": "[mainnet squid integrator id]",
"isMainNet": true,
"tokens": {
-2
View File
@@ -22,7 +22,6 @@ import { NotificationsProvider } from '@/hooks/useNotifications';
import { LocalNotificationsProvider } from '@/hooks/useLocalNotifications';
import { RestrictionProvider } from '@/hooks/useRestrictions';
import { SubaccountProvider } from '@/hooks/useSubaccount';
import { TestFlagsProvider } from '@/hooks/useTestFlags';
import { GuardedMobileRoute } from '@/components/GuardedMobileRoute';
@@ -123,7 +122,6 @@ const providers = [
wrapProvider(QueryClientProvider, { client: queryClient }),
wrapProvider(GrazProvider),
wrapProvider(WagmiConfig, { config }),
wrapProvider(TestFlagsProvider),
wrapProvider(LocaleProvider),
wrapProvider(RestrictionProvider),
wrapProvider(DydxProvider),
+28 -20
View File
@@ -10,7 +10,7 @@ import {
USD_DECIMALS,
} from '@/constants/numbers';
import { BIG_NUMBERS, MustBigNumber } from '@/lib/numbers';
import { BIG_NUMBERS } from '@/lib/numbers';
import { useLocaleSeparators } from '@/hooks';
export enum InputType {
@@ -29,31 +29,38 @@ type StyleProps = {
type ElementProps = {
type?: InputType;
value?: string | number | null;
allowNegative?: boolean;
decimals?: number;
disabled?: boolean;
id?: string;
max?: number;
onBlur?: () => void;
onChange?:
| Dispatch<SetStateAction<string>>
| React.ReactEventHandler<HTMLInputElement>
| ((values: NumberFormatValues, e: SourceInfo) => void);
onFocus?: () => void;
onInput?: ({
value,
floatValue,
formattedValue,
}: {
value: string;
floatValue?: number;
formattedValue: string;
}) => void;
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>(
(
@@ -138,6 +145,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref={ref as React.Ref<typeof NumericFormat<unknown>>}
id={id}
// NumericFormat
valueIsNumericString
allowNegative={allowNegative}
decimalScale={decimals}
decimalSeparator={LOCALE_DECIMAL_SEPARATOR}
@@ -161,7 +169,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
? undefined
: Number(formattedValue.replace(',', '.'));
onInput?.({ value, floatValue, formattedValue });
onInput?.({ value, floatValue, formattedValue, ...e });
}}
// Native
disabled={disabled}
+10
View File
@@ -51,4 +51,14 @@ export const MARKETS_TO_DISPLAY = [
'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,
} from '@/constants/markets';
import { testFlags } from '@/hooks/useTestFlags';
import { testFlags } from '@/lib/testFlags';
import { getAssets } from '@/state/assetsSelectors';
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;
useEffect(() => {
const floatValue = parseFloat(triggerPriceInput);
abacusStateManager.setTradeValue({
value: floatValue,
value: triggerPriceInput,
field: TradeInputField.triggerPrice,
});
}, [triggerPriceInput]);
useEffect(() => {
const floatValue = parseFloat(limitPriceInput);
abacusStateManager.setTradeValue({ value: floatValue, field: TradeInputField.limitPrice });
abacusStateManager.setTradeValue({ value: limitPriceInput, field: TradeInputField.limitPrice });
}, [limitPriceInput]);
useEffect(() => {
const floatValue = parseFloat(trailingPercentInput);
abacusStateManager.setTradeValue({
value: floatValue,
value: trailingPercentInput,
field: TradeInputField.trailingPercent,
});
}, [trailingPercentInput]);
+5 -14
View File
@@ -33,6 +33,8 @@ import type { RootStore } from '@/state/_store';
import { setTradeFormInputs } from '@/state/inputs';
import { getInputTradeOptions, getTransferInputs } from '@/state/inputsSelectors';
import { testFlags } from '@/lib/testFlags';
import AbacusRest from './rest';
import AbacusAnalytics from './analytics';
import AbacusWebsocket from './websocket';
@@ -82,7 +84,8 @@ class AbacusStateManager {
);
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(
'',
@@ -187,7 +190,7 @@ class AbacusStateManager {
if (nobleWallet) {
this.chainTransactions.setNobleWallet(nobleWallet);
}
}
};
setTransfersSourceAddress = (evmAddress: string) => {
this.stateManager.sourceAddress = evmAddress;
@@ -232,18 +235,6 @@ class AbacusStateManager {
this.abacusFormatter.setLocaleSeparators({ group, decimal });
};
setTransferStatus = ({
hash,
fromChainId,
toChainId,
}: {
hash: string;
fromChainId?: string;
toChainId?: string;
}) => {
this.stateManager.transferStatus(hash, fromChainId, toChainId);
};
// ------ Transactions ------ //
placeOrder = (
+1 -1
View File
@@ -10,7 +10,7 @@ import {
} from '@/constants/websocket';
import { lastSuccessfulWebsocketRequestByOrigin } from '@/hooks/useAnalytics';
import { testFlags } from '@/hooks/useTestFlags';
import { testFlags } from '@/lib/testFlags';
import { subscriptionsByChannelId } from '@/lib/tradingView/dydxfeed/cache';
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 { MARKETS_TO_DISPLAY } from '@/constants/markets';
import { useBreakpoints, useStringGetter } from '@/hooks';
import { testFlags } from '@/hooks/useTestFlags';
import { testFlags } from '@/lib/testFlags';
import { breakpoints } from '@/styles';
import { layoutMixins } from '@/styles/layoutMixins';
+2 -2
View File
@@ -206,7 +206,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ triggerPriceInput: value }));
},
value: triggerPriceInput ?? '',
decimals: tickSizeDecimals || USD_DECIMALS,
decimals: tickSizeDecimals ?? USD_DECIMALS,
});
}
@@ -226,7 +226,7 @@ export const TradeForm = ({
dispatch(setTradeFormInputs({ limitPriceInput: value }));
},
value: limitPriceInput,
decimals: tickSizeDecimals || USD_DECIMALS,
decimals: tickSizeDecimals ?? USD_DECIMALS,
});
}
+8 -7
View File
@@ -1,10 +1,10 @@
import { type Dispatch, type SetStateAction, useCallback, useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import styled, { AnyStyledComponent, css } from 'styled-components';
import { Root, Thumb, Track } from '@radix-ui/react-slider';
import _ from 'lodash';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { type Nullable, TradeInputField } from '@/constants/abacus';
import { TradeInputField } from '@/constants/abacus';
import { PositionSide } from '@/constants/trade';
import { MustBigNumber, type BigNumberish } from '@/lib/numbers';
@@ -12,11 +12,11 @@ import abacusStateManager from '@/lib/abacus';
type ElementProps = {
leverage?: BigNumberish | null;
leverageInputValue: Nullable<number>;
leverageInputValue: string;
maxLeverage: BigNumberish | null;
orderSide: OrderSide;
positionSide: PositionSide;
setLeverageInputValue: Dispatch<SetStateAction<Nullable<number>>>;
setLeverageInputValue: (value: string) => void;
};
type StyleProps = { className?: string };
@@ -33,6 +33,7 @@ export const LeverageSlider = ({
const leverageBN = MustBigNumber(leverage);
const maxLeverageBN = MustBigNumber(maxLeverage);
const leverageInputBN = MustBigNumber(leverageInputValue || leverage);
const leverageInputNumber = isNaN(leverageInputBN.toNumber()) ? 0 : leverageInputBN.toNumber();
const sliderConfig = useMemo(
() => ({
@@ -79,12 +80,12 @@ export const LeverageSlider = ({
);
const onSliderDrag = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage);
setLeverageInputValue(`${newLeverage}`);
debouncedSetAbacusLeverage(newLeverage);
};
const onValueCommit = ([newLeverage]: number[]) => {
setLeverageInputValue(newLeverage);
setLeverageInputValue(`${newLeverage}`);
// Ensure Abacus is updated with the latest, committed value
debouncedSetAbacusLeverage.cancel();
@@ -102,7 +103,7 @@ export const LeverageSlider = ({
min={min}
max={max}
step={0.1}
value={[Math.min(Math.max(leverageInputBN.toNumber(), min), max)]}
value={[Math.min(Math.max(leverageInputNumber, min), max)]}
onValueChange={onSliderDrag}
onValueCommit={onValueCommit}
>
@@ -1,9 +1,8 @@
import type { Dispatch, SetStateAction } from 'react';
import { shallowEqual, useSelector } from 'react-redux';
import styled, { AnyStyledComponent } from 'styled-components';
import { OrderSide } from '@dydxprotocol/v4-client-js';
import { TradeInputField, Nullable } from '@/constants/abacus';
import { TradeInputField } from '@/constants/abacus';
import { ButtonShape } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { LEVERAGE_DECIMALS } from '@/constants/numbers';
@@ -62,21 +61,7 @@ export const MarketLeverageInput = ({
const leveragePosition = postOrderLeverage ? newPositionSide : currentPositionSide;
const onLeverageInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
const newLeverage = MustBigNumber(floatValue).toFixed();
if (value === '' || newLeverage === 'NaN' || !floatValue) {
setLeverageInputValue('');
abacusStateManager.setTradeValue({
value: null,
field: TradeInputField.leverage,
});
} else {
updateLeverage(floatValue);
}
};
const updateLeverage = (newLeverage: string | number) => {
const getSignedLeverage = (newLeverage: string | number) => {
const newLeverageBN = MustBigNumber(newLeverage);
const newLeverageSignedBN =
leveragePosition === PositionSide.Short ||
@@ -84,10 +69,33 @@ export const MarketLeverageInput = ({
? newLeverageBN.abs().negated()
: newLeverageBN.abs();
setLeverageInputValue(newLeverageSignedBN.toString());
return newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS);
};
const onLeverageInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
setLeverageInputValue(formattedValue);
const newLeverage = MustBigNumber(floatValue).toFixed();
abacusStateManager.setTradeValue({
value: newLeverageSignedBN.toFixed(LEVERAGE_DECIMALS),
value:
formattedValue === '' || newLeverage === 'NaN' ? null : getSignedLeverage(formattedValue),
field: TradeInputField.leverage,
});
};
const updateLeverage = (newLeverage: string | number) => {
const newLeverageSigned = getSignedLeverage(newLeverage);
setLeverageInputValue(newLeverageSigned);
abacusStateManager.setTradeValue({
value: newLeverageSigned,
field: TradeInputField.leverage,
});
};
@@ -130,7 +138,7 @@ export const MarketLeverageInput = ({
>
<Styled.LeverageSlider
leverage={currentLeverage}
leverageInputValue={leverageInputValue}
leverageInputValue={getSignedLeverage(leverageInputValue)}
maxLeverage={maxLeverage}
orderSide={orderSide}
positionSide={currentPositionSide}
+20 -8
View File
@@ -49,7 +49,7 @@ export const TradeSizeInputs = () => {
useSelector(getCurrentMarketConfig, shallowEqual) || {};
const { size, usdcSize, leverage, input: lastEditedInput } = inputTradeSizeData || {};
const { needsLeverage } = currentTradeInputOptions || {};
const decimals = stepSizeDecimals || TOKEN_DECIMALS;
const decimals = stepSizeDecimals ?? TOKEN_DECIMALS;
const { amountInput, usdAmountInput, leverageInput } = useSelector(
getTradeFormInputs,
@@ -70,22 +70,34 @@ export const TradeSizeInputs = () => {
}
}, [size, usdcSize, leverage, lastEditedInput]);
const onSizeInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ amountInput: value }));
const onSizeInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ amountInput: formattedValue }));
const newAmount = MustBigNumber(floatValue).toFixed(decimals);
abacusStateManager.setTradeValue({
value: value === '' || newAmount === 'NaN' ? null : newAmount,
value: formattedValue === '' || newAmount === 'NaN' ? null : newAmount,
field: TradeInputField.size,
});
};
const onUSDCInput = ({ value, floatValue }: { value: string; floatValue?: number }) => {
dispatch(setTradeFormInputs({ usdAmountInput: value }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed();
const onUSDCInput = ({
floatValue,
formattedValue,
}: {
floatValue?: number;
formattedValue: string;
}) => {
dispatch(setTradeFormInputs({ usdAmountInput: formattedValue }));
const newUsdcAmount = MustBigNumber(floatValue).toFixed(tickSizeDecimals || USD_DECIMALS);
abacusStateManager.setTradeValue({
value: value === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
value: formattedValue === '' || newUsdcAmount === 'NaN' ? null : newUsdcAmount,
field: TradeInputField.usdcSize,
});
};
+69 -39
View File
@@ -42,7 +42,11 @@ type StyleProps = {
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 orderbook = useSelector(getCurrentMarketOrderbook, shallowEqual);
@@ -53,8 +57,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
return useMemo(() => {
const asks = (orderbook?.asks?.toArray() ?? [])
.map(
(row: OrderbookLine) =>
(row: OrderbookLine, idx: number) =>
({
key: `ask-${idx}`,
side: 'ask',
mine: openOrdersBySideAndPrice[OrderSide.SELL]?.[row.price]?.size,
...row,
@@ -64,8 +69,9 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
const bids = (orderbook?.bids?.toArray() ?? [])
.map(
(row: OrderbookLine) =>
(row: OrderbookLine, idx: number) =>
({
key: `bid-${idx}`,
side: 'bid',
mine: openOrdersBySideAndPrice[OrderSide.BUY]?.[row.price]?.size,
...row,
@@ -97,15 +103,44 @@ const useCalculateOrderbookData = ({ maxRowsPerSide }: { maxRowsPerSide: number
}
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 histogramRange = Math.max(
Number(bids[bids.length - 1]?.depth),
Number(asks[asks.length - 1]?.depth)
isNaN(Number(bids[bids.length - 1]?.depth)) ? 0 : Number(bids[bids.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 };
}, [orderbook, openOrdersBySideAndPrice]);
};
@@ -142,31 +177,33 @@ const OrderbookTable = ({
getCellValue: (row: RowData) => row.size,
label: stringGetter({ key: STRING_KEYS.ORDERBOOK_ORDER_SIZE }),
tag: symbol,
renderCell: (row: RowData) => (
<Styled.HistogramOutput
highlightText
type={OutputType.Asset}
value={row.size}
fractionDigits={stepSizeDecimals}
histogramSide={histogramSide === 'left' && 'left'}
useGrouping={false}
/>
),
renderCell: (row: RowData) =>
row.size > 0 && (
<Styled.HistogramOutput
highlightText
type={OutputType.Asset}
value={row.size}
fractionDigits={stepSizeDecimals}
histogramSide={histogramSide === 'left' && 'left'}
useGrouping={false}
/>
),
},
{
columnKey: 'price',
getCellValue: (row: RowData) => row.price,
label: stringGetter({ key: STRING_KEYS.PRICE }),
tag: 'USD',
renderCell: (row: RowData) => (
<OrderbookTradesOutput
highlightText
type={OutputType.Number}
value={row.price}
fractionDigits={tickSizeDecimals}
useGrouping={false}
/>
),
renderCell: (row: RowData) =>
row.price > 0 && (
<OrderbookTradesOutput
highlightText
type={OutputType.Number}
value={row.price}
fractionDigits={tickSizeDecimals}
useGrouping={false}
/>
),
},
{
columnKey: 'subaccount-orders',
@@ -194,12 +231,13 @@ const OrderbookTable = ({
label="Orderbook"
data={data}
columns={columns}
getRowKey={(row: RowData) => `${row.side}-${row.price}`}
getRowKey={(row: RowData) => row.key}
getRowAttributes={(row: RowData) => ({
'data-side': row.side,
style: {
'--histogram-bucket-size': row.size,
'--histogram-bucket-depth': row.depth,
'--tr-pointerEvents': row.price ? 'auto' : 'none',
},
})}
onRowAction={onRowAction}
@@ -238,19 +276,10 @@ export const Orderbook = ({
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(
() =>
[
...bids.slice(0, showRowsPerSide).reverse(),
...bids.reverse(),
{
key: 'spread',
slotCustomRow: (props) => (
@@ -267,9 +296,9 @@ export const Orderbook = ({
</Styled.SpreadTableRow>
),
} as CustomRowConfig,
...asks.slice(0, showRowsPerSide),
...asks,
].reverse(),
[asks, bids, spread, spreadPercent, showRowsPerSide, isTablet]
[asks, bids, spread, spreadPercent, isTablet]
);
const onRowAction = useCallback(
@@ -325,7 +354,7 @@ export const Orderbook = ({
</Styled.Header>
<Styled.SplitOrderbook>
<OrderbookTable data={asks} histogramSide="right" {...orderbookTableProps} />
<OrderbookTable data={bids} histogramSide="left" {...orderbookTableProps} />
<OrderbookTable data={bids.reverse()} histogramSide="left" {...orderbookTableProps} />
</Styled.SplitOrderbook>
</Styled.HorizontalLayout>
);
@@ -495,6 +524,7 @@ Styled.OrderbookTable = styled(OrderbookTradesTable)<StyleProps>`
tr {
--histogram-bucket-depth: 0;
pointer-events: var(--tr-pointerEvents);
&[data-side='bid'] {
--accent-color: var(--color-positive);