Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b71710cef | ||
|
|
91f5b89eb6 | ||
|
|
41cc531700 | ||
|
|
5aeb01f968 | ||
|
|
c9596de9c3 | ||
|
|
cc8c724f09 |
+9
-4
@@ -39,9 +39,9 @@
|
||||
"@cosmjs/proto-signing": "^0.32.1",
|
||||
"@cosmjs/stargate": "^0.32.1",
|
||||
"@cosmjs/tendermint-rpc": "^0.32.1",
|
||||
"@dydxprotocol/v4-abacus": "^1.2.5",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.14",
|
||||
"@dydxprotocol/v4-localization": "^1.1.11",
|
||||
"@dydxprotocol/v4-abacus": "^1.3.2",
|
||||
"@dydxprotocol/v4-client-js": "^1.0.17",
|
||||
"@dydxprotocol/v4-localization": "^1.1.17",
|
||||
"@ethersproject/providers": "^5.7.2",
|
||||
"@js-joda/core": "^5.5.3",
|
||||
"@radix-ui/react-accordion": "^1.1.2",
|
||||
@@ -155,5 +155,10 @@
|
||||
"vitest": "^0.32.2",
|
||||
"w3name": "^1.0.8",
|
||||
"web3.storage": "^4.5.4"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"follow-redirects": "1.15.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
+24
-31
@@ -1,8 +1,7 @@
|
||||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
overrides:
|
||||
follow-redirects: 1.15.3
|
||||
|
||||
dependencies:
|
||||
'@0xsquid/sdk':
|
||||
@@ -27,14 +26,14 @@ dependencies:
|
||||
specifier: ^0.32.1
|
||||
version: 0.32.2
|
||||
'@dydxprotocol/v4-abacus':
|
||||
specifier: ^1.2.5
|
||||
version: 1.2.5
|
||||
specifier: ^1.3.2
|
||||
version: 1.3.2
|
||||
'@dydxprotocol/v4-client-js':
|
||||
specifier: ^1.0.14
|
||||
version: 1.0.14
|
||||
specifier: ^1.0.17
|
||||
version: 1.0.17
|
||||
'@dydxprotocol/v4-localization':
|
||||
specifier: ^1.1.11
|
||||
version: 1.1.11
|
||||
specifier: ^1.1.17
|
||||
version: 1.1.17
|
||||
'@ethersproject/providers':
|
||||
specifier: ^5.7.2
|
||||
version: 5.7.2
|
||||
@@ -1086,12 +1085,12 @@ packages:
|
||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||
dev: true
|
||||
|
||||
/@dydxprotocol/v4-abacus@1.2.5:
|
||||
resolution: {integrity: sha512-pB3Fv1bzJRUDp29kAN61daZ34gTSGrSTNh+/AP3TUB63/Q/gaXQVBf91xVcso4Hw23xM3FwIcrRK0cvOSWgXKg==}
|
||||
/@dydxprotocol/v4-abacus@1.3.2:
|
||||
resolution: {integrity: sha512-zo0IHjGMlJRKOYDgNqNFQ9GtBJKJP4+Y9YY7V0X3Wt61ppKAYzodaYQhc9V/RYchcZTtS/xkicLug444YrvehQ==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-client-js@1.0.14:
|
||||
resolution: {integrity: sha512-nIjj9OKuXb3n3rrihDPkBE6OIBxXNB7gf0NMsrG1Pc9O7M0yjpitTUgnfIzplf0XMrtmozWRYZhlLgBfJcIbBg==}
|
||||
/@dydxprotocol/v4-client-js@1.0.17:
|
||||
resolution: {integrity: sha512-PbTKbzcS7VapuFRofkirUxkF9ThNS2tWYyr0asFOMUfebsQWMvTC1sYC/s1NZIiBeq3cFz9vKdZFbfsKi7Z1bw==}
|
||||
dependencies:
|
||||
'@cosmjs/amino': 0.32.2
|
||||
'@cosmjs/encoding': 0.32.2
|
||||
@@ -1119,8 +1118,8 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-localization@1.1.11:
|
||||
resolution: {integrity: sha512-ZHnyrWD1bEpvY1tctkcmSV6A5+hQSYBFwyjbiyOqepqDqrv24J5a3hlZU94lDyYf+7Sdk3alOvy9Z7Lpk59nvw==}
|
||||
/@dydxprotocol/v4-localization@1.1.17:
|
||||
resolution: {integrity: sha512-kal1LrcihLMEv5YxaA/hd6Zl10Mp3x6jicoXDcXvtyNdrczAl3YapyI2nmeifRAPvfueOaY3W/sKkm2BiSKSsA==}
|
||||
dev: false
|
||||
|
||||
/@dydxprotocol/v4-proto@3.0.0-dev.0:
|
||||
@@ -7476,7 +7475,7 @@ packages:
|
||||
/axios@0.21.4:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.5
|
||||
follow-redirects: 1.15.3
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
@@ -7484,7 +7483,7 @@ packages:
|
||||
/axios@0.27.2:
|
||||
resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2
|
||||
follow-redirects: 1.15.3
|
||||
form-data: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
@@ -7493,7 +7492,7 @@ packages:
|
||||
/axios@1.1.3:
|
||||
resolution: {integrity: sha512-00tXVRwKx/FZr/IDVFt4C+f9FYairX517WoGCL6dpOntqLkZofjhu43F/Xl44UOpqa+9sLFDrG/XAnFsUYgkDA==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.5
|
||||
follow-redirects: 1.15.3
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -7503,7 +7502,7 @@ packages:
|
||||
/axios@1.6.5:
|
||||
resolution: {integrity: sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.5
|
||||
follow-redirects: 1.15.3
|
||||
form-data: 4.0.0
|
||||
proxy-from-env: 1.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -9526,18 +9525,8 @@ packages:
|
||||
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
|
||||
dev: true
|
||||
|
||||
/follow-redirects@1.15.2:
|
||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dev: false
|
||||
|
||||
/follow-redirects@1.15.5:
|
||||
resolution: {integrity: sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==}
|
||||
/follow-redirects@1.15.3:
|
||||
resolution: {integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
@@ -14920,3 +14909,7 @@ packages:
|
||||
/zwitch@2.0.4:
|
||||
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
|
||||
dev: true
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"name": "coinbase",
|
||||
"label": "Coinbase",
|
||||
"icon": "/exchanges/coinbase.png",
|
||||
"depositType": "noble"
|
||||
}
|
||||
]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -5,7 +5,7 @@ import { Checkbox, CheckboxProps } from '@/components/Checkbox';
|
||||
|
||||
import { StoryWrapper } from '.ladle/components';
|
||||
|
||||
export const Checkboxes: Story<CheckboxProps> = (args) => {
|
||||
export const CheckboxStory: Story<CheckboxProps> = (args) => {
|
||||
const [isChecked, setIsChecked] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -21,4 +21,12 @@ export const Checkboxes: Story<CheckboxProps> = (args) => {
|
||||
);
|
||||
};
|
||||
|
||||
Checkboxes.args = {};
|
||||
CheckboxStory.args = {};
|
||||
|
||||
CheckboxStory.argTypes = {
|
||||
disabled: {
|
||||
options: [true, false],
|
||||
control: { type: 'select' },
|
||||
defaultValue: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
import styled, { css, type AnyStyledComponent } from 'styled-components';
|
||||
import { Root, Indicator } from '@radix-ui/react-checkbox';
|
||||
import { CheckIcon } from '@radix-ui/react-icons';
|
||||
|
||||
@@ -9,6 +9,7 @@ type ElementProps = {
|
||||
onCheckedChange: (checked: boolean) => void;
|
||||
id?: string;
|
||||
label?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
@@ -23,14 +24,15 @@ export const Checkbox: React.FC<CheckboxProps> = ({
|
||||
onCheckedChange,
|
||||
id,
|
||||
label,
|
||||
disabled
|
||||
}) => (
|
||||
<Styled.Container>
|
||||
<Styled.Root className={className} checked={checked} onCheckedChange={onCheckedChange} id={id}>
|
||||
<Styled.Root className={className} checked={checked} disabled={disabled} onCheckedChange={onCheckedChange} id={id}>
|
||||
<Styled.Indicator>
|
||||
<CheckIcon />
|
||||
</Styled.Indicator>
|
||||
</Styled.Root>
|
||||
{label && <label htmlFor={id}>{label}</label>}
|
||||
{label && <Styled.label disabled={disabled} htmlFor={id}>{label}</Styled.label>}
|
||||
</Styled.Container>
|
||||
);
|
||||
|
||||
@@ -48,17 +50,23 @@ Styled.Container = styled.div`
|
||||
|
||||
Styled.Root = styled(Root)`
|
||||
--checkbox-backgroundColor: var(--color-layer-0);
|
||||
--checkbox-borderColor: var(--color-border);
|
||||
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
|
||||
border-radius: 0.375rem;
|
||||
border: var(--border-width) solid var(--color-border);
|
||||
border: var(--border-width) solid var(--checkbox-borderColor);
|
||||
background-color: var(--checkbox-backgroundColor);
|
||||
|
||||
&[data-state='checked'] {
|
||||
--checkbox-backgroundColor: var(--color-accent);
|
||||
}
|
||||
|
||||
&[data-disabled] {
|
||||
cursor: not-allowed;
|
||||
--checkbox-backgroundColor: var(--color-layer-1);
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.Indicator = styled(Indicator)`
|
||||
@@ -68,3 +76,11 @@ Styled.Indicator = styled(Indicator)`
|
||||
|
||||
color: var(--color-text-2);
|
||||
`;
|
||||
|
||||
Styled.label = styled.div<{ disabled: boolean; }>`
|
||||
color: var(--color-text-2);
|
||||
|
||||
${({disabled}) => disabled && css`
|
||||
color: var(--color-text-0);
|
||||
`}
|
||||
`;
|
||||
|
||||
@@ -11,6 +11,7 @@ type ElementProps = {
|
||||
};
|
||||
|
||||
type StyleProps = {
|
||||
className?: string;
|
||||
hasLogo?: boolean;
|
||||
size?: number;
|
||||
};
|
||||
@@ -18,7 +19,7 @@ type StyleProps = {
|
||||
const DARK_LOGO_MARK_URL = '/logos/logo-mark-dark.svg';
|
||||
const LIGHT_LOGO_MARK_URL = '/logos/logo-mark-light.svg';
|
||||
|
||||
export const QrCode = ({ value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
|
||||
export const QrCode = ({ className, value, hasLogo, size = 300 }: ElementProps & StyleProps) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const appTheme: AppTheme = useSelector(getAppTheme);
|
||||
|
||||
@@ -74,7 +75,7 @@ export const QrCode = ({ value, hasLogo, size = 300 }: ElementProps & StyleProps
|
||||
}
|
||||
}, [appTheme, hasLogo]);
|
||||
|
||||
return <Styled.QrCode ref={ref} />;
|
||||
return <Styled.QrCode className={className} ref={ref} />;
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B
|
||||
|
||||
type ElementProps = {
|
||||
timeoutInSeconds: number;
|
||||
slotFinal?: ReactNode;
|
||||
} & ButtonProps;
|
||||
|
||||
export type TimeoutButtonProps = ElementProps;
|
||||
@@ -15,6 +16,7 @@ export type TimeoutButtonProps = ElementProps;
|
||||
export const TimeoutButton = ({
|
||||
children,
|
||||
timeoutInSeconds,
|
||||
slotFinal,
|
||||
...otherProps
|
||||
}: TimeoutButtonProps) => {
|
||||
const [timeoutDeadline] = useState(Date.now() + timeoutInSeconds * 1000);
|
||||
@@ -23,6 +25,8 @@ export const TimeoutButton = ({
|
||||
|
||||
const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);
|
||||
|
||||
if (slotFinal && secondsLeft <= 0) return slotFinal;
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...otherProps}
|
||||
|
||||
@@ -138,6 +138,10 @@ export const tradeTooltips: TooltipStrings = {
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_BODY }),
|
||||
}),
|
||||
'post-only-timeinforce-gtt': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TIMEINFORCE_GTT_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.POST_ONLY_TIMEINFORCE_GTT_BODY }),
|
||||
}),
|
||||
'price-impact': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.PRICE_IMPACT_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.PRICE_IMPACT_BODY }),
|
||||
@@ -151,6 +155,14 @@ export const tradeTooltips: TooltipStrings = {
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_BODY }),
|
||||
learnMoreLink: urlConfigs?.reduceOnlyLearnMore,
|
||||
}),
|
||||
'reduce-only-execution-ioc-fok': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_EXECUTION_IOC_FOK_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_EXECUTION_IOC_FOK_BODY }),
|
||||
}),
|
||||
'reduce-only-timeinforce-ioc-fok': ({ stringGetter }) => ({
|
||||
title: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TIMEINFORCE_IOC_FOK_TITLE }),
|
||||
body: stringGetter({ key: TOOLTIP_STRING_KEYS.REDUCE_ONLY_TIMEINFORCE_IOC_FOK_BODY }),
|
||||
}),
|
||||
spread: () => ({
|
||||
title: 'Spread',
|
||||
body: 'The difference in price between the highest bid (the price a buyer is willing to buy for) and lowest ask (the price a seller is willing to sell for) an asset.',
|
||||
|
||||
@@ -33,6 +33,7 @@ export enum WalletConnectionType {
|
||||
CosmosSigner = 'CosmosSigner',
|
||||
InjectedEip1193 = 'injectedEip1193',
|
||||
WalletConnect2 = 'walletConnect2',
|
||||
TestWallet = 'TestWallet',
|
||||
}
|
||||
|
||||
export enum WalletErrorType {
|
||||
@@ -68,6 +69,9 @@ export const walletConnectionTypes: Record<WalletConnectionType, WalletConnectio
|
||||
[WalletConnectionType.CosmosSigner]: {
|
||||
name: 'CosmosSigner',
|
||||
},
|
||||
[WalletConnectionType.TestWallet]: {
|
||||
name: 'TestWallet',
|
||||
},
|
||||
};
|
||||
|
||||
// Wallets
|
||||
@@ -88,7 +92,7 @@ export enum WalletType {
|
||||
TokenPocket = 'TOKEN_POCKET',
|
||||
TrustWallet = 'TRUST_WALLET',
|
||||
WalletConnect2 = 'WALLETCONNECT_2',
|
||||
// TestWallet = 'TEST_WALLET',
|
||||
TestWallet = 'TEST_WALLET',
|
||||
OtherWallet = 'OTHER_WALLET',
|
||||
}
|
||||
|
||||
@@ -243,6 +247,12 @@ export const wallets: Record<WalletType, WalletConfig> = {
|
||||
icon: KeplrIcon,
|
||||
connectionTypes: [WalletConnectionType.CosmosSigner],
|
||||
},
|
||||
[WalletType.TestWallet]: {
|
||||
type: WalletType.TestWallet,
|
||||
stringKey: STRING_KEYS.TEST_WALLET,
|
||||
icon: GenericWalletIcon,
|
||||
connectionTypes: [WalletConnectionType.TestWallet],
|
||||
},
|
||||
};
|
||||
|
||||
// Injected EIP-1193 Providers
|
||||
@@ -354,3 +364,5 @@ export enum DydxChainAsset {
|
||||
USDC = 'usdc',
|
||||
CHAINTOKEN = 'chain',
|
||||
}
|
||||
|
||||
export const TEST_WALLET_EVM_ADDRESS: EvmAddress = '0x0000000000000000000000000000000000000000';
|
||||
|
||||
@@ -6,14 +6,22 @@ import { NOBLE_BECH32_PREFIX, LocalWallet, type Subaccount } from '@dydxprotocol
|
||||
|
||||
import { OnboardingGuard, OnboardingState, type EvmDerivedAddresses } from '@/constants/account';
|
||||
import { DialogTypes } from '@/constants/dialogs';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { LocalStorageKey, LOCAL_STORAGE_VERSIONS } from '@/constants/localStorage';
|
||||
import { DydxAddress, EvmAddress, PrivateInformation } from '@/constants/wallets';
|
||||
import {
|
||||
DydxAddress,
|
||||
EvmAddress,
|
||||
PrivateInformation,
|
||||
TEST_WALLET_EVM_ADDRESS,
|
||||
WalletType,
|
||||
} from '@/constants/wallets';
|
||||
|
||||
import { setOnboardingState, setOnboardingGuard } from '@/state/account';
|
||||
import { forceOpenDialog } from '@/state/dialogs';
|
||||
|
||||
import abacusStateManager from '@/lib/abacus';
|
||||
import { log } from '@/lib/telemetry';
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
|
||||
import { useDydxClient } from './useDydxClient';
|
||||
import { useLocalStorage } from './useLocalStorage';
|
||||
@@ -153,6 +161,7 @@ const useAccountsContext = () => {
|
||||
|
||||
// dYdX wallet / onboarding state
|
||||
const [localDydxWallet, setLocalDydxWallet] = useState<LocalWallet>();
|
||||
const [localNobleWallet, setLocalNobleWallet] = useState<LocalWallet>();
|
||||
const [hdKey, setHdKey] = useState<PrivateInformation>();
|
||||
|
||||
const dydxAccounts = useMemo(() => localDydxWallet?.accounts, [localDydxWallet]);
|
||||
@@ -162,6 +171,11 @@ const useAccountsContext = () => {
|
||||
[localDydxWallet]
|
||||
);
|
||||
|
||||
const nobleAddress = useMemo(
|
||||
() => localNobleWallet?.address,
|
||||
[localNobleWallet]
|
||||
);
|
||||
|
||||
const setWalletFromEvmSignature = async (signature: string) => {
|
||||
const { wallet, mnemonic, privateKey, publicKey } = await getWalletFromEvmSignature({
|
||||
signature,
|
||||
@@ -178,7 +192,26 @@ const useAccountsContext = () => {
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (connectedDydxAddress && signerGraz) {
|
||||
if (walletType === WalletType.TestWallet) {
|
||||
// Get override values. Use the testFlags value if it exists, otherwise use the previously
|
||||
// saved value where possible. If neither exist, use a default garbage value.
|
||||
const addressOverride: DydxAddress = testFlags.addressOverride as DydxAddress ||
|
||||
evmDerivedAddresses?.[TEST_WALLET_EVM_ADDRESS]?.dydxAddress as DydxAddress ||
|
||||
'dydx1';
|
||||
|
||||
dispatch(setOnboardingState(OnboardingState.WalletConnected));
|
||||
|
||||
// Set variables.
|
||||
saveEvmDerivedAccount({
|
||||
evmAddress: TEST_WALLET_EVM_ADDRESS,
|
||||
dydxAddress: addressOverride,
|
||||
});
|
||||
const wallet = new LocalWallet();
|
||||
wallet.address = addressOverride;
|
||||
setLocalDydxWallet(wallet);
|
||||
|
||||
dispatch(setOnboardingState(OnboardingState.AccountConnected));
|
||||
} else if (connectedDydxAddress && signerGraz) {
|
||||
dispatch(setOnboardingState(OnboardingState.WalletConnected));
|
||||
try {
|
||||
setLocalDydxWallet(await LocalWallet.fromOfflineSigner(signerGraz));
|
||||
@@ -224,6 +257,7 @@ const useAccountsContext = () => {
|
||||
if (hdKey?.mnemonic) {
|
||||
const nobleWallet = await LocalWallet.fromMnemonic(hdKey.mnemonic, NOBLE_BECH32_PREFIX);
|
||||
abacusStateManager.setNobleWallet(nobleWallet);
|
||||
setLocalNobleWallet(nobleWallet);
|
||||
}
|
||||
};
|
||||
setNobleWallet();
|
||||
@@ -322,6 +356,7 @@ const useAccountsContext = () => {
|
||||
localDydxWallet,
|
||||
dydxAccounts,
|
||||
dydxAddress,
|
||||
nobleAddress,
|
||||
|
||||
// Onboarding state
|
||||
saveHasAcknowledgedTerms,
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { EvmDerivedAddresses } from '@/constants/account';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { LocalStorageKey } from '@/constants/localStorage';
|
||||
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||
|
||||
@@ -38,6 +39,7 @@ import { getWalletConnection, parseWalletError } from '@/lib/wallet';
|
||||
import { log } from '@/lib/telemetry';
|
||||
|
||||
import { useStringGetter } from './useStringGetter';
|
||||
import { testFlags } from '@/lib/testFlags';
|
||||
|
||||
export const useWalletConnection = () => {
|
||||
const stringGetter = useStringGetter();
|
||||
@@ -138,6 +140,8 @@ export const useWalletConnection = () => {
|
||||
walletType: cosmosWalletType,
|
||||
});
|
||||
}
|
||||
} else if (walletConnection.type === WalletConnectionType.TestWallet) {
|
||||
saveEvmAddress(STRING_KEYS.TEST_WALLET as EvmAddress);
|
||||
} else {
|
||||
const isAccountConnected = Boolean(
|
||||
evmAddress && evmDerivedAddresses[evmAddress]?.encryptedSignature
|
||||
@@ -224,6 +228,15 @@ export const useWalletConnection = () => {
|
||||
setSelectedWalletType(walletType);
|
||||
};
|
||||
|
||||
// On page load, if testFlag.address is set, connect to the test wallet.
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (testFlags.addressOverride) {
|
||||
setSelectedWalletType(WalletType.TestWallet);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
// Wallet connection
|
||||
walletType,
|
||||
|
||||
@@ -22,6 +22,10 @@ class TestFlags {
|
||||
get showMobileSignInOption() {
|
||||
return !!this.queryParams.mobilesignin;
|
||||
}
|
||||
|
||||
get addressOverride():string {
|
||||
return this.queryParams.address;
|
||||
}
|
||||
}
|
||||
|
||||
export const testFlags = new TestFlags();
|
||||
|
||||
@@ -62,6 +62,12 @@ export const getWalletConnection = ({
|
||||
type: WalletConnectionType.CosmosSigner,
|
||||
};
|
||||
}
|
||||
|
||||
case WalletConnectionType.TestWallet: {
|
||||
return {
|
||||
type: WalletConnectionType.TestWallet,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,6 +96,8 @@ export const useTradeFormData = () => {
|
||||
needsGoodUntil,
|
||||
needsPostOnly,
|
||||
needsReduceOnly,
|
||||
postOnlyTooltip,
|
||||
reduceOnlyTooltip,
|
||||
timeInForceOptions,
|
||||
} = tradeOptions || {};
|
||||
|
||||
@@ -111,6 +113,8 @@ export const useTradeFormData = () => {
|
||||
needsGoodUntil,
|
||||
needsPostOnly,
|
||||
needsReduceOnly,
|
||||
postOnlyTooltip,
|
||||
reduceOnlyTooltip,
|
||||
timeInForceOptions,
|
||||
|
||||
tradeErrors,
|
||||
|
||||
@@ -17,7 +17,6 @@ import type { EvmAddress } from '@/constants/wallets';
|
||||
import { useAccounts, useDebounce, useStringGetter, useSelectedNetwork } from '@/hooks';
|
||||
import { useAccountBalance, CHAIN_DEFAULT_TOKEN_ADDRESS } from '@/hooks/useAccountBalance';
|
||||
import { useLocalNotifications } from '@/hooks/useLocalNotifications';
|
||||
import { useWalletConnection } from '@/hooks/useWalletConnection';
|
||||
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
import { formMixins } from '@/styles/formMixins';
|
||||
@@ -41,10 +40,11 @@ import { getNobleChainId, NATIVE_TOKEN_ADDRESS } from '@/lib/squid';
|
||||
import { log } from '@/lib/telemetry';
|
||||
import { parseWalletError } from '@/lib/wallet';
|
||||
|
||||
import { ChainSelectMenu } from './ChainSelectMenu';
|
||||
import { SourceSelectMenu } from './SourceSelectMenu';
|
||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||
|
||||
import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
|
||||
import { NobleDeposit } from '../NobleDeposit';
|
||||
|
||||
type DepositFormProps = {
|
||||
onDeposit?: () => void;
|
||||
@@ -57,13 +57,14 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { selectedNetwork } = useSelectedNetwork();
|
||||
|
||||
const { evmAddress, signerWagmi, publicClientWagmi } = useAccounts();
|
||||
const { evmAddress, signerWagmi, publicClientWagmi, nobleAddress } = useAccounts();
|
||||
|
||||
const { addTransferNotification } = useLocalNotifications();
|
||||
|
||||
const {
|
||||
requestPayload,
|
||||
token,
|
||||
exchange,
|
||||
chain: chainIdStr,
|
||||
resources,
|
||||
summary,
|
||||
@@ -129,14 +130,21 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
if (error) onError?.();
|
||||
}, [error]);
|
||||
|
||||
const onSelectChain = useCallback((chain: string) => {
|
||||
if (chain) {
|
||||
const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => {
|
||||
if (name) {
|
||||
abacusStateManager.clearTransferInputValues();
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.chain,
|
||||
value: chain,
|
||||
});
|
||||
setFromAmount('');
|
||||
if (type === 'chain') {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.chain,
|
||||
value: name,
|
||||
});
|
||||
} else {
|
||||
abacusStateManager.setTransferValue({
|
||||
field: TransferInputField.exchange,
|
||||
value: name,
|
||||
});
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
@@ -369,39 +377,49 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
||||
|
||||
return (
|
||||
<Styled.Form onSubmit={onSubmit}>
|
||||
<ChainSelectMenu selectedChain={chainIdStr || undefined} onSelectChain={onSelectChain} />
|
||||
<TokenSelectMenu selectedToken={sourceToken || undefined} onSelectToken={onSelectToken} />
|
||||
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
|
||||
<FormInput
|
||||
type={InputType.Number}
|
||||
onChange={onChangeAmount}
|
||||
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
|
||||
value={fromAmount}
|
||||
slotRight={
|
||||
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
|
||||
{stringGetter({ key: STRING_KEYS.MAX })}
|
||||
</Styled.FormInputButton>
|
||||
}
|
||||
/>
|
||||
</Styled.WithDetailsReceipt>
|
||||
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
||||
{requireUserActionInWallet && (
|
||||
<AlertMessage type={AlertType.Warning}>
|
||||
{stringGetter({ key: STRING_KEYS.CHECK_WALLET_FOR_REQUEST })}
|
||||
</AlertMessage>
|
||||
<SourceSelectMenu
|
||||
selectedChain={chainIdStr || undefined}
|
||||
selectedExchange={exchange || undefined}
|
||||
onSelect={onSelectChain}
|
||||
/>
|
||||
{exchange && nobleAddress ? (
|
||||
<NobleDeposit />
|
||||
) : (
|
||||
<>
|
||||
<TokenSelectMenu selectedToken={sourceToken || undefined} onSelectToken={onSelectToken} />
|
||||
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
|
||||
<FormInput
|
||||
type={InputType.Number}
|
||||
onChange={onChangeAmount}
|
||||
label={stringGetter({ key: STRING_KEYS.AMOUNT })}
|
||||
value={fromAmount}
|
||||
slotRight={
|
||||
<Styled.FormInputButton size={ButtonSize.XSmall} onClick={onClickMax}>
|
||||
{stringGetter({ key: STRING_KEYS.MAX })}
|
||||
</Styled.FormInputButton>
|
||||
}
|
||||
/>
|
||||
</Styled.WithDetailsReceipt>
|
||||
{errorMessage && <AlertMessage type={AlertType.Error}>{errorMessage}</AlertMessage>}
|
||||
{requireUserActionInWallet && (
|
||||
<AlertMessage type={AlertType.Warning}>
|
||||
{stringGetter({ key: STRING_KEYS.CHECK_WALLET_FOR_REQUEST })}
|
||||
</AlertMessage>
|
||||
)}
|
||||
<Styled.Footer>
|
||||
<DepositButtonAndReceipt
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
chainId={chainId || undefined}
|
||||
setSlippage={onSetSlippage}
|
||||
slippage={slippage}
|
||||
sourceToken={sourceToken || undefined}
|
||||
setRequireUserActionInWallet={setRequireUserActionInWallet}
|
||||
setError={setError}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
</>
|
||||
)}
|
||||
<Styled.Footer>
|
||||
<DepositButtonAndReceipt
|
||||
isDisabled={isDisabled}
|
||||
isLoading={isLoading}
|
||||
chainId={chainId || undefined}
|
||||
setSlippage={onSetSlippage}
|
||||
slippage={slippage}
|
||||
sourceToken={sourceToken || undefined}
|
||||
setRequireUserActionInWallet={setRequireUserActionInWallet}
|
||||
setError={setError}
|
||||
/>
|
||||
</Styled.Footer>
|
||||
</Styled.Form>
|
||||
);
|
||||
};
|
||||
|
||||
+41
-8
@@ -3,6 +3,7 @@ import { shallowEqual, useSelector } from 'react-redux';
|
||||
|
||||
import { TransferType } from '@/constants/abacus';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
|
||||
import { useStringGetter } from '@/hooks';
|
||||
|
||||
import { SearchSelectMenu } from '@/components/SearchSelectMenu';
|
||||
@@ -12,45 +13,77 @@ import { popoverMixins } from '@/styles/popoverMixins';
|
||||
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
|
||||
import { isTruthy } from '@/lib/isTruthy';
|
||||
|
||||
type ElementProps = {
|
||||
label?: string;
|
||||
selectedExchange?: string;
|
||||
selectedChain?: string;
|
||||
onSelectChain: (chain: string) => void;
|
||||
onSelect: (name: string, type: 'chain' | 'exchange') => void;
|
||||
};
|
||||
|
||||
export const ChainSelectMenu = ({ label, selectedChain, onSelectChain }: ElementProps) => {
|
||||
export const SourceSelectMenu = ({
|
||||
label,
|
||||
selectedExchange,
|
||||
selectedChain,
|
||||
onSelect,
|
||||
}: ElementProps) => {
|
||||
const stringGetter = useStringGetter();
|
||||
const { type, depositOptions, withdrawalOptions, resources } =
|
||||
useSelector(getTransferInputs, shallowEqual) || {};
|
||||
const chains =
|
||||
(type === TransferType.deposit ? depositOptions : withdrawalOptions)?.chains?.toArray() || [];
|
||||
|
||||
const exchanges =
|
||||
(type === TransferType.deposit ? depositOptions : withdrawalOptions)?.exchanges?.toArray() ||
|
||||
[];
|
||||
|
||||
const chainItems = Object.values(chains).map((chain) => ({
|
||||
value: chain.type,
|
||||
label: chain.stringKey,
|
||||
onSelect: () => {
|
||||
onSelectChain(chain.type);
|
||||
onSelect(chain.type, 'chain');
|
||||
},
|
||||
slotBefore: <Styled.Img src={chain.iconUrl} alt="" />,
|
||||
}));
|
||||
|
||||
const selectedOption = chains.find((item) => item.type === selectedChain);
|
||||
const exchangeItems = Object.values(exchanges).map((exchange) => ({
|
||||
value: exchange.type,
|
||||
label: exchange.string,
|
||||
onSelect: () => {
|
||||
onSelect(exchange.type, 'exchange');
|
||||
},
|
||||
slotBefore: <Styled.Img src={exchange.iconUrl} alt="" />,
|
||||
}));
|
||||
|
||||
const selectedChainOption = chains.find((item) => item.type === selectedChain);
|
||||
const selectedExchangeOption = exchanges.find((item) => item.type === selectedExchange);
|
||||
|
||||
return (
|
||||
<SearchSelectMenu
|
||||
items={[
|
||||
{
|
||||
exchangeItems.length > 0 && {
|
||||
group: 'exchanges',
|
||||
groupLabel: stringGetter({ key: STRING_KEYS.EXCHANGES }),
|
||||
items: exchangeItems,
|
||||
},
|
||||
chainItems.length > 0 && {
|
||||
group: 'chains',
|
||||
groupLabel: stringGetter({ key: STRING_KEYS.CHAINS }),
|
||||
items: chainItems,
|
||||
},
|
||||
]}
|
||||
].filter(isTruthy)}
|
||||
label={label || (type === TransferType.deposit ? 'Source' : 'Destination')}
|
||||
>
|
||||
<Styled.ChainRow>
|
||||
{selectedChain ? (
|
||||
{selectedChainOption ? (
|
||||
<>
|
||||
<Styled.Img src={selectedOption?.iconUrl} alt="" /> {selectedOption?.stringKey}
|
||||
<Styled.Img src={selectedChainOption.iconUrl} alt="" /> {selectedChainOption.stringKey}
|
||||
</>
|
||||
) : selectedExchangeOption ? (
|
||||
<>
|
||||
<Styled.Img src={selectedExchangeOption.iconUrl} alt="" />{' '}
|
||||
{selectedExchangeOption.string}
|
||||
</>
|
||||
) : (
|
||||
stringGetter({ key: STRING_KEYS.SELECT_CHAIN })
|
||||
@@ -38,7 +38,7 @@ import { Tag } from '@/components/Tag';
|
||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
|
||||
import { ChainSelectMenu } from '@/views/forms/AccountManagementForms/ChainSelectMenu';
|
||||
import { SourceSelectMenu } from '@/views/forms/AccountManagementForms/SourceSelectMenu';
|
||||
|
||||
import { getSubaccount } from '@/state/accountSelectors';
|
||||
import { getTransferInputs } from '@/state/inputsSelectors';
|
||||
@@ -374,10 +374,10 @@ export const WithdrawForm = () => {
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<ChainSelectMenu
|
||||
<SourceSelectMenu
|
||||
label={stringGetter({ key: STRING_KEYS.NETWORK })}
|
||||
selectedChain={chainIdStr || undefined}
|
||||
onSelectChain={onSelectChain}
|
||||
onSelect={onSelectChain}
|
||||
/>
|
||||
</Styled.DestinationRow>
|
||||
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} />
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { useState } from 'react';
|
||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||
|
||||
import { OpacityToken } from '@/constants/styles/base';
|
||||
import { STRING_KEYS } from '@/constants/localization';
|
||||
import { layoutMixins } from '@/styles/layoutMixins';
|
||||
|
||||
import { useAccounts, useStringGetter } from '@/hooks';
|
||||
|
||||
import { CopyButton } from '@/components/CopyButton';
|
||||
import { QrCode } from '@/components/QrCode';
|
||||
import { Checkbox } from '@/components/Checkbox';
|
||||
import { Icon, IconName } from '@/components/Icon';
|
||||
import { TimeoutButton } from '@/components/TimeoutButton';
|
||||
import { WithDetailsReceipt } from '@/components/WithDetailsReceipt';
|
||||
import { WithReceipt } from '@/components/WithReceipt';
|
||||
|
||||
import { generateFadedColorVariant } from '@/lib/styles';
|
||||
|
||||
export const NobleDeposit = () => {
|
||||
const [hasAcknowledged, setHasAcknowledged] = useState(false);
|
||||
const stringGetter = useStringGetter();
|
||||
const { nobleAddress } = useAccounts();
|
||||
|
||||
return (
|
||||
<>
|
||||
<WithDetailsReceipt
|
||||
side="bottom"
|
||||
detailItems={[
|
||||
{
|
||||
key: 'nobleAddress',
|
||||
label: stringGetter({ key: STRING_KEYS.NOBLE_ADDRESS }),
|
||||
value: nobleAddress,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Styled.QrCodeContainer>
|
||||
<Styled.QrCode size={432} value={nobleAddress || ''} />
|
||||
</Styled.QrCodeContainer>
|
||||
</WithDetailsReceipt>
|
||||
|
||||
<Styled.WaitingSpan>
|
||||
<Styled.CautionIconContainer>
|
||||
<Icon iconName={IconName.CautionCircleStroked} />
|
||||
</Styled.CautionIconContainer>
|
||||
|
||||
<p>{stringGetter({ key: STRING_KEYS.NOBLE_WARNING })}</p>
|
||||
</Styled.WaitingSpan>
|
||||
|
||||
<Styled.WithReceipt
|
||||
slotReceipt={
|
||||
<Styled.CheckboxContainer>
|
||||
<Checkbox
|
||||
checked={hasAcknowledged}
|
||||
onCheckedChange={setHasAcknowledged}
|
||||
id="acknowledge-secret-phase-risk"
|
||||
label={stringGetter({
|
||||
key: STRING_KEYS.NOBLE_ACKNOWLEDGEMENT,
|
||||
})}
|
||||
/>
|
||||
</Styled.CheckboxContainer>
|
||||
}
|
||||
>
|
||||
<TimeoutButton
|
||||
timeoutInSeconds={8}
|
||||
slotFinal={<CopyButton state={{ isDisabled: !hasAcknowledged }} value={nobleAddress} />}
|
||||
/>
|
||||
</Styled.WithReceipt>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Styled: Record<string, AnyStyledComponent> = {};
|
||||
|
||||
Styled.WaitingSpan = styled.span`
|
||||
${layoutMixins.row}
|
||||
gap: 1rem;
|
||||
color: var(--color-text-1);
|
||||
`;
|
||||
|
||||
Styled.WithReceipt = styled(WithReceipt)`
|
||||
--withReceipt-backgroundColor: var(--color-layer-2);
|
||||
`;
|
||||
|
||||
Styled.QrCodeContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
padding: 0.5rem;
|
||||
|
||||
background-color: var(--color-layer-2);
|
||||
border-radius: 0.5rem;
|
||||
`;
|
||||
|
||||
Styled.QrCode = styled(QrCode)`
|
||||
max-height: 20rem;
|
||||
width: fit-content;
|
||||
|
||||
svg {
|
||||
max-height: 20rem;
|
||||
}
|
||||
`;
|
||||
|
||||
Styled.CheckboxContainer = styled.div`
|
||||
padding: 1rem;
|
||||
color: var(--color-text-0);
|
||||
`;
|
||||
|
||||
Styled.CautionIconContainer = styled.div`
|
||||
${layoutMixins.stack}
|
||||
min-width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
align-items: center;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
color: var(--color-warning);
|
||||
|
||||
svg {
|
||||
width: 1.125em;
|
||||
height: 1.125em;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-color: ${({ theme }) =>
|
||||
generateFadedColorVariant(theme.warning, OpacityToken.Opacity16)};
|
||||
}
|
||||
`;
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
type HumanReadablePlaceOrderPayload,
|
||||
type Nullable,
|
||||
TradeInputErrorAction,
|
||||
TradeInputField,
|
||||
ValidationError,
|
||||
} from '@/constants/abacus';
|
||||
|
||||
@@ -96,6 +95,8 @@ export const TradeForm = ({
|
||||
needsGoodUntil,
|
||||
needsPostOnly,
|
||||
needsReduceOnly,
|
||||
postOnlyTooltip,
|
||||
reduceOnlyTooltip,
|
||||
timeInForceOptions,
|
||||
tradeErrors,
|
||||
} = useTradeFormData();
|
||||
@@ -108,7 +109,7 @@ export const TradeForm = ({
|
||||
const { limitPriceInput, triggerPriceInput, trailingPercentInput } = tradeFormInputValues;
|
||||
|
||||
const needsAdvancedOptions =
|
||||
needsGoodUntil || timeInForceOptions || executionOptions || needsPostOnly || needsReduceOnly;
|
||||
needsGoodUntil || timeInForceOptions || executionOptions || (needsPostOnly || postOnlyTooltip) || (needsReduceOnly || reduceOnlyTooltip);
|
||||
|
||||
const tradeFormInputs: TradeBoxInputConfig[] = [];
|
||||
|
||||
|
||||
@@ -31,12 +31,15 @@ export const AdvancedTradeOptions = () => {
|
||||
|
||||
const { execution, goodTil, postOnly, reduceOnly, timeInForce, type } = inputTradeData || {};
|
||||
|
||||
const { executionOptions, needsGoodUntil, needsPostOnly, needsReduceOnly, timeInForceOptions } =
|
||||
const { executionOptions, needsGoodUntil, needsPostOnly, needsReduceOnly, postOnlyTooltip, reduceOnlyTooltip, timeInForceOptions } =
|
||||
currentTradeFormConfig || {};
|
||||
|
||||
const { duration, unit } = goodTil || {};
|
||||
|
||||
const needsExecution = executionOptions || needsPostOnly || needsReduceOnly;
|
||||
const showPostOnly = (needsPostOnly || postOnlyTooltip);
|
||||
const showReduceOnly = (needsReduceOnly || reduceOnlyTooltip);
|
||||
|
||||
const needsExecution = executionOptions || showPostOnly || showReduceOnly;
|
||||
const hasTimeInForce = timeInForceOptions?.toArray()?.length;
|
||||
|
||||
return (
|
||||
@@ -129,26 +132,9 @@ export const AdvancedTradeOptions = () => {
|
||||
))}
|
||||
</Styled.SelectMenu>
|
||||
)}
|
||||
{needsPostOnly && (
|
||||
<Checkbox
|
||||
checked={postOnly || false}
|
||||
onCheckedChange={(checked) =>
|
||||
abacusStateManager.setTradeValue({
|
||||
value: checked,
|
||||
field: TradeInputField.postOnly,
|
||||
})
|
||||
}
|
||||
id="post-only"
|
||||
label={
|
||||
<WithTooltip tooltip="post-only" side="right">
|
||||
{stringGetter({ key: STRING_KEYS.POST_ONLY })}
|
||||
</WithTooltip>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{needsReduceOnly && (
|
||||
<Checkbox
|
||||
checked={reduceOnly || false}
|
||||
{showReduceOnly && <Checkbox
|
||||
checked={(reduceOnly && !reduceOnlyTooltip) || false}
|
||||
disabled={!!reduceOnlyTooltip}
|
||||
onCheckedChange={(checked) =>
|
||||
abacusStateManager.setTradeValue({
|
||||
value: checked,
|
||||
@@ -157,11 +143,29 @@ export const AdvancedTradeOptions = () => {
|
||||
}
|
||||
id="reduce-only"
|
||||
label={
|
||||
<WithTooltip tooltip="reduce-only" side="right">
|
||||
<WithTooltip tooltip={needsReduceOnly ? "reduce-only" : reduceOnlyTooltip?.titleStringKey.includes("REDUCE_ONLY_EXECUTION_IOC_FOK") ? "reduce-only-execution-ioc-fok" : "reduce-only-timeinforce-ioc-fok"} side="right">
|
||||
{stringGetter({ key: STRING_KEYS.REDUCE_ONLY })}
|
||||
</WithTooltip>
|
||||
}
|
||||
/>
|
||||
}
|
||||
{showPostOnly && (
|
||||
<Checkbox
|
||||
checked={(postOnly && !postOnlyTooltip) || false}
|
||||
disabled={!!postOnlyTooltip}
|
||||
onCheckedChange={(checked) =>
|
||||
abacusStateManager.setTradeValue({
|
||||
value: checked,
|
||||
field: TradeInputField.postOnly,
|
||||
})
|
||||
}
|
||||
id="post-only"
|
||||
label={
|
||||
<WithTooltip tooltip={needsPostOnly ? "post-only" : "post-only-timeinforce-gtt"} side="right">
|
||||
{stringGetter({ key: STRING_KEYS.POST_ONLY })}
|
||||
</WithTooltip>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user