From d552bf768fef2635c92e7fb4068003f4a7b63a5c Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Mon, 30 Jan 2023 18:37:55 -0800 Subject: [PATCH] feat: add transfer dialog and form --- .../accounts-container/accounts-container.tsx | 8 +- apps/trading/pages/dialogs-container.tsx | 2 + libs/accounts/src/lib/index.ts | 1 + libs/accounts/src/lib/transfer-container.tsx | 45 ++++ libs/accounts/src/lib/transfer-dialog.tsx | 28 +++ libs/accounts/src/lib/transfer-form.tsx | 226 ++++++++++++++++++ .../src/hooks/use-network-params.ts | 1 + 7 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 libs/accounts/src/lib/transfer-container.tsx create mode 100644 libs/accounts/src/lib/transfer-dialog.tsx create mode 100644 libs/accounts/src/lib/transfer-form.tsx diff --git a/apps/trading/components/accounts-container/accounts-container.tsx b/apps/trading/components/accounts-container/accounts-container.tsx index 79bc4329c..a66a0c625 100644 --- a/apps/trading/components/accounts-container/accounts-container.tsx +++ b/apps/trading/components/accounts-container/accounts-container.tsx @@ -5,7 +5,7 @@ import { useWithdrawalDialog } from '@vegaprotocol/withdraws'; import { useAssetDetailsDialogStore } from '@vegaprotocol/assets'; import { Splash } from '@vegaprotocol/ui-toolkit'; import { useVegaWallet } from '@vegaprotocol/wallet'; -import { AccountManager } from '@vegaprotocol/accounts'; +import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts'; import { useDepositDialog } from '@vegaprotocol/deposits'; export const AccountsContainer = () => { @@ -13,6 +13,7 @@ export const AccountsContainer = () => { const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore(); const openWithdrawalDialog = useWithdrawalDialog((store) => store.open); const openDepositDialog = useDepositDialog((store) => store.open); + const openTransferDialog = useTransferDialog((store) => store.open); const onClickAsset = useCallback( (assetId?: string) => { @@ -41,7 +42,10 @@ export const AccountsContainer = () => { /> {!isReadOnly && ( -
+
+ diff --git a/apps/trading/pages/dialogs-container.tsx b/apps/trading/pages/dialogs-container.tsx index c03576ac4..6f0bbeb77 100644 --- a/apps/trading/pages/dialogs-container.tsx +++ b/apps/trading/pages/dialogs-container.tsx @@ -8,6 +8,7 @@ import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws'; import { DepositDialog } from '@vegaprotocol/deposits'; import { Web3ConnectUncontrolledDialog } from '@vegaprotocol/web3'; import { WelcomeDialog } from '../components/welcome-dialog'; +import { TransferDialog } from '@vegaprotocol/accounts'; const DialogsContainer = () => { const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore(); @@ -25,6 +26,7 @@ const DialogsContainer = () => { + ); }; diff --git a/libs/accounts/src/lib/index.ts b/libs/accounts/src/lib/index.ts index d3b35a103..ce1402607 100644 --- a/libs/accounts/src/lib/index.ts +++ b/libs/accounts/src/lib/index.ts @@ -7,3 +7,4 @@ export * from './breakdown-table'; export * from './use-account-balance'; export * from './get-settlement-account'; export * from './use-market-account-balance'; +export * from './transfer-dialog'; diff --git a/libs/accounts/src/lib/transfer-container.tsx b/libs/accounts/src/lib/transfer-container.tsx new file mode 100644 index 000000000..29c6535c0 --- /dev/null +++ b/libs/accounts/src/lib/transfer-container.tsx @@ -0,0 +1,45 @@ +import * as Schema from '@vegaprotocol/types'; +import { + addDecimal, + NetworkParams, + useDataProvider, + useNetworkParam, +} from '@vegaprotocol/react-helpers'; +import { useVegaWallet } from '@vegaprotocol/wallet'; +import { useMemo } from 'react'; +import { accountsDataProvider } from './accounts-data-provider'; +import { TransferForm } from './transfer-form'; + +export const TransferContainer = () => { + const { pubKey, pubKeys } = useVegaWallet(); + const { param } = useNetworkParam(NetworkParams.transfer_fee_factor); + const { data } = useDataProvider({ + dataProvider: accountsDataProvider, + variables: { partyId: pubKey }, + skip: !pubKey, + }); + + const assets = useMemo(() => { + if (!data) return []; + return data + .filter( + (account) => account.type === Schema.AccountType.ACCOUNT_TYPE_GENERAL + ) + .map((account) => ({ + id: account.asset.id, + symbol: account.asset.symbol, + name: account.asset.name, + decimals: account.asset.decimals, + balance: addDecimal(account.balance, account.asset.decimals), + })); + }, [data]); + + return ( + pk.publicKey) : null} + assets={assets} + feeFactor={param} + /> + ); +}; diff --git a/libs/accounts/src/lib/transfer-dialog.tsx b/libs/accounts/src/lib/transfer-dialog.tsx new file mode 100644 index 000000000..998eaf253 --- /dev/null +++ b/libs/accounts/src/lib/transfer-dialog.tsx @@ -0,0 +1,28 @@ +import { t } from '@vegaprotocol/react-helpers'; +import { Dialog } from '@vegaprotocol/ui-toolkit'; +import { create } from 'zustand'; +import { TransferContainer } from './transfer-container'; + +interface State { + isOpen: boolean; +} + +interface Actions { + open: (open?: boolean) => void; +} + +export const useTransferDialog = create((set) => ({ + isOpen: false, + open: (open = true) => { + set(() => ({ isOpen: open })); + }, +})); + +export const TransferDialog = () => { + const { isOpen, open } = useTransferDialog(); + return ( + + + + ); +}; diff --git a/libs/accounts/src/lib/transfer-form.tsx b/libs/accounts/src/lib/transfer-form.tsx new file mode 100644 index 000000000..d8ac7492c --- /dev/null +++ b/libs/accounts/src/lib/transfer-form.tsx @@ -0,0 +1,226 @@ +import { + t, + minSafe, + maxSafe, + required, + vegaPublicKey, + addDecimal, + formatNumber, +} from '@vegaprotocol/react-helpers'; +import * as Schema from '@vegaprotocol/types'; +import { + Button, + FormGroup, + Input, + InputError, + Option, + RichSelect, + Select, +} from '@vegaprotocol/ui-toolkit'; +import BigNumber from 'bignumber.js'; +import { useMemo, useState } from 'react'; +import { Controller, useForm } from 'react-hook-form'; + +interface FormFields { + toAddress: string; + asset: string; + amount: string; +} + +interface TransferFormProps { + pubKey: string | null; + pubKeys: string[] | null; + assets: Array<{ + id: string; + symbol: string; + name: string; + decimals: number; + balance: string; + }>; + feeFactor: string | null; +} + +export const TransferForm = ({ + pubKey, + pubKeys, + assets, + feeFactor, +}: TransferFormProps) => { + const [manualAddressEntry, setManualAddressEntry] = useState(false); + const { + control, + register, + watch, + handleSubmit, + setValue, + formState: { errors }, + } = useForm(); + + const onSubmit = (fields: FormFields) => { + console.log(fields); + }; + + const amount = watch('amount'); + const assetId = watch('asset'); + + const asset = useMemo(() => { + return assets.find((a) => a.id === assetId); + }, [assets, assetId]); + + const min = useMemo(() => { + // Min viable amount given asset decimals EG for WEI 0.000000000000000001 + const minViableAmount = asset + ? new BigNumber(addDecimal('1', asset.decimals)) + : new BigNumber(0); + + return minViableAmount; + }, [asset]); + + const max = useMemo(() => { + const maxAmount = asset ? new BigNumber(asset.balance) : new BigNumber(0); + + return maxAmount; + }, [asset]); + + console.log(asset, max.toString()); + + return ( +
+ + + + + {!manualAddressEntry && pubKeys?.length ? ( + + ) : ( + + )} + + {errors.toAddress?.message && ( + + {errors.toAddress.message} + + )} + + + ( + { + field.onChange(value); + }} + placeholder={t('Please select')} + value={field.value} + > + {assets.map((a) => ( + + ))} + + )} + /> + {errors.asset?.message && ( + {errors.asset.message} + )} + + + + + + minSafe(new BigNumber(min))(value), + maxSafe: (v) => { + const value = new BigNumber(v); + console.log(value.toString(), max.toString()); + if (value.isGreaterThan(max)) { + return t( + 'You cannot transfer more than your available collateral' + ); + } + return maxSafe(max)(v); + }, + }, + })} + /> + {errors.amount?.message && ( + {errors.amount.message} + )} + + + + + ); +}; + +const TransferFee = ({ + amount, + feeFactor, +}: { + amount: string; + feeFactor: string | null; +}) => { + if (!feeFactor || !amount) return null; + const value = new BigNumber(amount).times(feeFactor).toString(); + + return
Transfer fee: {value.toString()}
; +}; diff --git a/libs/react-helpers/src/hooks/use-network-params.ts b/libs/react-helpers/src/hooks/use-network-params.ts index 8f8081ae0..aae942a29 100644 --- a/libs/react-helpers/src/hooks/use-network-params.ts +++ b/libs/react-helpers/src/hooks/use-network-params.ts @@ -104,6 +104,7 @@ export const NetworkParams = { market_liquidity_stakeToCcyVolume: 'market_liquidity_stakeToCcyVolume', market_liquidity_targetstake_triggering_ratio: 'market_liquidity_targetstake_triggering_ratio', + transfer_fee_factor: 'transfer.fee.factor', } as const; type Params = typeof NetworkParams;