Skip to content

Commit

Permalink
feat: cctp withdrawals: setup [1/n] (#1152)
Browse files Browse the repository at this point in the history
  • Loading branch information
yogurtandjam authored Oct 17, 2024
1 parent 7a232fb commit e51329e
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 169 deletions.
224 changes: 115 additions & 109 deletions public/configs/v1/env.json

Large diffs are not rendered by default.

16 changes: 15 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ import { config, privyConfig } from '@/lib/wagmi';
import { RestrictionWarning } from './components/RestrictionWarning';
import { ComplianceStates } from './constants/compliance';
import { DialogTypes } from './constants/dialogs';
import { SkipProvider } from './hooks/transfers/skipClient';
import { SkipProvider, useSkipClient } from './hooks/transfers/skipClient';
import { assetsQueryFn, chainsQueryFn } from './hooks/transfers/useTransfers';
import { useAnalytics } from './hooks/useAnalytics';
import { useBreakpoints } from './hooks/useBreakpoints';
import { useCommandMenu } from './hooks/useCommandMenu';
Expand Down Expand Up @@ -86,6 +87,8 @@ const Content = () => {
const { complianceState } = useComplianceState();
const showRestrictionWarning = complianceState === ComplianceStates.READ_ONLY;

const { skipClient, skipClientId } = useSkipClient();

const pathFromHash = useMemo(() => {
if (location.hash === '') {
return '';
Expand All @@ -101,6 +104,17 @@ const Content = () => {
}
}, [dispatch]);

useEffect(() => {
appQueryClient.prefetchQuery({
queryKey: ['transferEligibleChains', skipClientId],
queryFn: () => chainsQueryFn(skipClient),
});
appQueryClient.prefetchQuery({
queryKey: ['transferEligibleAssets', skipClientId],
queryFn: () => assetsQueryFn(skipClient),
});
}, [skipClient]);

Check warning on line 116 in src/App.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useEffect has a missing dependency: 'skipClientId'. Either include it or remove the dependency array

return (
<>
<GlobalStyle />
Expand Down
1 change: 1 addition & 0 deletions src/constants/abacus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ export const PerpetualMarketType = Abacus.exchange.dydx.abacus.output.PerpetualM

// ------ Configs ------ //
export const StatsigConfig = Abacus.exchange.dydx.abacus.state.manager.StatsigConfig;
export const AutoSweepConfig = Abacus.exchange.dydx.abacus.state.manager.AutoSweepConfig;
export type Configs = Abacus.exchange.dydx.abacus.output.Configs;
export type FeeDiscount = Abacus.exchange.dydx.abacus.output.FeeDiscount;
export type FeeTier = Abacus.exchange.dydx.abacus.output.FeeTier;
Expand Down
4 changes: 4 additions & 0 deletions src/constants/graz.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ export const getNeutronChainId = () => {
return isMainnet ? CosmosChainId.Neutron : CosmosChainId.NeutronTestnet;
};

export const getSolanaChainId = () => {
return isMainnet ? 'solana' : 'solana-devnet';
};

const osmosisChainId = getOsmosisChainId();
const nobleChainId = getNobleChainId();
const neutronChainId = getNeutronChainId();
Expand Down
1 change: 1 addition & 0 deletions src/constants/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export enum TransferNotificationTypes {
Deposit = 'deposit',
}

// TODO: fix typo
export type TransferNotifcation = {
id?: string;
txHash: string;
Expand Down
8 changes: 6 additions & 2 deletions src/constants/transfers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,18 @@ export const getDefaultTokenDenomFromAssets = (assets: Asset[]): string => {
const nativeChainToken = assets.find((asset) => {
return isNativeDenom(asset.denom);
});
const uusdcToken = assets.find((asset) => {
return asset.denom === 'uusdc' || asset.originDenom === 'uusdc';
});
// If not cctp or native chain token, default to the first item in the list
const defaultTokenDenom = cctpToken?.denom ?? nativeChainToken?.denom ?? assets[0]?.denom;
const defaultTokenDenom =
cctpToken?.denom ?? nativeChainToken?.denom ?? uusdcToken?.denom ?? assets[0]?.denom;
return defaultTokenDenom;
};

export const getDefaultChainIDFromNetworkType = (networkType: NetworkType): string | undefined => {
if (networkType === 'evm') return '1';
if (networkType === 'svm') return 'solana';
if (networkType === 'cosmos') return 'noble';
if (networkType === 'cosmos') return 'noble-1';
return undefined;
};
16 changes: 12 additions & 4 deletions src/hooks/transfers/skipClient.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createContext, useContext, useMemo } from 'react';

import {
MsgWithdrawFromSubaccount,
TYPE_URL_MSG_WITHDRAW_FROM_SUBACCOUNT,
} from '@dydxprotocol/v4-client-js';
import { SkipClient } from '@skip-go/client';

import { getNeutronChainId, getNobleChainId, getOsmosisChainId } from '@/constants/graz';
Expand Down Expand Up @@ -28,9 +32,9 @@ const useSkipClientContext = () => {
useEndpointsConfig();
const { compositeClient } = useDydxClient();
const selectedDydxChainId = useAppSelector(getSelectedDydxChainId);
const skipClient = useMemo(
() =>
new SkipClient({
const { skipClient, skipClientId } = useMemo(
() => ({
skipClient: new SkipClient({
endpointOptions: {
getRpcEndpointForChain: async (chainId: string) => {
if (chainId === getNobleChainId()) return nobleValidator;
Expand All @@ -44,8 +48,12 @@ const useSkipClientContext = () => {
throw new Error(`Error: no rpc endpoint found for chainId: ${chainId}`);
},
},
registryTypes: [[TYPE_URL_MSG_WITHDRAW_FROM_SUBACCOUNT, MsgWithdrawFromSubaccount]],
}),
skipClientId: crypto.randomUUID(),
}),
[
compositeClient?.network.validatorConfig.restEndpoint,
neutronValidator,
nobleValidator,
osmosisValidator,
Expand All @@ -54,5 +62,5 @@ const useSkipClientContext = () => {
validators,
]
);
return { skipClient };
return { skipClient, skipClientId };
};
100 changes: 47 additions & 53 deletions src/hooks/transfers/useTransfers.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useMemo, useState } from 'react';

import { NOBLE_BECH32_PREFIX } from '@dydxprotocol/v4-client-js';
import { Chain, MsgsDirectRequest } from '@skip-go/client';
import { Chain, MsgsDirectRequest, SkipClient } from '@skip-go/client';
import { useQuery } from '@tanstack/react-query';
import { parseUnits } from 'viem';

Expand All @@ -14,7 +14,6 @@ import {
OSMO_BECH32_PREFIX,
} from '@/constants/graz';
import {
COSMOS_SWAP_VENUES,
getDefaultChainIDFromNetworkType,
getDefaultTokenDenomFromAssets,
getNetworkTypeFromWalletNetworkType,
Expand All @@ -28,12 +27,41 @@ import { useAppSelector } from '@/state/appTypes';

import { convertBech32Address } from '@/lib/addressUtils';
import { isNativeDenom } from '@/lib/assetUtils';
import { MustBigNumber } from '@/lib/numbers';

import { useAccounts } from '../useAccounts';
import { useDebounce } from '../useDebounce';
import { useSkipClient } from './skipClient';

export const chainsQueryFn = async (skipClient: SkipClient) => {
const skipSupportedChains = await skipClient.chains({
includeEVM: true,
includeSVM: true,
});
const chainsByNetworkMap = skipSupportedChains.reduce<{ [key: string]: Chain[] }>(
(chainsMap, nextChain) => {
const chainsListForNetworkType = chainsMap[nextChain.chainType] ?? [];
chainsMap[nextChain.chainType] = [...chainsListForNetworkType, nextChain];
return chainsMap;
},
{}
);
return {
skipSupportedChains,
chainsByNetworkMap,
};
};

export const assetsQueryFn = async (skipClient: SkipClient) => {
const assetsByChain = await skipClient.assets({
includeEvmAssets: true,
includeSvmAssets: true,
});
return { assetsByChain };
};

export const useTransfers = () => {
const { skipClient } = useSkipClient();
const { skipClient, skipClientId } = useSkipClient();
const { dydxAddress, sourceAccount } = useAccounts();
const selectedDydxChainId = useAppSelector(getSelectedDydxChainId);

Expand All @@ -51,39 +79,19 @@ export const useTransfers = () => {
const [transferType, setTransferType] = useState<TransferType>(TransferType.Withdraw);
const [amount, setAmount] = useState<string>('');

const debouncedAmount = useDebounce(amount, 500);
const debouncedAmountBN = useMemo(() => MustBigNumber(debouncedAmount), [debouncedAmount]);

const chainsQuery = useQuery({
queryKey: ['transferEligibleChains'],
queryFn: async () => {
const skipSupportedChains = await skipClient.chains({
includeEVM: true,
includeSVM: true,
});
const chainsByNetworkMap = skipSupportedChains.reduce<Record<string, Chain[]>>(
(chainsMap, nextChain) => {
const chainsListForNetworkType = chainsMap[nextChain.chainType] ?? [];
chainsMap[nextChain.chainType] = [...chainsListForNetworkType, nextChain];
return chainsMap;
},
{}
);
return {
skipSupportedChains,
chainsByNetworkMap,
};
},
queryKey: ['transferEligibleChains', skipClientId],
queryFn: () => chainsQueryFn(skipClient),
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
});
const assetsQuery = useQuery({
queryKey: ['transferEligibleAssets'],
queryFn: async () => {
const assetsByChain = await skipClient.assets({
includeEvmAssets: true,
includeSvmAssets: true,
});
return { assetsByChain };
},
queryKey: ['transferEligibleAssets', skipClientId],
queryFn: () => assetsQueryFn(skipClient),
refetchOnWindowFocus: false,
refetchOnMount: false,
refetchOnReconnect: false,
Expand Down Expand Up @@ -136,7 +144,7 @@ export const useTransfers = () => {
!!fromAddress &&
!!toAddress &&
!!transferType &&
!!amount &&
!!debouncedAmount &&
!!dydxAddress;

const routeQuery = useQuery({
Expand All @@ -149,7 +157,7 @@ export const useTransfers = () => {
fromAddress,
toAddress,
transferType,
amount,
debouncedAmount,
dydxAddress,
selectedDydxChainId,
],
Expand Down Expand Up @@ -187,27 +195,14 @@ export const useTransfers = () => {
};
// WITHDRAWALS
if (transferType === TransferType.Withdraw) {
if (isTokenCctp(toToken)) {
return skipClient.msgsDirect({
...baseParams,
chainIdsToAddresses: {
[selectedDydxChainId]: dydxAddress,
[toToken.chainID]: toAddress,
},
bridges: ['IBC', 'CCTP'],
allowMultiTx: true,
});
}
// Non cctp withdrawals
return skipClient.msgsDirect({
...baseParams,
chainIdsToAddresses: {
[selectedDydxChainId]: dydxAddress,
[toToken.chainID]: toAddress,
...cosmosChainAddresses,
},
bridges: ['IBC', 'AXELAR'],
swapVenues: COSMOS_SWAP_VENUES,
bridges: ['IBC', 'CCTP'],
allowMultiTx: true,
});
}

Expand Down Expand Up @@ -235,15 +230,11 @@ export const useTransfers = () => {
swapVenues: SWAP_VENUES,
});
},

enabled: hasAllParams,
});

const route = routeQuery?.data;

// TODO [onboarding-rewrite]: maybe abstract away the adding of transfer notifications to this hook instead of having
// withdrawal and deposit modals handle them separately in multiple places
// const { addOrUpdateTransferNotification } = useLocalNotifications();

const { route, txs } = routeQuery?.data ?? {};
return {
// TODO [onboarding-rewrite]: Think about trimming this list
// Right now we're exposing everything, but there's a good chance we can only expose a few properties
Expand All @@ -267,9 +258,12 @@ export const useTransfers = () => {
transferType,
setTransferType,
route,
txs,
defaultChainId,
defaultTokenDenom,
toToken,
fromToken,
debouncedAmount,
debouncedAmountBN,
};
};
1 change: 1 addition & 0 deletions src/hooks/useAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ const useAccountsContext = () => {
return {
// Wallet connection
sourceAccount,
localNobleWallet,

// Wallet selection
selectWallet,
Expand Down

0 comments on commit e51329e

Please sign in to comment.