From 91c49fab99b533f520efa318b06cf6e751c567e2 Mon Sep 17 00:00:00 2001 From: Reed <3893871+dharit-tan@users.noreply.github.com> Date: Thu, 7 Sep 2023 19:13:23 -0400 Subject: [PATCH] Wire up withdraw USDC saga to modal state (#4045) --- .../src/store/ui/withdraw-usdc/slice.ts | 9 +++- .../withdraw-usdc-modal/WithdrawUSDCModal.tsx | 46 +++++++++++++++---- .../components/ConfirmTransferDetails.tsx | 6 ++- .../components/TransferInProgress.tsx | 6 ++- .../components/TransferSuccessful.tsx | 6 ++- .../availability/UsdcPurchaseFields.tsx | 6 +-- .../application/ui/withdraw-usdc/sagas.ts | 24 ++++++---- packages/web/src/utils/tokenInput.ts | 4 ++ 8 files changed, 82 insertions(+), 25 deletions(-) diff --git a/packages/common/src/store/ui/withdraw-usdc/slice.ts b/packages/common/src/store/ui/withdraw-usdc/slice.ts index 6f91a7c225..f1d84a7936 100644 --- a/packages/common/src/store/ui/withdraw-usdc/slice.ts +++ b/packages/common/src/store/ui/withdraw-usdc/slice.ts @@ -62,7 +62,14 @@ const slice = createSlice({ setAmountFailed: (state, action: PayloadAction<{ error: Error }>) => { state.amountError = action.payload.error }, - beginWithdrawUSDC: (state) => { + beginWithdrawUSDC: ( + state, + _action: PayloadAction<{ + amount: number + destinationAddress: string + onSuccess: (transaction: string) => void + }> + ) => { state.withdrawStatus = Status.LOADING }, withdrawUSDCSucceeded: (state) => { diff --git a/packages/web/src/components/withdraw-usdc-modal/WithdrawUSDCModal.tsx b/packages/web/src/components/withdraw-usdc-modal/WithdrawUSDCModal.tsx index 32799328d4..c4d87521ae 100644 --- a/packages/web/src/components/withdraw-usdc-modal/WithdrawUSDCModal.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/WithdrawUSDCModal.tsx @@ -1,11 +1,15 @@ +import { useCallback } from 'react' + import { SolanaWalletAddress, useUSDCBalance, useWithdrawUSDCModal, - WithdrawUSDCModalPages + WithdrawUSDCModalPages, + withdrawUSDCActions } from '@audius/common' import { Modal, ModalContent, ModalHeader } from '@audius/stems' import { Formik } from 'formik' +import { useDispatch } from 'react-redux' import { z } from 'zod' import { toFormikValidationSchema } from 'zod-formik-adapter' @@ -20,6 +24,8 @@ import { EnterTransferDetails } from './components/EnterTransferDetails' import { TransferInProgress } from './components/TransferInProgress' import { TransferSuccessful } from './components/TransferSuccessful' +const { beginWithdrawUSDC } = withdrawUSDCActions + const messages = { title: 'Withdraw Funds', errors: { @@ -49,10 +55,34 @@ const WithdrawUSDCFormSchema = (userBalance: number) => { } export const WithdrawUSDCModal = () => { - const { isOpen, onClose, onClosed, data } = useWithdrawUSDCModal() + const dispatch = useDispatch() + const { isOpen, onClose, onClosed, data, setData } = useWithdrawUSDCModal() const { page } = data const { data: balance } = useUSDCBalance() + const onSuccess = useCallback( + (signature: string) => { + setData({ + page: WithdrawUSDCModalPages.TRANSFER_SUCCESSFUL, + signature + }) + }, + [setData] + ) + + const handleSubmit = useCallback( + ({ amount, address }: { amount: number; address: string }) => { + dispatch( + beginWithdrawUSDC({ + amount, + destinationAddress: address, + onSuccess + }) + ) + }, + [dispatch, onSuccess] + ) + let formPage switch (page) { case WithdrawUSDCModalPages.ENTER_TRANSFER_DETAILS: @@ -90,15 +120,15 @@ export const WithdrawUSDCModal = () => { { - console.info(values) - }} + onSubmit={handleSubmit} > {formPage} diff --git a/packages/web/src/components/withdraw-usdc-modal/components/ConfirmTransferDetails.tsx b/packages/web/src/components/withdraw-usdc-modal/components/ConfirmTransferDetails.tsx index ad31a1d5ec..03c729a040 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/ConfirmTransferDetails.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/ConfirmTransferDetails.tsx @@ -18,6 +18,7 @@ import { AMOUNT, CONFIRM } from 'components/withdraw-usdc-modal/WithdrawUSDCModal' +import { toHumanReadable } from 'utils/tokenInput' import styles from './ConfirmTransferDetails.module.css' import { Hint } from './Hint' @@ -56,7 +57,10 @@ export const ConfirmTransferDetails = () => { return (
- +
diff --git a/packages/web/src/components/withdraw-usdc-modal/components/TransferInProgress.tsx b/packages/web/src/components/withdraw-usdc-modal/components/TransferInProgress.tsx index 81450af1db..960f156acb 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/TransferInProgress.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/TransferInProgress.tsx @@ -14,6 +14,7 @@ import { ADDRESS, AMOUNT } from 'components/withdraw-usdc-modal/WithdrawUSDCModal' +import { toHumanReadable } from 'utils/tokenInput' import { TextRow } from './TextRow' import styles from './TransferInProgress.module.css' @@ -40,7 +41,10 @@ export const TransferInProgress = () => { left={messages.currentBalance} right={`$${balanceFormatted}`} /> - +
diff --git a/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx b/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx index 9f22d13157..f94d261402 100644 --- a/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx +++ b/packages/web/src/components/withdraw-usdc-modal/components/TransferSuccessful.tsx @@ -22,6 +22,7 @@ import { ADDRESS, AMOUNT } from 'components/withdraw-usdc-modal/WithdrawUSDCModal' +import { toHumanReadable } from 'utils/tokenInput' import { TextRow } from './TextRow' import styles from './TransferSuccessful.module.css' @@ -56,7 +57,10 @@ export const TransferSuccessful = () => { return (
- +
diff --git a/packages/web/src/pages/upload-page/fields/availability/UsdcPurchaseFields.tsx b/packages/web/src/pages/upload-page/fields/availability/UsdcPurchaseFields.tsx index b27df06727..931876b500 100644 --- a/packages/web/src/pages/upload-page/fields/availability/UsdcPurchaseFields.tsx +++ b/packages/web/src/pages/upload-page/fields/availability/UsdcPurchaseFields.tsx @@ -12,9 +12,9 @@ import { TextField, TextFieldProps } from 'components/form-fields' import layoutStyles from 'components/layout/layout.module.css' import { Text } from 'components/typography' import { - PRECISION, onTokenInputBlur, - onTokenInputChange + onTokenInputChange, + toHumanReadable } from 'utils/tokenInput' import { PREVIEW, PRICE } from '../AccessAndSaleField' @@ -94,7 +94,7 @@ const PriceField = (props: TrackAvailabilityFieldsProps) => { const { disabled } = props const [{ value }, , { setValue: setPrice }] = useField(PRICE) const [humanizedValue, setHumanizedValue] = useState( - value ? (value / 100).toFixed(PRECISION) : null + value ? toHumanReadable(value) : null ) const handlePriceChange: ChangeEventHandler = useCallback( diff --git a/packages/web/src/store/application/ui/withdraw-usdc/sagas.ts b/packages/web/src/store/application/ui/withdraw-usdc/sagas.ts index cfb66f6800..04e6895ce4 100644 --- a/packages/web/src/store/application/ui/withdraw-usdc/sagas.ts +++ b/packages/web/src/store/application/ui/withdraw-usdc/sagas.ts @@ -1,6 +1,5 @@ import { withdrawUSDCActions, - withdrawUSDCSelectors, solanaSelectors, ErrorLevel, SolanaWalletAddress, @@ -47,10 +46,9 @@ const { setDestinationAddress, setDestinationAddressFailed, setDestinationAddressSucceeded, - withdrawUSDCFailed + withdrawUSDCFailed, + withdrawUSDCSucceeded } = withdrawUSDCActions -const { getWithdrawDestinationAddress, getWithdrawAmount } = - withdrawUSDCSelectors const { getFeePayer } = solanaSelectors function* doSetAmount({ payload: { amount } }: ReturnType) { @@ -109,15 +107,18 @@ function* doSetDestinationAddress({ } } -function* doWithdrawUSDC({ payload }: ReturnType) { +/** + * Handles all logic for withdrawing USDC to a given destination. Expects amount in dollars. + */ +function* doWithdrawUSDC({ + payload: { amount, destinationAddress, onSuccess } +}: ReturnType) { try { const libs = yield* call(getLibs) if (!libs.solanaWeb3Manager) { throw new Error('Failed to get solana web3 manager') } // Assume destinationAddress and amount have already been validated - const destinationAddress = yield* select(getWithdrawDestinationAddress) - const amount = yield* select(getWithdrawAmount) if (!destinationAddress || !amount) { throw new Error('Please enter a valid destination address and amount') } @@ -256,9 +257,10 @@ function* doWithdrawUSDC({ payload }: ReturnType) { ) destinationTokenAccount = destinationTokenAccountPubkey.toString() } - const amountWei = new BN(amount).mul( - new BN(TOKEN_LISTING_MAP.USDC.decimals) - ) + // Multiply by 10^6 to account for USDC decimals, but also convert from cents to dollars + const amountWei = new BN(amount) + .mul(new BN(10 ** TOKEN_LISTING_MAP.USDC.decimals)) + .div(new BN(100)) const usdcUserBank = yield* call(getUSDCUserBank) const transferInstructions = yield* call( [ @@ -288,6 +290,8 @@ function* doWithdrawUSDC({ payload }: ReturnType) { 'Withdraw USDC - successfully transferred USDC - tx hash', transferSignature ) + yield* call(onSuccess, transferSignature) + yield* put(withdrawUSDCSucceeded()) } catch (e: unknown) { console.error('Withdraw USDC failed', e) const reportToSentry = yield* getContext('reportToSentry') diff --git a/packages/web/src/utils/tokenInput.ts b/packages/web/src/utils/tokenInput.ts index 89e442d0d3..4435e6bbdf 100644 --- a/packages/web/src/utils/tokenInput.ts +++ b/packages/web/src/utils/tokenInput.ts @@ -30,3 +30,7 @@ export const onTokenInputBlur = (e: FocusEvent) => { .padEnd(PRECISION, '0') return `${whole.length > 0 ? whole : '0'}.${paddedDecimal}` } + +export const toHumanReadable = (value: number) => { + return (value / 100).toFixed(PRECISION) +}