import { t, minSafe, maxSafe, required, vegaPublicKey, addDecimal, formatNumber, } from '@vegaprotocol/react-helpers'; import { Button, FormGroup, Input, InputError, Option, RichSelect, Select, Tooltip, } from '@vegaprotocol/ui-toolkit'; import type { Transfer } from '@vegaprotocol/wallet'; import { normalizeTransfer } from '@vegaprotocol/wallet'; import BigNumber from 'bignumber.js'; import type { ReactNode } from 'react'; import { useCallback, 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; submitTransfer: (transfer: Transfer) => void; } export const TransferForm = ({ pubKey, pubKeys, assets, feeFactor, submitTransfer, }: TransferFormProps) => { const { control, register, watch, handleSubmit, setValue, formState: { errors }, } = useForm(); const amount = watch('amount'); const assetId = watch('asset'); const asset = useMemo(() => { return assets.find((a) => a.id === assetId); }, [assets, assetId]); const onSubmit = useCallback( (fields: FormFields) => { if (!asset) { throw new Error('Submitted transfer with no asset selected'); } const transfer = normalizeTransfer(fields.toAddress, fields.amount, { id: asset.id, decimals: asset.decimals, }); submitTransfer(transfer); }, [asset, submitTransfer] ); 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]); return (
setValue('toAddress', '')} select={ } input={ { if (value === pubKey) { return t('Vega key is the same as current key'); } return true; }, }, })} /> } /> {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} )} {asset.symbol} } {...register('amount', { validate: { required, minSafe: (value) => minSafe(new BigNumber(min))(value), maxSafe: (v) => { const value = new BigNumber(v); if (value.isGreaterThan(max)) { return t( 'You cannot transfer more than your available collateral' ); } return maxSafe(max)(v); }, }, })} /> {errors.amount?.message && ( {errors.amount.message} )} ); }; export const TransferFee = ({ amount, feeFactor, }: { amount: string; feeFactor: string | null; }) => { if (!feeFactor || !amount) return null; // using toFixed without an argument will always return a // number in normal notation without rounding, formatting functions // arent working in a way which won't round the decimal places const value = new BigNumber(amount).times(feeFactor).toFixed(); return (
{t('Transfer fee')}
{value}
); }; interface AddressInputProps { pubKeys: string[] | null; select: ReactNode; input: ReactNode; onChange: () => void; } export const AddressField = ({ pubKeys, select, input, onChange, }: AddressInputProps) => { const [isInput, setIsInput] = useState(() => { if (pubKeys && pubKeys.length <= 1) { return true; } return false; }); return ( <> {isInput ? input : select} {pubKeys && pubKeys.length > 1 && ( )} ); };