Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b35f89c72d | ||
|
|
a46512f29b | ||
|
|
8f09f66bb0 | ||
|
|
371397b72e | ||
|
|
895fbeb573 | ||
|
|
662898ce82 | ||
|
|
abd97a91f9 | ||
|
|
ba78afc166 | ||
|
|
5ed9af9afa | ||
|
|
cd7e6298d8 | ||
|
|
f1a16f9de8 |
@@ -6,3 +6,5 @@ VITE_PK_ENCRYPTION_KEY=
|
|||||||
|
|
||||||
VITE_WALLETCONNECT1_BRIDGE=
|
VITE_WALLETCONNECT1_BRIDGE=
|
||||||
VITE_WALLETCONNECT2_PROJECT_ID=
|
VITE_WALLETCONNECT2_PROJECT_ID=
|
||||||
|
|
||||||
|
VITE_V3_TOKEN_ADDRESS=
|
||||||
+1
-1
@@ -39,7 +39,7 @@
|
|||||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||||
"@dydxprotocol/v4-abacus": "^0.6.3",
|
"@dydxprotocol/v4-abacus": "^0.6.3",
|
||||||
"@dydxprotocol/v4-client-js": "^0.36.1",
|
"@dydxprotocol/v4-client-js": "^0.36.1",
|
||||||
"@dydxprotocol/v4-localization": "^0.1.12",
|
"@dydxprotocol/v4-localization": "^0.1.23",
|
||||||
"@ethersproject/providers": "^5.7.2",
|
"@ethersproject/providers": "^5.7.2",
|
||||||
"@js-joda/core": "^5.5.3",
|
"@js-joda/core": "^5.5.3",
|
||||||
"@radix-ui/react-collapsible": "^1.0.3",
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
|
|||||||
Generated
+4
-4
@@ -33,8 +33,8 @@ dependencies:
|
|||||||
specifier: ^0.36.1
|
specifier: ^0.36.1
|
||||||
version: 0.36.1
|
version: 0.36.1
|
||||||
'@dydxprotocol/v4-localization':
|
'@dydxprotocol/v4-localization':
|
||||||
specifier: ^0.1.12
|
specifier: ^0.1.23
|
||||||
version: 0.1.12
|
version: 0.1.23
|
||||||
'@ethersproject/providers':
|
'@ethersproject/providers':
|
||||||
specifier: ^5.7.2
|
specifier: ^5.7.2
|
||||||
version: 5.7.2
|
version: 5.7.2
|
||||||
@@ -1010,8 +1010,8 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-localization@0.1.12:
|
/@dydxprotocol/v4-localization@0.1.23:
|
||||||
resolution: {integrity: sha512-EiP/8+6Nk9QBa6YSWsyzsZaXoVbz5o/KgUlMp1Cew6VWkXL6DkQY0oMIEEy21pv0zuu/woxhlbyXygpUIcpdMg==}
|
resolution: {integrity: sha512-TaEey7dINwxELlEyA8XsQ4GQLfJ7e1b434bafpnlG9ccW1sIW7TYTsfaSkck2egR4R736hA7k5WvVy0aI/0TFw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-proto@0.2.1:
|
/@dydxprotocol/v4-proto@0.2.1:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ type ElementProps = {
|
|||||||
};
|
};
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
href?: string;
|
href?: string;
|
||||||
onClick?: () => void;
|
onClick?: (e: MouseEvent) => void;
|
||||||
withIcon?: boolean;
|
withIcon?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,12 +37,12 @@ export const Link = forwardRef<HTMLAnchorElement, ElementProps & StyleProps>(
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={className}
|
className={className}
|
||||||
href={href}
|
href={href}
|
||||||
onClick={() => {
|
onClick={(e: MouseEvent) => {
|
||||||
if (analyticsConfig) {
|
if (analyticsConfig) {
|
||||||
console.log(analyticsConfig);
|
console.log(analyticsConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick?.();
|
onClick?.(e);
|
||||||
}}
|
}}
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ type PanelProps = {
|
|||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
href?: string;
|
href?: string;
|
||||||
onHeaderClick?: () => void;
|
onHeaderClick?: () => void;
|
||||||
|
onClick?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PanelStyleProps = {
|
type PanelStyleProps = {
|
||||||
@@ -24,10 +25,11 @@ export const Panel = ({
|
|||||||
children,
|
children,
|
||||||
href,
|
href,
|
||||||
onHeaderClick,
|
onHeaderClick,
|
||||||
|
onClick,
|
||||||
hasSeparator,
|
hasSeparator,
|
||||||
className,
|
className,
|
||||||
}: PanelProps & PanelStyleProps) => (
|
}: PanelProps & PanelStyleProps) => (
|
||||||
<Styled.Panel>
|
<Styled.Panel onClick={onClick}>
|
||||||
{href ? (
|
{href ? (
|
||||||
<Link to={href}>
|
<Link to={href}>
|
||||||
{slotHeader ? (
|
{slotHeader ? (
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export enum DialogTypes {
|
|||||||
ExchangeOffline = 'ExchangeOffline',
|
ExchangeOffline = 'ExchangeOffline',
|
||||||
FillDetails = 'FillDetails',
|
FillDetails = 'FillDetails',
|
||||||
Help = 'Help',
|
Help = 'Help',
|
||||||
|
ExternalNavKeplr = 'ExternalNavKeplr',
|
||||||
MnemonicExport = 'MnemonicExport',
|
MnemonicExport = 'MnemonicExport',
|
||||||
MobileSignIn = 'MobileSignIn',
|
MobileSignIn = 'MobileSignIn',
|
||||||
Onboarding = 'Onboarding',
|
Onboarding = 'Onboarding',
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { EvmAddress } from '@/constants/wallets';
|
|||||||
import { convertBech32Address } from '@/lib/addressUtils';
|
import { convertBech32Address } from '@/lib/addressUtils';
|
||||||
import { MustBigNumber } from '@/lib/numbers';
|
import { MustBigNumber } from '@/lib/numbers';
|
||||||
|
|
||||||
import { getBalances } from '@/state/accountSelectors';
|
import { getBalances, getStakingBalances } from '@/state/accountSelectors';
|
||||||
import { getSelectedNetwork } from '@/state/appSelectors';
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
|
|
||||||
import { useAccounts } from './useAccounts';
|
import { useAccounts } from './useAccounts';
|
||||||
@@ -53,6 +53,7 @@ export const useAccountBalance = ({
|
|||||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
const balances = useSelector(getBalances, shallowEqual);
|
const balances = useSelector(getBalances, shallowEqual);
|
||||||
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
const evmChainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||||
|
const stakingBalances = useSelector(getStakingBalances, shallowEqual);
|
||||||
|
|
||||||
const evmQuery = useBalance({
|
const evmQuery = useBalance({
|
||||||
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
|
enabled: Boolean(!isCosmosChain && addressOrDenom?.startsWith('0x')),
|
||||||
@@ -100,9 +101,15 @@ export const useAccountBalance = ({
|
|||||||
const usdcCoinBalance = balances?.[USDC_DENOM];
|
const usdcCoinBalance = balances?.[USDC_DENOM];
|
||||||
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
|
const usdcBalance = MustBigNumber(usdcCoinBalance?.amount).div(QUANTUM_MULTIPLIER).toNumber();
|
||||||
|
|
||||||
|
const nativeStakingCoinBalanace = stakingBalances?.[DYDX_DENOM];
|
||||||
|
const nativeStakingBalance = MustBigNumber(nativeStakingCoinBalanace?.amount)
|
||||||
|
.div(QUANTUM_MULTIPLIER)
|
||||||
|
.toNumber();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
balance,
|
balance,
|
||||||
nativeTokenBalance,
|
nativeTokenBalance,
|
||||||
|
nativeStakingBalance,
|
||||||
usdcBalance,
|
usdcBalance,
|
||||||
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
|
queryStatus: isCosmosChain ? cosmosQuery.status : evmQuery.status,
|
||||||
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
|
isQueryFetching: isCosmosChain ? cosmosQuery.isFetching : evmQuery.fetchStatus === 'fetching',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { DepositDialog } from '@/views/dialogs/DepositDialog';
|
|||||||
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
|
import { DisconnectDialog } from '@/views/dialogs/DisconnectDialog';
|
||||||
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
|
import { ExchangeOfflineDialog } from '@/views/dialogs/ExchangeOfflineDialog';
|
||||||
import { HelpDialog } from '@/views/dialogs/HelpDialog';
|
import { HelpDialog } from '@/views/dialogs/HelpDialog';
|
||||||
|
import { ExternalNavKeplrDialog } from '@/views/dialogs/ExternalNavKeplrDialog';
|
||||||
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
|
import { MnemonicExportDialog } from '@/views/dialogs/MnemonicExportDialog';
|
||||||
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
|
import { MobileSignInDialog } from '@/views/dialogs/MobileSignInDialog';
|
||||||
import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog';
|
import { OnboardingDialog } from '@/views/dialogs/OnboardingDialog';
|
||||||
@@ -47,6 +48,7 @@ export const DialogManager = () => {
|
|||||||
[DialogTypes.ExchangeOffline]: <ExchangeOfflineDialog {...modalProps} />,
|
[DialogTypes.ExchangeOffline]: <ExchangeOfflineDialog {...modalProps} />,
|
||||||
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
|
[DialogTypes.FillDetails]: <FillDetailsDialog {...modalProps} />,
|
||||||
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
[DialogTypes.Help]: <HelpDialog {...modalProps} />,
|
||||||
|
[DialogTypes.ExternalNavKeplr]: <ExternalNavKeplrDialog {...modalProps} />,
|
||||||
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
[DialogTypes.MnemonicExport]: <MnemonicExportDialog {...modalProps} />,
|
||||||
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
[DialogTypes.MobileSignIn]: <MobileSignInDialog {...modalProps} />,
|
||||||
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
[DialogTypes.Onboarding]: <OnboardingDialog {...modalProps} />,
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ export const HeaderDesktop = () => {
|
|||||||
label: stringGetter({ key: STRING_KEYS.TRADE }),
|
label: stringGetter({ key: STRING_KEYS.TRADE }),
|
||||||
href: AppRoute.Trade,
|
href: AppRoute.Trade,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'Dv4TNT',
|
||||||
|
label: 'Dv4TNT',
|
||||||
|
href: AppRoute.Rewards,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: 'PORTFOLIO',
|
value: 'PORTFOLIO',
|
||||||
label: stringGetter({ key: STRING_KEYS.PORTFOLIO }),
|
label: stringGetter({ key: STRING_KEYS.PORTFOLIO }),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import type { RootStore } from '@/state/_store';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
setBalances,
|
setBalances,
|
||||||
|
setStakingBalances,
|
||||||
setFills,
|
setFills,
|
||||||
setFundingPayments,
|
setFundingPayments,
|
||||||
setHistoricalPnl,
|
setHistoricalPnl,
|
||||||
@@ -82,6 +83,13 @@ class AbacusStateNotifier implements AbacusStateNotificationProtocol {
|
|||||||
}
|
}
|
||||||
dispatch(setBalances(balances));
|
dispatch(setBalances(balances));
|
||||||
}
|
}
|
||||||
|
if (updatedState.account?.stakingBalances) {
|
||||||
|
const stakingBalances: Record<string, AccountBalance> = {}
|
||||||
|
for (const { k, v } of updatedState.account.stakingBalances.toArray()) {
|
||||||
|
stakingBalances[k] = v;
|
||||||
|
}
|
||||||
|
dispatch(setStakingBalances(stakingBalances));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.has(Changes.configs)) {
|
if (changes.has(Changes.configs)) {
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ export const DYDXBalancePanel = () => {
|
|||||||
|
|
||||||
const { walletType } = useAccounts();
|
const { walletType } = useAccounts();
|
||||||
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
|
const canAccountTrade = useSelector(calculateCanAccountTrade, shallowEqual);
|
||||||
const { nativeTokenBalance } = useAccountBalance();
|
const { nativeTokenBalance, nativeStakingBalance } = useAccountBalance();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.TransfersCard
|
<Panel
|
||||||
slotHeader={
|
slotHeader={
|
||||||
<Styled.Header>
|
<Styled.Header>
|
||||||
<Styled.Title>
|
<Styled.Title>
|
||||||
@@ -97,7 +97,7 @@ export const DYDXBalancePanel = () => {
|
|||||||
</Styled.Label>
|
</Styled.Label>
|
||||||
),
|
),
|
||||||
|
|
||||||
value: <Output type={OutputType.Asset} value={undefined} />,
|
value: <Output type={OutputType.Asset} value={nativeStakingBalance} />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
@@ -106,21 +106,17 @@ export const DYDXBalancePanel = () => {
|
|||||||
{
|
{
|
||||||
key: 'totalBalance',
|
key: 'totalBalance',
|
||||||
label: 'Total balance',
|
label: 'Total balance',
|
||||||
value: <Output type={OutputType.Asset} value={nativeTokenBalance} tag="Dv4TNT" />,
|
value: <Output type={OutputType.Asset} value={nativeTokenBalance + nativeStakingBalance} tag="Dv4TNT" />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
</Styled.TransfersCard>
|
</Panel>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Styled: Record<string, AnyStyledComponent> = {};
|
const Styled: Record<string, AnyStyledComponent> = {};
|
||||||
|
|
||||||
Styled.TransfersCard = styled(Panel)`
|
|
||||||
width: 21.25rem;
|
|
||||||
`;
|
|
||||||
|
|
||||||
Styled.Header = styled.div`
|
Styled.Header = styled.div`
|
||||||
${layoutMixins.spacedRow}
|
${layoutMixins.spacedRow}
|
||||||
padding: 1.25rem 1.5rem 0.5rem;
|
padding: 1.25rem 1.5rem 0.5rem;
|
||||||
@@ -174,13 +170,15 @@ Styled.IconContainer = styled.div`
|
|||||||
|
|
||||||
Styled.WalletAndStakedBalance = styled(Details)`
|
Styled.WalletAndStakedBalance = styled(Details)`
|
||||||
--details-item-backgroundColor: var(--color-layer-6);
|
--details-item-backgroundColor: var(--color-layer-6);
|
||||||
|
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
|
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
width: 8.625rem;
|
|
||||||
|
|
||||||
border-radius: 0.75em;
|
border-radius: 0.75em;
|
||||||
background-color: var(--color-layer-5);
|
background-color: var(--color-layer-5);
|
||||||
|
|||||||
@@ -1,17 +1,333 @@
|
|||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
|
|
||||||
|
import { ENVIRONMENT_CONFIG_MAP } from '@/constants/networks';
|
||||||
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { ButtonAction, ButtonSize, ButtonState, ButtonType } from '@/constants/buttons';
|
||||||
|
import { DialogTypes } from '@/constants/dialogs';
|
||||||
|
|
||||||
|
import { useAccountBalance, useBreakpoints, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
|
import { breakpoints } from '@/styles';
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
|
import { Details } from '@/components/Details';
|
||||||
|
import { Panel } from '@/components/Panel';
|
||||||
|
import { IconName } from '@/components/Icon';
|
||||||
|
import { Button } from '@/components/Button';
|
||||||
|
import { IconButton } from '@/components/IconButton';
|
||||||
|
import { Link } from '@/components/Link';
|
||||||
|
import { Output, OutputType } from '@/components/Output';
|
||||||
|
import { VerticalSeparator } from '@/components/Separator';
|
||||||
|
import { WithReceipt } from '@/components/WithReceipt';
|
||||||
|
|
||||||
|
import { openDialog } from '@/state/dialogs';
|
||||||
|
import { getSelectedNetwork } from '@/state/appSelectors';
|
||||||
|
|
||||||
import { DYDXBalancePanel } from './DYDXBalancePanel';
|
import { DYDXBalancePanel } from './DYDXBalancePanel';
|
||||||
|
|
||||||
export const RewardsPage = () => (
|
// TODO: replace placeholder URL with real URLs when avaialble
|
||||||
<Styled.Page>
|
const GOVERNANCE_HELP_URL = 'https://help.dydx.exchange/';
|
||||||
<DYDXBalancePanel />
|
const STAKING_HELP_URL = 'https://help.dydx.exchange/';
|
||||||
</Styled.Page>
|
|
||||||
);
|
export const RewardsPage = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const stringGetter = useStringGetter();
|
||||||
|
const { isTablet, isNotTablet } = useBreakpoints();
|
||||||
|
|
||||||
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
|
|
||||||
|
// const chainId = Number(ENVIRONMENT_CONFIG_MAP[selectedNetwork].ethereumChainId);
|
||||||
|
|
||||||
|
// const { balance } = useAccountBalance({
|
||||||
|
// addressOrDenom: import.meta.env.VITE_V3_TOKEN_ADDRESS,
|
||||||
|
// assetSymbol: 'DYDX',
|
||||||
|
// chainId,
|
||||||
|
// isCosmosChain: false,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// const tokenBalance = import.meta.env.VITE_V3_TOKEN_ADDRESS ? balance : 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Styled.Page>
|
||||||
|
{/* {isNotTablet ? (
|
||||||
|
<Styled.Migrate>
|
||||||
|
<Styled.TwoItemRow>
|
||||||
|
<div>
|
||||||
|
<Styled.MigrateTitle>
|
||||||
|
{stringGetter({ key: STRING_KEYS.MIGRATE })}
|
||||||
|
</Styled.MigrateTitle>
|
||||||
|
<Styled.Description>
|
||||||
|
{stringGetter({ key: STRING_KEYS.MIGRATE_DESCRIPTION })}
|
||||||
|
<Link href={MIGRATE_HELP_URL}>
|
||||||
|
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||||
|
</Link>
|
||||||
|
</Styled.Description>
|
||||||
|
</div>
|
||||||
|
<Styled.MigrateAction>
|
||||||
|
<div>
|
||||||
|
<div>{stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE })}</div>
|
||||||
|
<div>
|
||||||
|
<Styled.Token type={OutputType.Asset} value={tokenBalance} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
|
||||||
|
// type={ButtonType.Link}
|
||||||
|
// href={BRIDGE_URL}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{tokenBalance
|
||||||
|
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
|
||||||
|
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
|
||||||
|
</Button>
|
||||||
|
</Styled.MigrateAction>
|
||||||
|
</Styled.TwoItemRow>
|
||||||
|
</Styled.Migrate>
|
||||||
|
) : (
|
||||||
|
<Styled.MobileMigrateCard
|
||||||
|
slotHeader={
|
||||||
|
<Styled.MobileMigrateHeader>
|
||||||
|
<h3>{stringGetter({ key: STRING_KEYS.MIGRATE })}</h3>
|
||||||
|
<VerticalSeparator />
|
||||||
|
<span>
|
||||||
|
{stringGetter({
|
||||||
|
key: STRING_KEYS.FROM_TO,
|
||||||
|
params: { FROM: <strong>Ethereum</strong>, TO: <strong>dYdX Chain</strong> },
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</Styled.MobileMigrateHeader>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Styled.WithReceipt
|
||||||
|
slotReceipt={
|
||||||
|
<Styled.Details
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'available-to-migrate',
|
||||||
|
label: stringGetter({ key: STRING_KEYS.AVAILABLE_TO_MIGRATE }),
|
||||||
|
value: <Output type={OutputType.Asset} value={tokenBalance} />,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
action={tokenBalance ? ButtonAction.Primary : ButtonAction.Base}
|
||||||
|
size={ButtonSize.Medium}
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
{tokenBalance
|
||||||
|
? `${stringGetter({ key: STRING_KEYS.MIGRATE_NOW })} →`
|
||||||
|
: stringGetter({ key: STRING_KEYS.NO_TOKENS_TO_MIGRATE })}
|
||||||
|
</Button>
|
||||||
|
</Styled.WithReceipt>
|
||||||
|
<Styled.LearnMore>
|
||||||
|
{stringGetter({ key: STRING_KEYS.WANT_TO_LEARN })}
|
||||||
|
<Link href={MIGRATE_HELP_URL}>{stringGetter({ key: STRING_KEYS.CLICK_HERE })}</Link>
|
||||||
|
</Styled.LearnMore>
|
||||||
|
</Styled.MobileMigrateCard>
|
||||||
|
)} */}
|
||||||
|
|
||||||
|
<Styled.PanelRow>
|
||||||
|
{isTablet && (
|
||||||
|
<Styled.BalancePanelContainer>
|
||||||
|
<DYDXBalancePanel />
|
||||||
|
</Styled.BalancePanelContainer>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Styled.Panel
|
||||||
|
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.GOVERNANCE })}</Styled.Title>}
|
||||||
|
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
|
||||||
|
>
|
||||||
|
<Styled.Row>
|
||||||
|
<Styled.Description>
|
||||||
|
{stringGetter({ key: STRING_KEYS.GOVERNANCE_DESCRIPTION })}
|
||||||
|
<Link href={GOVERNANCE_HELP_URL} onClick={(e) => e.stopPropagation()}>
|
||||||
|
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||||
|
</Link>
|
||||||
|
</Styled.Description>
|
||||||
|
{/* TODO: vertically center based on Panel height */}
|
||||||
|
<Styled.IconButton
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
iconName={IconName.Arrow}
|
||||||
|
size={ButtonSize.Small}
|
||||||
|
/>
|
||||||
|
</Styled.Row>
|
||||||
|
</Styled.Panel>
|
||||||
|
|
||||||
|
<Styled.Panel
|
||||||
|
slotHeader={<Styled.Title>{stringGetter({ key: STRING_KEYS.STAKING })}</Styled.Title>}
|
||||||
|
onClick={() => dispatch(openDialog({ type: DialogTypes.ExternalNavKeplr }))}
|
||||||
|
>
|
||||||
|
<Styled.Row>
|
||||||
|
<Styled.Description>
|
||||||
|
{stringGetter({ key: STRING_KEYS.STAKING_DESCRIPTION })}
|
||||||
|
<Link href={STAKING_HELP_URL} onClick={(e) => e.stopPropagation()}>
|
||||||
|
{stringGetter({ key: STRING_KEYS.LEARN_MORE })} →
|
||||||
|
</Link>
|
||||||
|
</Styled.Description>
|
||||||
|
{/* TODO: vertically center based on Panel height */}
|
||||||
|
<Styled.IconButton
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
iconName={IconName.Arrow}
|
||||||
|
size={ButtonSize.Small}
|
||||||
|
/>
|
||||||
|
</Styled.Row>
|
||||||
|
</Styled.Panel>
|
||||||
|
|
||||||
|
{isNotTablet && (
|
||||||
|
<Styled.BalancePanelContainer>
|
||||||
|
<DYDXBalancePanel />
|
||||||
|
</Styled.BalancePanelContainer>
|
||||||
|
)}
|
||||||
|
</Styled.PanelRow>
|
||||||
|
</Styled.Page>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Styled: Record<string, AnyStyledComponent> = {};
|
const Styled: Record<string, AnyStyledComponent> = {};
|
||||||
|
|
||||||
Styled.Page = styled.div`
|
Styled.Page = styled.div`
|
||||||
${layoutMixins.centered}
|
${layoutMixins.contentContainerPage}
|
||||||
|
gap: 1.5rem;
|
||||||
|
|
||||||
|
@media ${breakpoints.tablet} {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Panel = styled(Panel)`
|
||||||
|
padding: 0 1.5rem 1rem;
|
||||||
|
|
||||||
|
@media ${breakpoints.tablet} {
|
||||||
|
max-width: calc(100vw - 2rem);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Row = styled.div`
|
||||||
|
${layoutMixins.spacedRow}
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Description = styled.div`
|
||||||
|
color: var(--color-text-0);
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Migrate = styled.section`
|
||||||
|
max-width: min(100vw, var(--content-max-width));
|
||||||
|
|
||||||
|
padding: 1.5rem;
|
||||||
|
|
||||||
|
background-color: var(--color-layer-3);
|
||||||
|
border-radius: 0.875rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.TwoItemRow = styled(Styled.Row)`
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.MigrateAction = styled(Styled.TwoItemRow)`
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
background-color: var(--color-layer-2);
|
||||||
|
border: solid var(--border-width) var(--color-border);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Token = styled(Output)`
|
||||||
|
font: var(--font-large-book);
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.PanelRow = styled(Styled.Row)`
|
||||||
|
gap: 1.5rem;
|
||||||
|
max-width: min(100vw, var(--content-max-width));
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
> section {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media ${breakpoints.tablet} {
|
||||||
|
grid-auto-flow: row;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
max-width: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.BalancePanelContainer = styled.div`
|
||||||
|
width: 21.25rem;
|
||||||
|
|
||||||
|
@media ${breakpoints.tablet} {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Title = styled.h3`
|
||||||
|
${layoutMixins.inlineRow}
|
||||||
|
padding: 1.25rem 1.5rem 0.5rem;
|
||||||
|
|
||||||
|
font: var(--font-medium-book);
|
||||||
|
color: var(--color-text-2);
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.MigrateTitle = styled(Styled.Title)`
|
||||||
|
padding: 0 0 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.MobileMigrateCard = styled(Styled.Panel)`
|
||||||
|
${layoutMixins.flexColumn}
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.MobileMigrateHeader = styled(Styled.Title)`
|
||||||
|
${layoutMixins.inlineRow}
|
||||||
|
gap: 1ch;
|
||||||
|
padding-bottom: 1rem;
|
||||||
|
|
||||||
|
font: var(--font-small-book);
|
||||||
|
color: var(--color-text-0);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
${layoutMixins.inlineRow}
|
||||||
|
font: var(--font-large-book);
|
||||||
|
color: var(--color-text-2);
|
||||||
|
|
||||||
|
svg {
|
||||||
|
font-size: 1.75rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
b {
|
||||||
|
font-weight: var(--fontWeight-book);
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Details = styled(Details)`
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.WithReceipt = styled(WithReceipt)`
|
||||||
|
width: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.LearnMore = styled(Styled.Description)`
|
||||||
|
${layoutMixins.row}
|
||||||
|
gap: 1ch;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.IconButton = styled(IconButton)`
|
||||||
|
color: var(--color-text-0);
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export type AccountState = {
|
|||||||
walletType?: WalletType;
|
walletType?: WalletType;
|
||||||
historicalPnlPeriod?: HistoricalPnlPeriods;
|
historicalPnlPeriod?: HistoricalPnlPeriods;
|
||||||
balances?: Record<string, AccountBalance>;
|
balances?: Record<string, AccountBalance>;
|
||||||
|
stakingBalances?: Record<string, AccountBalance>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: AccountState = {
|
const initialState: AccountState = {
|
||||||
@@ -154,6 +155,9 @@ export const accountSlice = createSlice({
|
|||||||
setBalances: (state, action: PayloadAction<Record<string, AccountBalance>>) => {
|
setBalances: (state, action: PayloadAction<Record<string, AccountBalance>>) => {
|
||||||
state.balances = action.payload;
|
state.balances = action.payload;
|
||||||
},
|
},
|
||||||
|
setStakingBalances: (state, action: PayloadAction<Record<string, AccountBalance>>) => {
|
||||||
|
state.stakingBalances = action.payload;
|
||||||
|
},
|
||||||
addUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
addUncommittedOrderClientId: (state, action: PayloadAction<number>) => {
|
||||||
state.uncommittedOrderClientIds.push(action.payload);
|
state.uncommittedOrderClientIds.push(action.payload);
|
||||||
},
|
},
|
||||||
@@ -179,6 +183,7 @@ export const {
|
|||||||
viewedFills,
|
viewedFills,
|
||||||
viewedOrders,
|
viewedOrders,
|
||||||
setBalances,
|
setBalances,
|
||||||
|
setStakingBalances,
|
||||||
addUncommittedOrderClientId,
|
addUncommittedOrderClientId,
|
||||||
removeUncommittedOrderClientId,
|
removeUncommittedOrderClientId,
|
||||||
} = accountSlice.actions;
|
} = accountSlice.actions;
|
||||||
|
|||||||
@@ -337,3 +337,8 @@ export const getUserStats = (state: RootState) => ({
|
|||||||
* @returns user wallet balances
|
* @returns user wallet balances
|
||||||
*/
|
*/
|
||||||
export const getBalances = (state: RootState) => state.account?.balances;
|
export const getBalances = (state: RootState) => state.account?.balances;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns user wallet staking balances
|
||||||
|
* */
|
||||||
|
export const getStakingBalances = (state: RootState) => state.account?.stakingBalances;
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
|
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
|
||||||
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { useBreakpoints, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
|
import { Button } from '@/components/Button';
|
||||||
|
import { Dialog, DialogPlacement } from '@/components/Dialog';
|
||||||
|
import { IconName } from '@/components/Icon';
|
||||||
|
import { IconButton } from '@/components/IconButton';
|
||||||
|
|
||||||
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
|
type ElementProps = {
|
||||||
|
setIsOpen: (open: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: replace placeholder URL with real URLs when avaialble
|
||||||
|
const KEPLR_DASHBOARD_URL = 'https://testnet.keplr.app/';
|
||||||
|
const HELP_URL = 'https://help.dydx.exchange/en/articles/2921366-how-do-i-create-an-account-or-sign-up';
|
||||||
|
|
||||||
|
export const ExternalNavKeplrDialog = ({ setIsOpen }: ElementProps) => {
|
||||||
|
const stringGetter = useStringGetter();
|
||||||
|
const { isTablet } = useBreakpoints();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
isOpen
|
||||||
|
setIsOpen={setIsOpen}
|
||||||
|
title={stringGetter({ key: STRING_KEYS.HAVE_YOU_EXPORTED })}
|
||||||
|
placement={isTablet ? DialogPlacement.FullScreen : DialogPlacement.Default}
|
||||||
|
>
|
||||||
|
<Styled.Content>
|
||||||
|
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={KEPLR_DASHBOARD_URL}>
|
||||||
|
<span>
|
||||||
|
{stringGetter({
|
||||||
|
key: STRING_KEYS.NAVIGATE_TO_KEPLR,
|
||||||
|
params: {
|
||||||
|
STRONG_YES: <strong>{stringGetter({ key: STRING_KEYS.YES })}</strong>,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Styled.IconButton
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
iconName={IconName.Arrow}
|
||||||
|
size={ButtonSize.XSmall}
|
||||||
|
/>
|
||||||
|
</Styled.Button>
|
||||||
|
|
||||||
|
<Styled.Button type={ButtonType.Link} size={ButtonSize.XLarge} href={HELP_URL}>
|
||||||
|
<span>
|
||||||
|
{stringGetter({
|
||||||
|
key: STRING_KEYS.LEARN_TO_EXPORT,
|
||||||
|
params: {
|
||||||
|
STRONG_NO: <strong>{stringGetter({ key: STRING_KEYS.NO })}</strong>,
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<Styled.IconButton
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
iconName={IconName.Arrow}
|
||||||
|
size={ButtonSize.XSmall}
|
||||||
|
/>
|
||||||
|
</Styled.Button>
|
||||||
|
</Styled.Content>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Styled: Record<string, AnyStyledComponent> = {};
|
||||||
|
|
||||||
|
Styled.TextToggle = styled.div`
|
||||||
|
${layoutMixins.stickyFooter}
|
||||||
|
color: var(--color-accent);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
margin-top: auto;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Content = styled.div`
|
||||||
|
${layoutMixins.stickyArea0}
|
||||||
|
--stickyArea0-bottomHeight: 2rem;
|
||||||
|
--stickyArea0-bottomGap: 1rem;
|
||||||
|
--stickyArea0-totalInsetBottom: 0.5rem;
|
||||||
|
|
||||||
|
${layoutMixins.flexColumn}
|
||||||
|
gap: 1rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Button = styled(Button)`
|
||||||
|
--button-font: var(--font-base-book);
|
||||||
|
--button-padding: 0 1.5rem;
|
||||||
|
|
||||||
|
gap: 0;
|
||||||
|
|
||||||
|
justify-content: space-between;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.IconButton = styled(IconButton)`
|
||||||
|
color: var(--color-text-0);
|
||||||
|
`;
|
||||||
Reference in New Issue
Block a user