Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0189fac442 | ||
|
|
610c583246 | ||
|
|
d4bcfc0428 | ||
|
|
7dec32df5e | ||
|
|
e09d574d5b | ||
|
|
944fc6dc95 | ||
|
|
91a97a1c68 |
+1
-1
@@ -39,7 +39,7 @@
|
|||||||
"@cosmjs/proto-signing": "^0.31.0",
|
"@cosmjs/proto-signing": "^0.31.0",
|
||||||
"@cosmjs/stargate": "^0.31.0",
|
"@cosmjs/stargate": "^0.31.0",
|
||||||
"@cosmjs/tendermint-rpc": "^0.31.0",
|
"@cosmjs/tendermint-rpc": "^0.31.0",
|
||||||
"@dydxprotocol/v4-abacus": "^1.0.19",
|
"@dydxprotocol/v4-abacus": "^1.0.24",
|
||||||
"@dydxprotocol/v4-client-js": "^1.0.0",
|
"@dydxprotocol/v4-client-js": "^1.0.0",
|
||||||
"@dydxprotocol/v4-localization": "^1.0.5",
|
"@dydxprotocol/v4-localization": "^1.0.5",
|
||||||
"@ethersproject/providers": "^5.7.2",
|
"@ethersproject/providers": "^5.7.2",
|
||||||
|
|||||||
Generated
+4
-4
@@ -27,8 +27,8 @@ dependencies:
|
|||||||
specifier: ^0.31.0
|
specifier: ^0.31.0
|
||||||
version: 0.31.0
|
version: 0.31.0
|
||||||
'@dydxprotocol/v4-abacus':
|
'@dydxprotocol/v4-abacus':
|
||||||
specifier: ^1.0.19
|
specifier: ^1.0.24
|
||||||
version: 1.0.19
|
version: 1.0.24
|
||||||
'@dydxprotocol/v4-client-js':
|
'@dydxprotocol/v4-client-js':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
@@ -982,8 +982,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
resolution: {integrity: sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@dydxprotocol/v4-abacus@1.0.19:
|
/@dydxprotocol/v4-abacus@1.0.24:
|
||||||
resolution: {integrity: sha512-XhvSHGr503gNwHWEiOYP7M2uYeLu+Qm4szpE5w5H6hWFCanSGz6zAsTbPuyPLtt7IE1gj5z8mlL9J3tw2AZROg==}
|
resolution: {integrity: sha512-wDGSjkrc3Se6Ev7UTjPgJV7PiyzZSz2mJwOTZikLwH7W3k1iPYGQmaCd7TudFk8h2aSdlEwNsQBCf0sLoyvHaQ==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@dydxprotocol/v4-client-js@1.0.0:
|
/@dydxprotocol/v4-client-js@1.0.0:
|
||||||
|
|||||||
@@ -439,6 +439,7 @@
|
|||||||
"tos": "https://dydx.exchange/v4-terms",
|
"tos": "https://dydx.exchange/v4-terms",
|
||||||
"privacy": "https://dydx.exchange/privacy",
|
"privacy": "https://dydx.exchange/privacy",
|
||||||
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
"mintscan": "https://testnet.mintscan.io/dydx-testnet/txs/{tx_hash}",
|
||||||
|
"mintscanBase": "https://testnet.mintscan.io/dydx-testnet",
|
||||||
"documentation": "https://v4-teacher.vercel.app/",
|
"documentation": "https://v4-teacher.vercel.app/",
|
||||||
"community": "https://discord.com/invite/dydx",
|
"community": "https://discord.com/invite/dydx",
|
||||||
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
"feedback": "https://docs.google.com/forms/d/e/1FAIpQLSezLsWCKvAYDEb7L-2O4wOON1T56xxro9A2Azvl6IxXHP_15Q/viewform",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import styled, { type AnyStyledComponent, css } from 'styled-components';
|
import styled, { type AnyStyledComponent, css } from 'styled-components';
|
||||||
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
@@ -22,6 +22,11 @@ export const SearchInput = ({
|
|||||||
}: SearchInputProps) => {
|
}: SearchInputProps) => {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) inputRef?.current?.focus();
|
||||||
|
}, [inputRef, isOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.Search>
|
<Styled.Search>
|
||||||
@@ -41,6 +46,7 @@ export const SearchInput = ({
|
|||||||
/>
|
/>
|
||||||
<Styled.Input
|
<Styled.Input
|
||||||
autoFocus
|
autoFocus
|
||||||
|
ref={inputRef}
|
||||||
value={value}
|
value={value}
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
type={type}
|
type={type}
|
||||||
|
|||||||
@@ -831,6 +831,7 @@ Styled.SortArrow = styled.span<{ sortDirection: 'ascending' | 'descending' }>`
|
|||||||
Styled.Thead = styled.thead<StyleProps>`
|
Styled.Thead = styled.thead<StyleProps>`
|
||||||
${layoutMixins.stickyHeader}
|
${layoutMixins.stickyHeader}
|
||||||
scroll-snap-align: none;
|
scroll-snap-align: none;
|
||||||
|
font: var(--font-mini-book);
|
||||||
|
|
||||||
> * {
|
> * {
|
||||||
height: var(--stickyArea-topHeight);
|
height: var(--stickyArea-topHeight);
|
||||||
@@ -839,18 +840,17 @@ Styled.Thead = styled.thead<StyleProps>`
|
|||||||
color: var(--tableHeader-textColor);
|
color: var(--tableHeader-textColor);
|
||||||
background-color: var(--tableHeader-backgroundColor);
|
background-color: var(--tableHeader-backgroundColor);
|
||||||
|
|
||||||
@media ${breakpoints.notTablet} {
|
${({ withInnerBorders, withGradientCardRows }) =>
|
||||||
${({ withInnerBorders, withGradientCardRows }) =>
|
withInnerBorders &&
|
||||||
withInnerBorders &&
|
!withGradientCardRows &&
|
||||||
!withGradientCardRows &&
|
css`
|
||||||
css`
|
${layoutMixins.withInnerHorizontalBorders}
|
||||||
${layoutMixins.withInnerHorizontalBorders}
|
`}
|
||||||
`}
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
Styled.Tbody = styled.tbody<StyleProps>`
|
Styled.Tbody = styled.tbody<StyleProps>`
|
||||||
${layoutMixins.stickyArea2}
|
${layoutMixins.stickyArea2}
|
||||||
|
font: var(--font-small-book);
|
||||||
|
|
||||||
// If <table> height is fixed with not enough rows to overflow, vertically center the rows
|
// If <table> height is fixed with not enough rows to overflow, vertically center the rows
|
||||||
&:before,
|
&:before,
|
||||||
@@ -869,6 +869,10 @@ Styled.Tbody = styled.tbody<StyleProps>`
|
|||||||
--stickyArea2-paddingBottom: var(--border-width);
|
--stickyArea2-paddingBottom: var(--border-width);
|
||||||
--stickyArea2-paddingLeft: var(--border-width);
|
--stickyArea2-paddingLeft: var(--border-width);
|
||||||
--stickyArea2-paddingRight: var(--border-width);
|
--stickyArea2-paddingRight: var(--border-width);
|
||||||
|
|
||||||
|
tr:first-of-type {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
`}
|
`}
|
||||||
|
|
||||||
${({ withGradientCardRows }) =>
|
${({ withGradientCardRows }) =>
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ const useAccountsContext = () => {
|
|||||||
getSubaccounts: async ({ dydxAddress }: { dydxAddress: DydxAddress }) => {
|
getSubaccounts: async ({ dydxAddress }: { dydxAddress: DydxAddress }) => {
|
||||||
try {
|
try {
|
||||||
const response = await compositeClient?.indexerClient.account.getSubaccounts(dydxAddress);
|
const response = await compositeClient?.indexerClient.account.getSubaccounts(dydxAddress);
|
||||||
setDydxSubaccounts(response.subaccounts);
|
setDydxSubaccounts(response?.subaccounts);
|
||||||
return response.subaccounts;
|
return response?.subaccounts ?? [];
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 404 is expected if the user has no subaccounts
|
// 404 is expected if the user has no subaccounts
|
||||||
if (error.status === 404) {
|
if (error.status === 404) {
|
||||||
|
|||||||
@@ -66,7 +66,16 @@ const useLocalNotificationsContext = () => {
|
|||||||
status: currentStatus,
|
status: currentStatus,
|
||||||
} of transferNotifications) {
|
} of transferNotifications) {
|
||||||
try {
|
try {
|
||||||
if (currentStatus && currentStatus?.squidTransactionStatus !== 'ongoing') continue;
|
// skip if error is returned or if the transaction is not ongoing
|
||||||
|
if (
|
||||||
|
// @ts-ignore status.errors is not in the type definition but can be returned
|
||||||
|
currentStatus?.errors ||
|
||||||
|
currentStatus?.error ||
|
||||||
|
(currentStatus?.squidTransactionStatus &&
|
||||||
|
currentStatus?.squidTransactionStatus !== 'ongoing')
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
|
const status = await squid?.getStatus({ transactionId: txHash, toChainId, fromChainId });
|
||||||
if (status) statuses[txHash] = status;
|
if (status) statuses[txHash] = status;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { getSelectedNetwork } from '@/state/appSelectors';
|
|||||||
|
|
||||||
export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
export const NATIVE_TOKEN_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
|
||||||
|
|
||||||
export const STATUS_ERROR_GRACE_PERIOD = 120_000;
|
export const STATUS_ERROR_GRACE_PERIOD = 300_000;
|
||||||
|
|
||||||
const useSquidContext = () => {
|
const useSquidContext = () => {
|
||||||
const selectedNetwork = useSelector(getSelectedNetwork);
|
const selectedNetwork = useSelector(getSelectedNetwork);
|
||||||
|
|||||||
@@ -200,10 +200,9 @@ class DydxChainTransactions implements AbacusDYDXChainTransactionsProtocol {
|
|||||||
|
|
||||||
if (isTestnet) {
|
if (isTestnet) {
|
||||||
console.log(
|
console.log(
|
||||||
`${
|
`${ENVIRONMENT_CONFIG_MAP[
|
||||||
ENVIRONMENT_CONFIG_MAP[this.compositeClient.network.getString() as DydxNetwork]?.links
|
this.compositeClient.network.getString() as DydxNetwork
|
||||||
?.mintscanBase
|
]?.links?.mintscan?.replace('{tx_hash}', hash.toString())}`
|
||||||
}/txs/${hash}`
|
|
||||||
);
|
);
|
||||||
} else console.log(`txHash: ${hash}`);
|
} else console.log(`txHash: ${hash}`);
|
||||||
|
|
||||||
|
|||||||
@@ -56,12 +56,7 @@ class AbacusRest implements AbacusRestProtocol {
|
|||||||
.then(async (response) => {
|
.then(async (response) => {
|
||||||
const data = await response.text();
|
const data = await response.text();
|
||||||
|
|
||||||
if (response.ok) {
|
callback(data, response.status);
|
||||||
callback(data, response.status);
|
|
||||||
} else {
|
|
||||||
// response not OK, call callback with null data and the status, this includes 400/500 status codes
|
|
||||||
callback(null, response.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
lastSuccessfulRestRequestByOrigin[new URL(url).origin] = Date.now();
|
lastSuccessfulRestRequestByOrigin[new URL(url).origin] = Date.now();
|
||||||
|
|||||||
@@ -304,10 +304,6 @@ Styled.FeeTable = styled(Table)`
|
|||||||
|
|
||||||
@media ${breakpoints.notTablet} {
|
@media ${breakpoints.notTablet} {
|
||||||
--tableHeader-backgroundColor: var(--color-layer-1);
|
--tableHeader-backgroundColor: var(--color-layer-1);
|
||||||
|
|
||||||
thead tr {
|
|
||||||
--border-width: 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import { useEffect } from 'react';
|
import { shallowEqual, useDispatch, useSelector } from 'react-redux';
|
||||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
import { Navigate, Route, Routes } from 'react-router-dom';
|
import { Navigate, Route, Routes } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { OnboardingState } from '@/constants/account';
|
||||||
|
import { DialogTypes } from '@/constants/dialogs';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
import { HistoryRoute, PortfolioRoute } from '@/constants/routes';
|
import { HistoryRoute, PortfolioRoute } from '@/constants/routes';
|
||||||
|
|
||||||
import { useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks';
|
import { useAccountBalance, useBreakpoints, useDocumentTitle, useStringGetter } from '@/hooks';
|
||||||
|
|
||||||
import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
|
import { FillsTable, FillsTableColumnKey } from '@/views/tables/FillsTable';
|
||||||
import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable';
|
import { FundingPaymentsTable } from '@/views/tables/FundingPaymentsTable';
|
||||||
@@ -14,6 +16,9 @@ import { Icon, IconName } from '@/components/Icon';
|
|||||||
import { NavigationMenu } from '@/components/NavigationMenu';
|
import { NavigationMenu } from '@/components/NavigationMenu';
|
||||||
import { WithSidebar } from '@/components/WithSidebar';
|
import { WithSidebar } from '@/components/WithSidebar';
|
||||||
|
|
||||||
|
import { getOnboardingState, getSubaccount } from '@/state/accountSelectors';
|
||||||
|
import { openDialog } from '@/state/dialogs';
|
||||||
|
|
||||||
import { PortfolioNavMobile } from './PortfolioNavMobile';
|
import { PortfolioNavMobile } from './PortfolioNavMobile';
|
||||||
import { Overview } from './Overview';
|
import { Overview } from './Overview';
|
||||||
import { Positions } from './Positions';
|
import { Positions } from './Positions';
|
||||||
@@ -22,11 +27,20 @@ import { Fees } from './Fees';
|
|||||||
import { History } from './History';
|
import { History } from './History';
|
||||||
|
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
import { Button } from '@/components/Button';
|
||||||
|
import { ButtonAction } from '@/constants/buttons';
|
||||||
|
|
||||||
export default () => {
|
export default () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
const { isTablet, isNotTablet } = useBreakpoints();
|
const { isTablet, isNotTablet } = useBreakpoints();
|
||||||
|
|
||||||
|
const onboardingState = useSelector(getOnboardingState);
|
||||||
|
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
|
||||||
|
const { nativeTokenBalance } = useAccountBalance();
|
||||||
|
|
||||||
|
const usdcBalance = freeCollateral?.current || 0;
|
||||||
|
|
||||||
useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO }));
|
useDocumentTitle(stringGetter({ key: STRING_KEYS.PORTFOLIO }));
|
||||||
|
|
||||||
const routesComponent = (
|
const routesComponent = (
|
||||||
@@ -84,46 +98,74 @@ export default () => {
|
|||||||
<WithSidebar
|
<WithSidebar
|
||||||
sidebar={
|
sidebar={
|
||||||
isTablet ? null : (
|
isTablet ? null : (
|
||||||
<Styled.NavigationMenu
|
<Styled.SideBar>
|
||||||
items={[
|
<Styled.NavigationMenu
|
||||||
{
|
items={[
|
||||||
group: 'views',
|
{
|
||||||
groupLabel: stringGetter({ key: STRING_KEYS.VIEWS }),
|
group: 'views',
|
||||||
items: [
|
groupLabel: stringGetter({ key: STRING_KEYS.VIEWS }),
|
||||||
{
|
items: [
|
||||||
value: PortfolioRoute.Overview,
|
{
|
||||||
slotBefore: <Styled.Icon iconName={IconName.Overview} />,
|
value: PortfolioRoute.Overview,
|
||||||
label: stringGetter({ key: STRING_KEYS.OVERVIEW }),
|
slotBefore: <Styled.Icon iconName={IconName.Overview} />,
|
||||||
href: PortfolioRoute.Overview,
|
label: stringGetter({ key: STRING_KEYS.OVERVIEW }),
|
||||||
},
|
href: PortfolioRoute.Overview,
|
||||||
{
|
},
|
||||||
value: PortfolioRoute.Positions,
|
{
|
||||||
slotBefore: <Styled.Icon iconName={IconName.Cube} />,
|
value: PortfolioRoute.Positions,
|
||||||
label: stringGetter({ key: STRING_KEYS.POSITIONS }),
|
slotBefore: <Styled.Icon iconName={IconName.Cube} />,
|
||||||
href: PortfolioRoute.Positions,
|
label: stringGetter({ key: STRING_KEYS.POSITIONS }),
|
||||||
},
|
href: PortfolioRoute.Positions,
|
||||||
{
|
},
|
||||||
value: PortfolioRoute.Orders,
|
{
|
||||||
slotBefore: <Styled.Icon iconName={IconName.OrderPending} />,
|
value: PortfolioRoute.Orders,
|
||||||
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
slotBefore: <Styled.Icon iconName={IconName.OrderPending} />,
|
||||||
href: PortfolioRoute.Orders,
|
label: stringGetter({ key: STRING_KEYS.ORDERS }),
|
||||||
},
|
href: PortfolioRoute.Orders,
|
||||||
{
|
},
|
||||||
value: PortfolioRoute.Fees,
|
{
|
||||||
slotBefore: <Styled.Icon iconName={IconName.Calculator} />,
|
value: PortfolioRoute.Fees,
|
||||||
label: stringGetter({ key: STRING_KEYS.FEES }),
|
slotBefore: <Styled.Icon iconName={IconName.Calculator} />,
|
||||||
href: PortfolioRoute.Fees,
|
label: stringGetter({ key: STRING_KEYS.FEES }),
|
||||||
},
|
href: PortfolioRoute.Fees,
|
||||||
{
|
},
|
||||||
value: PortfolioRoute.History,
|
{
|
||||||
slotBefore: <Styled.Icon iconName={IconName.History} />,
|
value: PortfolioRoute.History,
|
||||||
label: stringGetter({ key: STRING_KEYS.HISTORY }),
|
slotBefore: <Styled.Icon iconName={IconName.History} />,
|
||||||
href: PortfolioRoute.History,
|
label: stringGetter({ key: STRING_KEYS.HISTORY }),
|
||||||
},
|
href: PortfolioRoute.History,
|
||||||
],
|
},
|
||||||
},
|
],
|
||||||
]}
|
},
|
||||||
/>
|
]}
|
||||||
|
/>
|
||||||
|
{onboardingState === OnboardingState.AccountConnected && (
|
||||||
|
<Styled.Footer>
|
||||||
|
<Button
|
||||||
|
action={ButtonAction.Primary}
|
||||||
|
onClick={() => dispatch(openDialog({ type: DialogTypes.Deposit }))}
|
||||||
|
>
|
||||||
|
{stringGetter({ key: STRING_KEYS.DEPOSIT })}
|
||||||
|
</Button>
|
||||||
|
{usdcBalance > 0 && (
|
||||||
|
<Button
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
onClick={() => dispatch(openDialog({ type: DialogTypes.Withdraw }))}
|
||||||
|
>
|
||||||
|
{stringGetter({ key: STRING_KEYS.WITHDRAW })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{(usdcBalance > 0 || nativeTokenBalance.gt(0)) && (
|
||||||
|
<Button
|
||||||
|
action={ButtonAction.Base}
|
||||||
|
onClick={() => dispatch(openDialog({ type: DialogTypes.Transfer }))}
|
||||||
|
>
|
||||||
|
{stringGetter({ key: STRING_KEYS.TRANSFER })}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Styled.Footer>
|
||||||
|
)}
|
||||||
|
</Styled.SideBar>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
@@ -139,6 +181,26 @@ Styled.PortfolioMobile = styled.div`
|
|||||||
${layoutMixins.expandingColumnWithHeader}
|
${layoutMixins.expandingColumnWithHeader}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
Styled.SideBar = styled.div`
|
||||||
|
${layoutMixins.flexColumn}
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
Styled.Footer = styled.div`
|
||||||
|
${layoutMixins.row}
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
padding: 1rem;
|
||||||
|
|
||||||
|
gap: 0.5rem;
|
||||||
|
|
||||||
|
> button {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
Styled.NavigationMenu = styled(NavigationMenu)`
|
Styled.NavigationMenu = styled(NavigationMenu)`
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
|
|||||||
@@ -43,23 +43,14 @@ export const DYDXBalancePanel = () => {
|
|||||||
{!canAccountTrade ? (
|
{!canAccountTrade ? (
|
||||||
<OnboardingTriggerButton size={ButtonSize.Small} />
|
<OnboardingTriggerButton size={ButtonSize.Small} />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<Button
|
||||||
<Styled.ReceiveButton
|
slotLeft={<Icon iconName={IconName.Send} />}
|
||||||
slotLeft={<Icon iconName={IconName.Qr} />}
|
size={ButtonSize.Small}
|
||||||
size={ButtonSize.Small}
|
action={ButtonAction.Primary}
|
||||||
onClick={() => dispatch(openDialog({ type: DialogTypes.Receive }))}
|
onClick={() => dispatch(openDialog({ type: DialogTypes.Transfer }))}
|
||||||
>
|
>
|
||||||
{stringGetter({ key: STRING_KEYS.RECEIVE })}
|
{stringGetter({ key: STRING_KEYS.TRANSFER })}
|
||||||
</Styled.ReceiveButton>
|
</Button>
|
||||||
<Button
|
|
||||||
slotLeft={<Icon iconName={IconName.Send} />}
|
|
||||||
size={ButtonSize.Small}
|
|
||||||
action={ButtonAction.Primary}
|
|
||||||
onClick={() => dispatch(openDialog({ type: DialogTypes.Transfer }))}
|
|
||||||
>
|
|
||||||
{stringGetter({ key: STRING_KEYS.TRANSFER })}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</Styled.ReceiveAndTransferButtons>
|
</Styled.ReceiveAndTransferButtons>
|
||||||
</Styled.Header>
|
</Styled.Header>
|
||||||
|
|||||||
@@ -18,9 +18,5 @@ export const tradeViewMixins: Record<
|
|||||||
tbody {
|
tbody {
|
||||||
font: var(--font-small-book);
|
font: var(--font-small-book);
|
||||||
}
|
}
|
||||||
|
|
||||||
thead tr {
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -82,18 +82,18 @@ const MarketsDropdownContent = ({ onRowAction }: { onRowAction?: (market: string
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
columnKey: 'priceChange24H',
|
columnKey: 'priceChange24HPercent',
|
||||||
getCellValue: (row) => row.priceChange24H,
|
getCellValue: (row) => row.priceChange24HPercent,
|
||||||
label: stringGetter({ key: STRING_KEYS._24H }),
|
label: stringGetter({ key: STRING_KEYS._24H }),
|
||||||
renderCell: ({ priceChange24H, priceChange24HPercent }) => (
|
renderCell: ({ priceChange24HPercent }) => (
|
||||||
<Styled.InlineRow>
|
<Styled.InlineRow>
|
||||||
{!priceChange24H ? (
|
{!priceChange24HPercent ? (
|
||||||
<Styled.Output type={OutputType.Text} value={null} />
|
<Styled.Output type={OutputType.Text} value={null} />
|
||||||
) : (
|
) : (
|
||||||
<Styled.PriceChangeOutput
|
<Styled.PriceChangeOutput
|
||||||
type={OutputType.Percent}
|
type={OutputType.Percent}
|
||||||
value={priceChange24HPercent}
|
value={priceChange24HPercent}
|
||||||
isNegative={MustBigNumber(priceChange24H).isNegative()}
|
isNegative={MustBigNumber(priceChange24HPercent).isNegative()}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</Styled.InlineRow>
|
</Styled.InlineRow>
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ export const TransferStatusSteps = ({ status, type }: ElementProps) => {
|
|||||||
type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL,
|
type === 'deposit' ? STRING_KEYS.INITIATED_DEPOSIT : STRING_KEYS.INITIATED_WITHDRAWAL,
|
||||||
}),
|
}),
|
||||||
step: TransferStatusStep.FromChain,
|
step: TransferStatusStep.FromChain,
|
||||||
link: status?.fromChain?.transactionUrl,
|
link:
|
||||||
|
type === 'deposit'
|
||||||
|
? status?.fromChain?.transactionUrl
|
||||||
|
: routeStatus?.[0]?.chainId === dydxChainId && routeStatus[0].txHash
|
||||||
|
? `${mintscanTxUrl?.replace('{tx_hash}', routeStatus[0].txHash.replace('0x', ''))}`
|
||||||
|
: undefined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: stringGetter({ key: STRING_KEYS.BRIDGING_TOKENS }),
|
label: stringGetter({ key: STRING_KEYS.BRIDGING_TOKENS }),
|
||||||
@@ -64,7 +69,7 @@ export const TransferStatusSteps = ({ status, type }: ElementProps) => {
|
|||||||
type === 'withdrawal'
|
type === 'withdrawal'
|
||||||
? status?.toChain?.transactionUrl
|
? status?.toChain?.transactionUrl
|
||||||
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash
|
: currentStatus?.chainId === dydxChainId && currentStatus?.txHash
|
||||||
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash)}`
|
? `${mintscanTxUrl?.replace('{tx_hash}', currentStatus.txHash.replace('0x', ''))}`
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import styled, { AnyStyledComponent, css } from 'styled-components';
|
|||||||
|
|
||||||
import { AnalyticsEvent } from '@/constants/analytics';
|
import { AnalyticsEvent } from '@/constants/analytics';
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { isMainnet } from '@/constants/networks';
|
||||||
import { EvmDerivedAccountStatus, OnboardingSteps } from '@/constants/account';
|
import { EvmDerivedAccountStatus, OnboardingSteps } from '@/constants/account';
|
||||||
import { wallets } from '@/constants/wallets';
|
import { wallets } from '@/constants/wallets';
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ import { track } from '@/lib/analytics';
|
|||||||
import { AcknowledgeTerms } from './OnboardingDialog/AcknowledgeTerms';
|
import { AcknowledgeTerms } from './OnboardingDialog/AcknowledgeTerms';
|
||||||
import { ChooseWallet } from './OnboardingDialog/ChooseWallet';
|
import { ChooseWallet } from './OnboardingDialog/ChooseWallet';
|
||||||
import { GenerateKeys } from './OnboardingDialog/GenerateKeys';
|
import { GenerateKeys } from './OnboardingDialog/GenerateKeys';
|
||||||
|
import { DepositForm } from '../forms/AccountManagementForms/DepositForm';
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
setIsOpen?: (open: boolean) => void;
|
setIsOpen?: (open: boolean) => void;
|
||||||
@@ -50,7 +52,7 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
|
|||||||
disconnect();
|
disconnect();
|
||||||
}
|
}
|
||||||
setIsOpen?.(open);
|
setIsOpen?.(open);
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.Dialog
|
<Styled.Dialog
|
||||||
@@ -82,10 +84,7 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
|
|||||||
description: stringGetter({ key: STRING_KEYS.SIGNATURE_CREATES_COSMOS_WALLET }),
|
description: stringGetter({ key: STRING_KEYS.SIGNATURE_CREATES_COSMOS_WALLET }),
|
||||||
children: (
|
children: (
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
<GenerateKeys
|
<GenerateKeys status={derivationStatus} setStatus={setDerivationStatus} />
|
||||||
status={derivationStatus}
|
|
||||||
setStatus={setDerivationStatus}
|
|
||||||
/>
|
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
),
|
),
|
||||||
width: '23rem',
|
width: '23rem',
|
||||||
@@ -101,14 +100,22 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
|
|||||||
},
|
},
|
||||||
[OnboardingSteps.DepositFunds]: {
|
[OnboardingSteps.DepositFunds]: {
|
||||||
title: stringGetter({ key: STRING_KEYS.DEPOSIT }),
|
title: stringGetter({ key: STRING_KEYS.DEPOSIT }),
|
||||||
description: 'Test funds will be sent directly to your dYdX account.',
|
description: !isMainnet && 'Test funds will be sent directly to your dYdX account.',
|
||||||
children: (
|
children: (
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
<TestnetDepositForm
|
{isMainnet ? (
|
||||||
onDeposit={() => {
|
<DepositForm
|
||||||
track(AnalyticsEvent.TransferFaucet);
|
onDeposit={() => {
|
||||||
}}
|
track(AnalyticsEvent.TransferDeposit);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<TestnetDepositForm
|
||||||
|
onDeposit={() => {
|
||||||
|
track(AnalyticsEvent.TransferFaucet);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { isDev } from '@/constants/networks';
|
||||||
import { useStringGetter } from '@/hooks';
|
import { useStringGetter } from '@/hooks';
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
import { Dialog } from '@/components/Dialog';
|
import { Dialog } from '@/components/Dialog';
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
|
import { NetworkSelectMenu } from '@/views/menus/NetworkSelectMenu';
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
preventClose?: boolean;
|
preventClose?: boolean;
|
||||||
@@ -25,6 +27,7 @@ export const RestrictedGeoDialog = ({ preventClose, setIsOpen }: ElementProps) =
|
|||||||
>
|
>
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
{stringGetter({ key: STRING_KEYS.REGION_NOT_PERMITTED_SUBTITLE })}
|
{stringGetter({ key: STRING_KEYS.REGION_NOT_PERMITTED_SUBTITLE })}
|
||||||
|
{isDev && <NetworkSelectMenu />}
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import styled, { AnyStyledComponent } from 'styled-components';
|
import styled, { AnyStyledComponent } from 'styled-components';
|
||||||
|
|
||||||
import { STRING_KEYS } from '@/constants/localization';
|
import { STRING_KEYS } from '@/constants/localization';
|
||||||
|
import { isDev } from '@/constants/networks';
|
||||||
import { useStringGetter } from '@/hooks';
|
import { useStringGetter } from '@/hooks';
|
||||||
import { layoutMixins } from '@/styles/layoutMixins';
|
import { layoutMixins } from '@/styles/layoutMixins';
|
||||||
|
|
||||||
import { Dialog } from '@/components/Dialog';
|
import { Dialog } from '@/components/Dialog';
|
||||||
import { Icon, IconName } from '@/components/Icon';
|
import { Icon, IconName } from '@/components/Icon';
|
||||||
|
import { NetworkSelectMenu } from '@/views/menus/NetworkSelectMenu';
|
||||||
|
|
||||||
type ElementProps = {
|
type ElementProps = {
|
||||||
preventClose?: boolean;
|
preventClose?: boolean;
|
||||||
@@ -25,6 +27,7 @@ export const RestrictedWalletDialog = ({ preventClose, setIsOpen }: ElementProps
|
|||||||
>
|
>
|
||||||
<Styled.Content>
|
<Styled.Content>
|
||||||
{stringGetter({ key: STRING_KEYS.REGION_NOT_PERMITTED_SUBTITLE })}
|
{stringGetter({ key: STRING_KEYS.REGION_NOT_PERMITTED_SUBTITLE })}
|
||||||
|
{isDev && <NetworkSelectMenu />}
|
||||||
</Styled.Content>
|
</Styled.Content>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react
|
|||||||
import styled, { type AnyStyledComponent } from 'styled-components';
|
import styled, { type AnyStyledComponent } from 'styled-components';
|
||||||
import { type NumberFormatValues } from 'react-number-format';
|
import { type NumberFormatValues } from 'react-number-format';
|
||||||
import { shallowEqual, useSelector } from 'react-redux';
|
import { shallowEqual, useSelector } from 'react-redux';
|
||||||
import { parseUnits } from 'viem'
|
import { parseUnits } from 'viem';
|
||||||
|
|
||||||
import erc20 from '@/abi/erc20.json';
|
import erc20 from '@/abi/erc20.json';
|
||||||
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
|
||||||
@@ -65,6 +65,8 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
chain: chainIdStr,
|
chain: chainIdStr,
|
||||||
resources,
|
resources,
|
||||||
summary,
|
summary,
|
||||||
|
errors: routeErrors,
|
||||||
|
errorMessage: routeErrorMessage,
|
||||||
} = useSelector(getTransferInputs, shallowEqual) || {};
|
} = useSelector(getTransferInputs, shallowEqual) || {};
|
||||||
const chainId = chainIdStr ? parseInt(chainIdStr) : undefined;
|
const chainId = chainIdStr ? parseInt(chainIdStr) : undefined;
|
||||||
|
|
||||||
@@ -97,7 +99,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hasInvalidInput =
|
const hasInvalidInput =
|
||||||
debouncedAmountBN.isNaN() || debouncedAmountBN.lte(0) || debouncedAmountBN.gte(balanceBN);
|
debouncedAmountBN.isNaN() || debouncedAmountBN.lte(0) || debouncedAmountBN.gt(balanceBN);
|
||||||
|
|
||||||
abacusStateManager.setTransferValue({
|
abacusStateManager.setTransferValue({
|
||||||
value: hasInvalidInput ? 0 : debouncedAmount,
|
value: hasInvalidInput ? 0 : debouncedAmount,
|
||||||
@@ -171,7 +173,8 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
|
|
||||||
const validateTokenApproval = useCallback(async () => {
|
const validateTokenApproval = useCallback(async () => {
|
||||||
if (!signerWagmi || !publicClientWagmi) throw new Error('Missing signer');
|
if (!signerWagmi || !publicClientWagmi) throw new Error('Missing signer');
|
||||||
if (!sourceToken?.address || !sourceToken.decimals) throw new Error('Missing source token address');
|
if (!sourceToken?.address || !sourceToken.decimals)
|
||||||
|
throw new Error('Missing source token address');
|
||||||
if (!sourceChain?.rpc) throw new Error('Missing source chain rpc');
|
if (!sourceChain?.rpc) throw new Error('Missing source chain rpc');
|
||||||
if (!requestPayload?.targetAddress) throw new Error('Missing target address');
|
if (!requestPayload?.targetAddress) throw new Error('Missing target address');
|
||||||
if (!requestPayload?.value) throw new Error('Missing transaction value');
|
if (!requestPayload?.value) throw new Error('Missing transaction value');
|
||||||
@@ -181,7 +184,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
address: sourceToken.address as EvmAddress,
|
address: sourceToken.address as EvmAddress,
|
||||||
abi: erc20,
|
abi: erc20,
|
||||||
functionName: 'allowance',
|
functionName: 'allowance',
|
||||||
args: [evmAddress as EvmAddress, requestPayload.targetAddress as EvmAddress]
|
args: [evmAddress as EvmAddress, requestPayload.targetAddress as EvmAddress],
|
||||||
});
|
});
|
||||||
|
|
||||||
const sourceAmountBN = parseUnits(debouncedAmount, sourceToken.decimals);
|
const sourceAmountBN = parseUnits(debouncedAmount, sourceToken.decimals);
|
||||||
@@ -193,12 +196,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
abi: erc20,
|
abi: erc20,
|
||||||
functionName: 'approve',
|
functionName: 'approve',
|
||||||
args: [requestPayload.targetAddress as EvmAddress, sourceAmountBN],
|
args: [requestPayload.targetAddress as EvmAddress, sourceAmountBN],
|
||||||
})
|
});
|
||||||
|
|
||||||
const approveTx = await signerWagmi.writeContract(request);
|
const approveTx = await signerWagmi.writeContract(request);
|
||||||
await publicClientWagmi.waitForTransactionReceipt({
|
await publicClientWagmi.waitForTransactionReceipt({
|
||||||
hash: approveTx,
|
hash: approveTx,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}, [signerWagmi, sourceToken, sourceChain, requestPayload, publicClientWagmi]);
|
}, [signerWagmi, sourceToken, sourceChain, requestPayload, publicClientWagmi]);
|
||||||
|
|
||||||
@@ -228,8 +231,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
to: requestPayload.targetAddress as EvmAddress,
|
to: requestPayload.targetAddress as EvmAddress,
|
||||||
data: requestPayload.data as EvmAddress,
|
data: requestPayload.data as EvmAddress,
|
||||||
gasLimit: BigInt(requestPayload.gasLimit),
|
gasLimit: BigInt(requestPayload.gasLimit),
|
||||||
value:
|
value: requestPayload.routeType !== 'SEND' ? BigInt(requestPayload.value) : undefined,
|
||||||
requestPayload.routeType !== 'SEND' ? BigInt(requestPayload.value) : undefined,
|
|
||||||
};
|
};
|
||||||
const txHash = await signerWagmi.sendTransaction(tx);
|
const txHash = await signerWagmi.sendTransaction(tx);
|
||||||
|
|
||||||
@@ -287,6 +289,15 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
return parseWalletError({ error, stringGetter }).message;
|
return parseWalletError({ error, stringGetter }).message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (routeErrors) {
|
||||||
|
return routeErrorMessage
|
||||||
|
? stringGetter({
|
||||||
|
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||||
|
params: { ERROR_MESSAGE: routeErrorMessage },
|
||||||
|
})
|
||||||
|
: stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG });
|
||||||
|
}
|
||||||
|
|
||||||
if (fromAmount) {
|
if (fromAmount) {
|
||||||
if (!chainId) {
|
if (!chainId) {
|
||||||
return stringGetter({ key: STRING_KEYS.MUST_SPECIFY_CHAIN });
|
return stringGetter({ key: STRING_KEYS.MUST_SPECIFY_CHAIN });
|
||||||
@@ -300,7 +311,16 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [error, balance, chainId, fromAmount, sourceToken]);
|
}, [
|
||||||
|
error,
|
||||||
|
routeErrors,
|
||||||
|
routeErrorMessage,
|
||||||
|
balance,
|
||||||
|
chainId,
|
||||||
|
fromAmount,
|
||||||
|
sourceToken,
|
||||||
|
stringGetter,
|
||||||
|
]);
|
||||||
|
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
Boolean(errorMessage) ||
|
Boolean(errorMessage) ||
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ export const DepositButtonAndReceipt = ({
|
|||||||
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
|
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
|
||||||
value: (
|
value: (
|
||||||
<SlippageEditor
|
<SlippageEditor
|
||||||
|
disabled
|
||||||
slippage={slippage}
|
slippage={slippage}
|
||||||
setIsEditing={setIsEditingSlipapge}
|
setIsEditing={setIsEditingSlipapge}
|
||||||
setSlippage={setSlippage}
|
setSlippage={setSlippage}
|
||||||
|
|||||||
@@ -24,11 +24,17 @@ type ElementProps = {
|
|||||||
slippage: number;
|
slippage: number;
|
||||||
setIsEditing?: Dispatch<SetStateAction<boolean>>;
|
setIsEditing?: Dispatch<SetStateAction<boolean>>;
|
||||||
setSlippage: (slippage: number) => void;
|
setSlippage: (slippage: number) => void;
|
||||||
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SlippageEditorProps = ElementProps;
|
export type SlippageEditorProps = ElementProps;
|
||||||
|
|
||||||
export const SlippageEditor = ({ slippage, setIsEditing, setSlippage }: SlippageEditorProps) => {
|
export const SlippageEditor = ({
|
||||||
|
disabled,
|
||||||
|
slippage,
|
||||||
|
setIsEditing,
|
||||||
|
setSlippage,
|
||||||
|
}: SlippageEditorProps) => {
|
||||||
const percentSlippage = slippage * 100;
|
const percentSlippage = slippage * 100;
|
||||||
const [slippageInputValue, setSlippageInputValue] = useState(percentSlippage.toString());
|
const [slippageInputValue, setSlippageInputValue] = useState(percentSlippage.toString());
|
||||||
const [editorState, setEditorState] = useState(EditorState.Viewing);
|
const [editorState, setEditorState] = useState(EditorState.Viewing);
|
||||||
@@ -80,6 +86,10 @@ export const SlippageEditor = ({ slippage, setIsEditing, setSlippage }: Slippage
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
return <Output type={OutputType.Percent} value={slippage} />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Styled.WithConfirmationPopover
|
<Styled.WithConfirmationPopover
|
||||||
open={editorState !== EditorState.Viewing}
|
open={editorState !== EditorState.Viewing}
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ import { MustBigNumber } from '@/lib/numbers';
|
|||||||
|
|
||||||
import { TokenSelectMenu } from './TokenSelectMenu';
|
import { TokenSelectMenu } from './TokenSelectMenu';
|
||||||
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
|
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
|
||||||
import { join } from 'path';
|
|
||||||
|
|
||||||
export const WithdrawForm = () => {
|
export const WithdrawForm = () => {
|
||||||
const stringGetter = useStringGetter();
|
const stringGetter = useStringGetter();
|
||||||
@@ -69,6 +68,8 @@ export const WithdrawForm = () => {
|
|||||||
chain: chainIdStr,
|
chain: chainIdStr,
|
||||||
address: toAddress,
|
address: toAddress,
|
||||||
resources,
|
resources,
|
||||||
|
errors: routeErrors,
|
||||||
|
errorMessage: routeErrorMessage,
|
||||||
} = useSelector(getTransferInputs, shallowEqual) || {};
|
} = useSelector(getTransferInputs, shallowEqual) || {};
|
||||||
|
|
||||||
const isValidAddress = toAddress && isAddress(toAddress);
|
const isValidAddress = toAddress && isAddress(toAddress);
|
||||||
@@ -281,6 +282,15 @@ export const WithdrawForm = () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (routeErrors) {
|
||||||
|
return routeErrorMessage
|
||||||
|
? stringGetter({
|
||||||
|
key: STRING_KEYS.SOMETHING_WENT_WRONG_WITH_MESSAGE,
|
||||||
|
params: { ERROR_MESSAGE: routeErrorMessage },
|
||||||
|
})
|
||||||
|
: stringGetter({ key: STRING_KEYS.SOMETHING_WENT_WRONG });
|
||||||
|
}
|
||||||
|
|
||||||
if (!toAddress) return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ADDRESS });
|
if (!toAddress) return stringGetter({ key: STRING_KEYS.WITHDRAW_MUST_SPECIFY_ADDRESS });
|
||||||
|
|
||||||
if (sanctionedAddresses.has(toAddress))
|
if (sanctionedAddresses.has(toAddress))
|
||||||
@@ -303,12 +313,15 @@ export const WithdrawForm = () => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}, [
|
}, [
|
||||||
error,
|
error,
|
||||||
|
routeErrors,
|
||||||
|
routeErrorMessage,
|
||||||
freeCollateralBN,
|
freeCollateralBN,
|
||||||
chainIdStr,
|
chainIdStr,
|
||||||
debouncedAmountBN,
|
debouncedAmountBN,
|
||||||
toToken,
|
toToken,
|
||||||
toAddress,
|
toAddress,
|
||||||
sanctionedAddresses,
|
sanctionedAddresses,
|
||||||
|
stringGetter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const isDisabled =
|
const isDisabled =
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export const WithdrawButtonAndReceipt = ({
|
|||||||
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
|
label: <span>{stringGetter({ key: STRING_KEYS.SLIPPAGE })}</span>,
|
||||||
value: (
|
value: (
|
||||||
<SlippageEditor
|
<SlippageEditor
|
||||||
|
disabled
|
||||||
slippage={slippage}
|
slippage={slippage}
|
||||||
setIsEditing={setIsEditingSlipapge}
|
setIsEditing={setIsEditingSlipapge}
|
||||||
setSlippage={setSlippage}
|
setSlippage={setSlippage}
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ const Styled: Record<string, AnyStyledComponent> = {};
|
|||||||
Styled.DropdownSelectMenu = styled(DropdownSelectMenu)`
|
Styled.DropdownSelectMenu = styled(DropdownSelectMenu)`
|
||||||
${headerMixins.dropdownTrigger}
|
${headerMixins.dropdownTrigger}
|
||||||
|
|
||||||
|
width: max-content;
|
||||||
|
|
||||||
& > span:first-of-type {
|
& > span:first-of-type {
|
||||||
${layoutMixins.textOverflow}
|
${layoutMixins.textOverflow}
|
||||||
max-width: 5.625rem;
|
max-width: 5.625rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user