Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cex withdraw #301

Merged
merged 6 commits into from
Feb 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@
"@cosmjs/proto-signing": "^0.32.1",
"@cosmjs/stargate": "^0.32.1",
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "^1.4.5",
"@dydxprotocol/v4-abacus": "^1.4.6",
"@dydxprotocol/v4-client-js": "^1.0.20",
"@dydxprotocol/v4-localization": "^1.1.30",
"@dydxprotocol/v4-localization": "^1.1.31",
"@ethersproject/providers": "^5.7.2",
"@js-joda/core": "^5.5.3",
"@radix-ui/react-accordion": "^1.1.2",
Expand Down
16 changes: 8 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions src/components/SearchSelectMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export const SearchSelectMenu = ({
disabled,
label,
items,
withSearch,
withSearch = true,
withReceiptItems,
}: SearchSelectMenuProps) => {
const [open, setOpen] = useState(false);
Expand Down Expand Up @@ -77,6 +77,7 @@ export const SearchSelectMenu = ({
withSearch={withSearch}
onItemSelected={() => setOpen(false)}
withStickyLayout
$withSearch={withSearch}
/>
</Styled.Popover>
</Styled.WithDetailsReceipt>
Expand Down Expand Up @@ -127,7 +128,7 @@ Styled.Popover = styled(Popover)`
box-shadow: none;
`;

Styled.ComboboxMenu = styled(ComboboxMenu)`
Styled.ComboboxMenu = styled(ComboboxMenu)<{ $withSearch?: boolean }>`
${layoutMixins.withInnerHorizontalBorders}

--comboboxMenu-backgroundColor: var(--color-layer-4);
Expand All @@ -140,7 +141,8 @@ Styled.ComboboxMenu = styled(ComboboxMenu)`
--comboboxMenu-item-checked-textColor: var(--color-text-2);
--comboboxMenu-item-highlighted-textColor: var(--color-text-2);

--stickyArea1-topHeight: var(--form-input-height);
--stickyArea1-topHeight: ${({ $withSearch }) =>
!$withSearch ? '0' : 'var(--form-input-height)'};

input:focus-visible {
outline: none;
Expand Down
9 changes: 8 additions & 1 deletion src/components/TimeoutButton.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactNode, useState } from 'react';
import { type ReactNode, useState, useEffect } from 'react';

import { ButtonAction, ButtonState } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
Expand All @@ -8,6 +8,7 @@ import { Button, type ButtonStateConfig, type ButtonProps } from '@/components/B

type ElementProps = {
timeoutInSeconds: number;
onTimeOut?: () => void;
slotFinal?: ReactNode;
} & ButtonProps;

Expand All @@ -16,6 +17,7 @@ export type TimeoutButtonProps = ElementProps;
export const TimeoutButton = ({
children,
timeoutInSeconds,
onTimeOut,
slotFinal,
...otherProps
}: TimeoutButtonProps) => {
Expand All @@ -25,6 +27,11 @@ export const TimeoutButton = ({

const secondsLeft = Math.max(0, (timeoutDeadline - now) / 1000);

useEffect(() => {
if (secondsLeft > 0) return;
onTimeOut?.();
}, [secondsLeft]);

if (slotFinal && secondsLeft <= 0) return slotFinal;

return (
Expand Down
12 changes: 10 additions & 2 deletions src/constants/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,17 @@ export type AnalyticsEventData<T extends AnalyticsEvent> =
validatorUrl: string;
}
: T extends AnalyticsEvent.TransferDeposit
? {}
? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: T extends AnalyticsEvent.TransferWithdraw
? {}
? {
chainId?: string;
tokenAddress?: string;
tokenSymbol?: string;
}
: // Trading
T extends AnalyticsEvent.TradeOrderTypeSelected
? {
Expand Down
1 change: 1 addition & 0 deletions src/constants/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export type TransferNotifcation = {
isCctp?: boolean;
errorCount?: number;
status?: StatusResponse;
isExchange?: boolean;
};

/**
Expand Down
24 changes: 15 additions & 9 deletions src/hooks/useLocalNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,24 +82,30 @@ const useLocalNotificationsContext = () => {
isCctp,
errorCount,
status: currentStatus,
isExchange,
} = transferNotification;

// @ts-ignore status.errors is not in the type definition but can be returned
// also error can some time come back as an empty object so we need to ignore for that
const hasErrors = !!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);
const hasErrors =
// @ts-ignore status.errors is not in the type definition but can be returned
// also error can some time come back as an empty object so we need to ignore for that
!!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);

if (
!isExchange &&
!hasErrors &&
(!currentStatus?.squidTransactionStatus ||
currentStatus?.squidTransactionStatus === 'ongoing')
) {
try {
const status = await fetchSquidStatus({
transactionId: txHash,
toChainId,
fromChainId,
}, isCctp);
const status = await fetchSquidStatus(
{
transactionId: txHash,
toChainId,
fromChainId,
},
isCctp
);

if (status) {
transferNotification.status = status;
Expand Down
5 changes: 3 additions & 2 deletions src/hooks/useNotificationTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@ export const notificationTypes: NotificationTypeConfig[] = [

useEffect(() => {
for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount, type } = transfer;
const isFinished = Boolean(status) && status?.squidTransactionStatus !== 'ongoing';
const { fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
const isFinished =
(Boolean(status) && status?.squidTransactionStatus !== 'ongoing') || isExchange;
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;

const transferType =
Expand Down
19 changes: 19 additions & 0 deletions src/lib/addressUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,22 @@ export function convertBech32Address({
}): string {
return toBech32(bech32Prefix, fromHex(toHex(fromBech32(address).data)));
}

/**
* Validates a Cosmos address with a specific prefix.
* @param {string} address The Cosmos address to validate.
* @param {string} prefix The expected prefix for the address.
* @returns {boolean} True if the address is valid and matches the prefix, false otherwise.
*/
export function validateCosmosAddress(address: string, prefix: string) {
try {
// Decode the address to verify its structure and prefix
const { prefix: decodedPrefix } = fromBech32(address);

// Check if the decoded address has the expected prefix
return decodedPrefix === prefix;
} catch (error) {
// If decoding fails, the address is not valid
return false;
}
}
Comment on lines +35 to +46
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good candidate to add to tests

8 changes: 6 additions & 2 deletions src/lib/testFlags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ class TestFlags {
return !!this.queryParams.displayinitializingmarkets;
}

get addressOverride():string {
get addressOverride(): string {
return this.queryParams.address;
}

get showTradingRewards() {
return !!this.queryParams.tradingrewards;
}

get showCexWithdrawal() {
return !!this.queryParams.cexwithdrawal;
}
}

export const testFlags = new TestFlags();
4 changes: 1 addition & 3 deletions src/views/dialogs/DepositDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ export const DepositDialog = ({ setIsOpen }: ElementProps) => {
const stringGetter = useStringGetter();
const { isMobile } = useBreakpoints();

const closeDialog = () => setIsOpen?.(false);

return (
<Dialog
isOpen
setIsOpen={setIsOpen}
title={stringGetter({ key: STRING_KEYS.DEPOSIT })}
placement={isMobile ? DialogPlacement.FullScreen : DialogPlacement.Default}
>
<DepositDialogContent onDeposit={closeDialog} />
<DepositDialogContent />
</Dialog>
);
};
16 changes: 14 additions & 2 deletions src/views/dialogs/DepositDialog/DepositDialogContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ import { useEffect, useState } from 'react';
import styled, { type AnyStyledComponent } from 'styled-components';

import { TransferInputField, TransferType } from '@/constants/abacus';
import { AnalyticsEvent } from '@/constants/analytics';
import { isMainnet } from '@/constants/networks';
import { layoutMixins } from '@/styles/layoutMixins';

import { DepositForm } from '@/views/forms/AccountManagementForms/DepositForm';
import { TestnetDepositForm } from '@/views/forms/AccountManagementForms/TestnetDepositForm';

import abacusStateManager from '@/lib/abacus';
import { track } from '@/lib/analytics';

type ElementProps = {
onDeposit?: () => void;
Expand All @@ -34,9 +36,19 @@ export const DepositDialogContent = ({ onDeposit }: ElementProps) => {
return (
<Styled.Content>
{isMainnet || !showFaucet ? (
<DepositForm onDeposit={onDeposit} />
<DepositForm
onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit, event);
onDeposit?.();
}}
/>
) : (
<TestnetDepositForm onDeposit={onDeposit} />
<TestnetDepositForm
onDeposit={() => {
track(AnalyticsEvent.TransferFaucet);
onDeposit?.();
}}
/>
)}
{!isMainnet && (
<Styled.TextToggle onClick={() => setShowFaucet(!showFaucet)}>
Expand Down
4 changes: 2 additions & 2 deletions src/views/dialogs/OnboardingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ export const OnboardingDialog = ({ setIsOpen }: ElementProps) => {
<Styled.Content>
{isMainnet ? (
<DepositForm
onDeposit={() => {
track(AnalyticsEvent.TransferDeposit);
onDeposit={(event) => {
track(AnalyticsEvent.TransferDeposit, event);
}}
/>
) : (
Expand Down
17 changes: 11 additions & 6 deletions src/views/forms/AccountManagementForms/DepositForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Abi, parseUnits } from 'viem';
import erc20 from '@/abi/erc20.json';
import erc20_usdt from '@/abi/erc20_usdt.json';
import { TransferInputField, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import { AnalyticsEvent, AnalyticsEventData } from '@/constants/analytics';
import { AlertType } from '@/constants/alerts';
import { ButtonSize } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
Expand Down Expand Up @@ -48,7 +49,7 @@ import { DepositButtonAndReceipt } from './DepositForm/DepositButtonAndReceipt';
import { NobleDeposit } from '../NobleDeposit';

type DepositFormProps = {
onDeposit?: () => void;
onDeposit?: (event?: AnalyticsEventData<AnalyticsEvent.TransferDeposit>) => void;
onError?: () => void;
};

Expand Down Expand Up @@ -132,7 +133,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
if (error) onError?.();
}, [error]);

const onSelectChain = useCallback((name: string, type: 'chain' | 'exchange') => {
const onSelectNetwork = useCallback((name: string, type: 'chain' | 'exchange') => {
if (name) {
abacusStateManager.clearTransferInputValues();
setFromAmount('');
Expand Down Expand Up @@ -258,8 +259,6 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
};
const txHash = await signerWagmi.sendTransaction(tx);

onDeposit?.();

if (txHash) {
addTransferNotification({
txHash: txHash,
Expand All @@ -271,6 +270,12 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
});
abacusStateManager.clearTransferInputValues();
setFromAmount('');

onDeposit?.({
chainId: chainIdStr || undefined,
tokenAddress: sourceToken?.address || undefined,
tokenSymbol: sourceToken?.symbol || undefined,
});
}
} catch (error) {
log('DepositForm/onSubmit', error);
Expand All @@ -279,7 +284,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
setIsLoading(false);
}
},
[requestPayload, signerWagmi, chainId]
[requestPayload, signerWagmi, chainId, sourceToken, sourceChain]
);

const amountInputReceipt = [
Expand Down Expand Up @@ -378,7 +383,7 @@ export const DepositForm = ({ onDeposit, onError }: DepositFormProps) => {
<SourceSelectMenu
selectedChain={chainIdStr || undefined}
selectedExchange={exchange || undefined}
onSelect={onSelectChain}
onSelect={onSelectNetwork}
/>
{exchange && nobleAddress ? (
<NobleDeposit />
Expand Down
Loading