e2e working deposit/withdraws

This commit is contained in:
Bill He
2023-08-28 11:15:43 -07:00
parent 7d7adc8da2
commit 5093823b25
4 changed files with 137 additions and 76 deletions
+42 -17
View File
@@ -89,6 +89,7 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
simulateTransferNativeToken,
placeOrderForSubaccount,
cancelOrderForSubaccount,
sendSquidWithdrawFromSubaccount,
} = useMemo(
() => ({
depositToSubaccount: async ({
@@ -302,31 +303,31 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
return result;
},
sendSquidWithdraw: async ({
subaccount,
sendSquidWithdrawFromSubaccount: async ({
subaccountClient,
amount,
payload,
}:{
subaccount: SubaccountClient;
}: {
subaccountClient: SubaccountClient;
amount: number;
payload: string;
}) => {
const decode = (str: string):string => Buffer.from(str, 'base64').toString('binary');
const decoded = decode(payload);
const json = JSON.parse(decoded);
if (!compositeClient) throw new Error('client not initialized');
const transaction = JSON.parse(payload);
const msg = compositeClient.withdrawFromSubaccountMessage(subaccountClient, amount);
const ibcMsg: EncodeObject = {
typeUrl: json.msgTypeUrl,
value: json.msg,
typeUrl: transaction.msgTypeUrl,
value: transaction.msg,
};
const encodeObjects: Promise<EncodeObject[]> = Promise.resolve([ibcMsg]);
await compositeClient?.send(
subaccount.wallet,
() => encodeObjects,
return await compositeClient.send(
subaccountClient.wallet,
() => Promise.resolve([msg, ibcMsg]),
false
)
);
},
}),
[compositeClient]
);
@@ -424,6 +425,28 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
[subaccountClient, simulateWithdrawFromSubaccount, simulateTransferNativeToken]
);
const simulateWithdraw = useCallback(
async (amount: number) => {
if (!subaccountClient) {
return;
}
return await simulateWithdrawFromSubaccount({ subaccountClient, amount });
},
[subaccountClient, simulateWithdrawFromSubaccount]
);
const sendSquidWithdraw = useCallback(
async (amount: number, payload: string) => {
if (!subaccountClient) {
return;
}
return await sendSquidWithdrawFromSubaccount({ subaccountClient, amount, payload });
},
[subaccountClient, sendSquidWithdrawFromSubaccount]
);
// ------ Faucet Methods ------ //
const requestFaucetFunds = useCallback(async () => {
if (!dydxAddress) return;
@@ -615,6 +638,8 @@ export const useSubaccountContext = ({ localDydxWallet }: { localDydxWallet?: Lo
// Transfer Methods
simulateTransfer,
transfer,
simulateWithdraw,
sendSquidWithdraw,
// Trading Methods
placeOrder,
+12
View File
@@ -190,6 +190,18 @@ class AbacusStateManager {
this.abacusFormatter.setLocaleSeparators({ group, decimal });
};
setTransferStatus = ({
hash,
fromChainId,
toChainId,
}: {
hash: string;
fromChainId?: string;
toChainId?: string;
}) => {
this.stateManager.transferStatus(hash, fromChainId, toChainId);
};
// ------ Utils ------ //
placeOrderPayload = (): Nullable<HumanReadablePlaceOrderPayload> =>
this.stateManager.placeOrderPayload();
@@ -6,14 +6,19 @@ import type { TokenData, ChainData } from '@0xsquid/sdk';
import { shallowEqual, useSelector } from 'react-redux';
import { parseUnits } from 'viem';
import { TransferInputField, TransferInputChainResource, TransferInputTokenResource, TransferType } from '@/constants/abacus';
import {
TransferInputField,
TransferInputChainResource,
TransferInputTokenResource,
TransferType,
} from '@/constants/abacus';
import { AlertType } from '@/constants/alerts';
import { ButtonAction, ButtonSize, ButtonType } from '@/constants/buttons';
import { STRING_KEYS } from '@/constants/localization';
import { CLIENT_NETWORK_CONFIGS, type DydxV4Network } from '@/constants/networks';
import { NumberSign } from '@/constants/numbers';
import { NumberSign, QUANTUM_MULTIPLIER } from '@/constants/numbers';
import { useAccounts, useDebounce, useStringGetter, useSquidRouter } from '@/hooks';
import { useAccounts, useDebounce, useStringGetter, useSquidRouter, useSubaccount } from '@/hooks';
import { SQUID_WITHDRAW_ROUTE_DEFAULTS } from '@/hooks/useSquidRouter';
import { layoutMixins } from '@/styles/layoutMixins';
@@ -39,27 +44,32 @@ import { MustBigNumber } from '@/lib/numbers';
import { TokenSelectMenu } from './TokenSelectMenu';
import { WithdrawButtonAndReceipt } from './WithdrawForm/WithdrawButtonAndReceipt';
import { parse } from 'path';
import { s } from 'vitest/dist/types-2b1c412e';
export const WithdrawForm = () => {
const stringGetter = useStringGetter();
const { dydxAddress } = useAccounts();
const [error, setError] = useState<Error | null>(null);
const [isLoading, setIsLoading] = useState(false);
const { simulateWithdraw, sendSquidWithdraw } = useSubaccount();
const { freeCollateral } = useSelector(getSubaccount, shallowEqual) || {};
const {
error,
setError,
isLoading,
axelarscanURL,
} = useSquidRouter({ allChains: true });
const { axelarscanURL } = useSquidRouter();
// User input
const [withdrawAmount, setWithdrawAmount] = useState('');
const [slippage, setSlippage] = useState(0.001); // 0.1% slippage
const [slippage, setSlippage] = useState(0.01); // 0.1% slippage
const debouncedAmount = useDebounce<string>(withdrawAmount, 500);
const { requestPayload, token, chain, address: toAddress, resources } = useSelector(getTransferInputs) || {};
const {
requestPayload,
token,
chain,
address: toAddress,
resources,
} = useSelector(getTransferInputs) || {};
const toChain = useMemo(
() => (chain ? resources?.chainResources?.get(chain) : undefined),
[chain, resources]
@@ -88,14 +98,24 @@ export const WithdrawForm = () => {
}, []);
useEffect(() => {
const hasInvalidInput = debouncedAmountBN.isNaN() || debouncedAmountBN.lte(0);
const setTransferValue = async () => {
const hasInvalidInput = debouncedAmountBN.isNaN() || debouncedAmountBN.lte(0);
abacusStateManager.setTransferValue({
value: hasInvalidInput ? 0 : debouncedAmount,
field: TransferInputField.usdcSize,
});
const stdFee = await simulateWithdraw(parseFloat(debouncedAmount));
const amount = hasInvalidInput
? 0
: parseFloat(debouncedAmount) -
parseFloat(stdFee?.amount[0]?.amount || '0') / QUANTUM_MULTIPLIER;
setError(null);
abacusStateManager.setTransferValue({
value: hasInvalidInput ? 0 : amount.toString(),
field: TransferInputField.usdcSize,
});
setError(null);
};
setTransferValue();
}, [debouncedAmountBN.toNumber()]);
/**
@@ -106,23 +126,33 @@ export const WithdrawForm = () => {
const onSubmit = useCallback(
async (e: FormEvent) => {
e.preventDefault();
// const withdraw = await dydxClient.withdrawFromSubaccount(debouncedAmountBN.toNumber(), 0);
// const txHash = await executeRoute();
// @ts-ignore execute route returns Promise<string> instead of Promise<TransactionReponse>
// setTransactionHash(txHash);
try {
if (!requestPayload?.data || !debouncedAmountBN.toNumber()) {
throw new Error('Invalid request payload');
}
const txHash = await sendSquidWithdraw(debouncedAmountBN.toNumber(), requestPayload?.data);
if (txHash?.hash) {
abacusStateManager.setTransferStatus({
hash: `0x${Buffer.from(txHash.hash).toString('hex')}`,
fromChainId: 'dydx-testnet-2',
toChainId: toChain?.chainId?.toString(),
})
}
} catch (error) {
setError(error);
}
},
[setTransactionHash]
[setTransactionHash, requestPayload, debouncedAmountBN]
);
const onChangeAddress = useCallback(
(e: ChangeEvent<HTMLInputElement>) => {
abacusStateManager.setTransferValue({
field: TransferInputField.address,
value: e.target.value,
});
},
[]
);
const onChangeAddress = useCallback((e: ChangeEvent<HTMLInputElement>) => {
abacusStateManager.setTransferValue({
field: TransferInputField.address,
value: e.target.value,
});
}, []);
const onChangeAmount = useCallback(
({ value }: NumberFormatValues) => {
@@ -146,7 +176,6 @@ export const WithdrawForm = () => {
setWithdrawAmount(freeCollateralBN.toString());
}, [freeCollateral?.current, setWithdrawAmount]);
const onSelectChain = useCallback((chain: TransferInputChainResource) => {
if (chain) {
abacusStateManager.clearTransferInputValues();
@@ -158,19 +187,16 @@ export const WithdrawForm = () => {
}
}, []);
const onSelectToken = useCallback(
(token: TransferInputTokenResource) => {
if (token) {
abacusStateManager.clearTransferInputValues();
abacusStateManager.setTransferValue({
field: TransferInputField.token,
value: token.address,
});
setWithdrawAmount('');
}
},
[]
);
const onSelectToken = useCallback((token: TransferInputTokenResource) => {
if (token) {
abacusStateManager.clearTransferInputValues();
abacusStateManager.setTransferValue({
field: TransferInputField.token,
value: token.address,
});
setWithdrawAmount('');
}
}, []);
const amountInputReceipt = [
{
@@ -196,8 +222,9 @@ export const WithdrawForm = () => {
];
let errorMessage = !toAddress ? 'Please enter a destination address' : undefined;
const isDisabled = !!errorMessage ||
const isDisabled =
!!errorMessage ||
!toToken ||
!toChain ||
debouncedAmountBN.isNaN() ||
@@ -219,10 +246,7 @@ export const WithdrawForm = () => {
onSelectChain={onSelectChain}
/>
</Styled.DestinationRow>
<TokenSelectMenu
selectedToken={toToken || undefined}
onSelectToken={onSelectToken}
/>
<TokenSelectMenu selectedToken={toToken || undefined} onSelectToken={onSelectToken} />
<Styled.WithDetailsReceipt side="bottom" detailItems={amountInputReceipt}>
<FormInput
type={InputType.Number}
@@ -260,7 +284,6 @@ export const WithdrawForm = () => {
)}
<WithdrawButtonAndReceipt
isDisabled={isDisabled}
isLoading={isLoading}
setError={undefined}
setSlippage={onSetSlippage}
slippage={slippage}
@@ -306,4 +329,4 @@ Styled.Link = styled(Link)`
Styled.TransactionInfo = styled.span`
${layoutMixins.row}
`;
`;
@@ -40,7 +40,6 @@ type ElementProps = {
withdrawToken?: TransferInputTokenResource;
isDisabled?: boolean;
isLoading?: boolean;
};
export const WithdrawButtonAndReceipt = ({
@@ -52,7 +51,6 @@ export const WithdrawButtonAndReceipt = ({
withdrawToken,
isDisabled,
isLoading,
}: ElementProps) => {
const [showFeeBreakdown, setShowFeeBreakdown] = useState(false);
const [isEditingSlippage, setIsEditingSlipapge] = useState(false);
@@ -166,6 +164,9 @@ export const WithdrawButtonAndReceipt = ({
// },
];
const isFormValid = !isDisabled && !isEditingSlippage && queryStatus !== 'error';
const isLoading = isFormValid && !requestPayload;
return (
<Styled.WithReceipt
slotReceipt={
@@ -192,7 +193,7 @@ export const WithdrawButtonAndReceipt = ({
action={ButtonAction.Primary}
type={ButtonType.Submit}
state={{
isDisabled: isEditingSlippage || isDisabled || queryStatus === 'error',
isDisabled: !isFormValid,
isLoading,
}}
>