diff --git a/README.md b/README.md index f1e797ac9c..b5e263d4aa 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ The platform is composed of multiple Rust crates. Top-level executable binary cr * nym-gateway - acts sort of like a mailbox for mixnet messages, removing the need for directly delivery to potentially offline or firewalled devices. * nym-network-monitor - sends packets through the full system to check that they are working as expected, and stores node uptime histories as the basis of a rewards system ("mixmining" or "proof-of-mixing"). * nym-explorer - a (projected) block explorer and (existing) mixnet viewer. +* nym-wallet (currently in development)- a desktop wallet implemented using the [Tauri](https://tauri.studio/en/docs/about/intro) framework. [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=for-the-badge)](https://opensource.org/licenses/Apache-2.0) [![Build Status](https://img.shields.io/github/workflow/status/nymtech/nym/Continuous%20integration/develop?style=for-the-badge&logo=github-actions)](https://github.com/nymtech/nym/actions?query=branch%3Adevelop) diff --git a/tauri-wallet/README.md b/tauri-wallet/README.md new file mode 100644 index 0000000000..db954805b7 --- /dev/null +++ b/tauri-wallet/README.md @@ -0,0 +1,19 @@ + + +# Nym Tauri Wallet + +A Rust and Tauri desktop wallet implementation. + +## Installation prerequisites + +* `Yarn` +* `NodeJS >= v16.8.0` +* `Rust & cargo >= v1.51` + +## Installation & usage + +* `yarn install` +* `yarn dev` diff --git a/tauri-wallet/public/index.html b/tauri-wallet/public/index.html index 7d20a22d58..7255670d07 100644 --- a/tauri-wallet/public/index.html +++ b/tauri-wallet/public/index.html @@ -1,7 +1,7 @@ - Tauri Web Wallet + Nym Wallet
diff --git a/tauri-wallet/src/components/AdminForm.tsx b/tauri-wallet/src/components/AdminForm.tsx index 9c11370ae0..f3ac868a72 100644 --- a/tauri-wallet/src/components/AdminForm.tsx +++ b/tauri-wallet/src/components/AdminForm.tsx @@ -1,7 +1,8 @@ -import React, { useContext } from 'react' +import React, { useContext, useEffect, useState } from 'react' import { useForm } from 'react-hook-form' import { Backdrop, + Box, Button, CircularProgress, FormControl, @@ -14,20 +15,45 @@ import { import { useTheme } from '@material-ui/styles' import { ClientContext } from '../context/main' import { NymCard } from '.' +import { getContractParams, setContractParams } from '../requests' +import { TauriStateParams } from '../types' export const Admin: React.FC = () => { const { showAdmin, handleShowAdmin } = useContext(ClientContext) + const [isLoading, setIsLoading] = useState(false) + const [params, setParams] = useState() const onCancel = () => { + setParams(undefined) + setIsLoading(false) handleShowAdmin() } + useEffect(() => { + const requestContractParams = async () => { + if (showAdmin) { + setIsLoading(true) + const params = await getContractParams() + setParams(params) + setIsLoading(false) + } + } + requestContractParams() + }, [showAdmin]) + return ( - + {isLoading && ( + + + + )} + {!isLoading && params && ( + + )} @@ -35,14 +61,18 @@ export const Admin: React.FC = () => { ) } -const AdminForm: React.FC<{ onCancel: () => void }> = ({ onCancel }) => { +const AdminForm: React.FC<{ + params: TauriStateParams + onCancel: () => void +}> = ({ params, onCancel }) => { const { register, handleSubmit, formState: { errors, isSubmitting }, - } = useForm() + } = useForm({ defaultValues: { ...params } }) - const onSubmit = (data: any) => { + const onSubmit = async (data: TauriStateParams) => { + await setContractParams(data) console.log(data) onCancel() } @@ -51,110 +81,112 @@ const AdminForm: React.FC<{ onCancel: () => void }> = ({ onCancel }) => { return ( -
+
diff --git a/tauri-wallet/src/components/NodeTypeSelector.tsx b/tauri-wallet/src/components/NodeTypeSelector.tsx index db235fe78e..b949eb00d3 100644 --- a/tauri-wallet/src/components/NodeTypeSelector.tsx +++ b/tauri-wallet/src/components/NodeTypeSelector.tsx @@ -9,9 +9,11 @@ import React from 'react' import { EnumNodeType } from '../types/global' export const NodeTypeSelector = ({ + disabled, nodeType, setNodeType, }: { + disabled: boolean nodeType: EnumNodeType setNodeType: (nodeType: EnumNodeType) => void }) => { @@ -32,11 +34,13 @@ export const NodeTypeSelector = ({ value={EnumNodeType.mixnode} control={} label="Mixnode" + disabled={disabled} /> } label="Gateway" + disabled={disabled} /> diff --git a/tauri-wallet/src/context/main.tsx b/tauri-wallet/src/context/main.tsx index 9e8dfe7482..da14162777 100644 --- a/tauri-wallet/src/context/main.tsx +++ b/tauri-wallet/src/context/main.tsx @@ -1,6 +1,6 @@ import React, { createContext, useEffect, useState } from 'react' import { useHistory } from 'react-router-dom' -import { Coin, TClientDetails, TSignInWithMnemonic } from '../types' +import { TClientDetails, TSignInWithMnemonic } from '../types' import { TUseGetBalance, useGetBalance } from '../hooks/useGetBalance' export const ADMIN_ADDRESS = 'punk1h3w4nj7kny5dfyjw2le4vm74z03v9vd4dstpu0' diff --git a/tauri-wallet/src/hooks/useCheckOwnership.ts b/tauri-wallet/src/hooks/useCheckOwnership.ts new file mode 100644 index 0000000000..6022fd0a4b --- /dev/null +++ b/tauri-wallet/src/hooks/useCheckOwnership.ts @@ -0,0 +1,39 @@ +import { useEffect, useState } from 'react' +import { checkGatewayOwnership, checkMixnodeOwnership } from '../requests' +import { EnumNodeType, TNodeOwnership } from '../types' + +export const useCheckOwnership = () => { + const [ownership, setOwnership] = useState({ + hasOwnership: false, + nodeType: undefined, + }) + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState() + + const checkOwnership = async () => { + const status = {} as TNodeOwnership + + setIsLoading(true) + + try { + const ownsMixnode = await checkMixnodeOwnership() + const ownsGateway = await checkGatewayOwnership() + + if (ownsMixnode) { + status.hasOwnership = true + status.nodeType = EnumNodeType.mixnode + } + + if (ownsGateway) { + status.hasOwnership = true + status.nodeType = EnumNodeType.gateway + } + + setOwnership(status) + } catch (e) { + setError(e as string) + } + } + + return { isLoading, error, ownership, checkOwnership } +} diff --git a/tauri-wallet/src/hooks/useGetBalance.tsx b/tauri-wallet/src/hooks/useGetBalance.tsx index b60e1bb4f0..8325cbb736 100644 --- a/tauri-wallet/src/hooks/useGetBalance.tsx +++ b/tauri-wallet/src/hooks/useGetBalance.tsx @@ -11,17 +11,17 @@ export type TUseGetBalance = { export const useGetBalance = (): TUseGetBalance => { const [balance, setBalance] = useState() - const [error, setEror] = useState() + const [error, setError] = useState() const [isLoading, setIsLoading] = useState(false) const fetchBalance = () => { setIsLoading(true) - setEror(undefined) + setError(undefined) invoke('get_balance') .then((balance) => { setBalance(balance as Balance) }) - .catch((e) => setEror(e)) + .catch(setError) setTimeout(() => { setIsLoading(false) }, 1000) diff --git a/tauri-wallet/src/requests/index.ts b/tauri-wallet/src/requests/index.ts index 62629f72c0..ed94e7c503 100644 --- a/tauri-wallet/src/requests/index.ts +++ b/tauri-wallet/src/requests/index.ts @@ -1,5 +1,17 @@ import { invoke } from '@tauri-apps/api' -import { Coin, Operation, TCreateAccount, TSignInWithMnemonic } from '../types' +import { + Balance, + Coin, + DelegationResult, + EnumNodeType, + Gateway, + MixNode, + Operation, + TauriStateParams, + TauriTxResult, + TCreateAccount, + TSignInWithMnemonic, +} from '../types' export const createAccount = async (): Promise => await invoke('create_new_account') @@ -17,3 +29,57 @@ export const majorToMinor = async (amount: string): Promise => export const getGasFee = async (operation: Operation): Promise => await invoke('get_fee', { operation }) + +export const delegate = async ({ + type, + identity, + amount, +}: { + type: EnumNodeType + identity: string + amount: Coin +}): Promise => + await invoke(`delegate_to_${type}`, { identity, amount }) + +export const undelegate = async ({ + type, + identity, +}: { + type: EnumNodeType + identity: string +}): Promise => + await invoke(`undelegate_from_${type}`, { identity }) + +export const send = async (args: { + amount: Coin + address: string + memo: string +}): Promise => await invoke('send', args) +export const checkMixnodeOwnership = async (): Promise => + await invoke('owns_mixnode') + +export const checkGatewayOwnership = async (): Promise => + await invoke('owns_gateway') + +export const bond = async ({ + type, + data, + amount, +}: { + type: EnumNodeType + data: MixNode | Gateway + amount: Coin +}): Promise => await invoke(`bond_${type}`, { [type]: data, bond: amount }) + +export const unbond = async (type: EnumNodeType) => + await invoke(`unbond_${type}`) + +export const getBalance = async (): Promise => + await invoke('get_balance') + +export const getContractParams = async (): Promise => + await invoke('get_state_params') + +export const setContractParams = async ( + params: TauriStateParams +): Promise => await invoke('update_state_params', { params }) diff --git a/tauri-wallet/src/routes/bond/BondForm.tsx b/tauri-wallet/src/routes/bond/BondForm.tsx index 710881b6e1..8d1152bed9 100644 --- a/tauri-wallet/src/routes/bond/BondForm.tsx +++ b/tauri-wallet/src/routes/bond/BondForm.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useContext } from 'react' import { Button, Checkbox, @@ -11,14 +11,16 @@ import { Theme, } from '@material-ui/core' import { useTheme } from '@material-ui/styles' -import { useForm } from 'react-hook-form' +import { Alert } from '@material-ui/lab' import { yupResolver } from '@hookform/resolvers/yup' +import { useForm } from 'react-hook-form' import { EnumNodeType } from '../../types/global' import { NodeTypeSelector } from '../../components/NodeTypeSelector' +import { bond, majorToMinor } from '../../requests' import { validationSchema } from './validationSchema' import { Coin, Gateway, MixNode } from '../../types' -import { invoke } from '@tauri-apps/api' -import { Alert } from '@material-ui/lab' +import { ClientContext } from '../../context/main' +import { checkHasEnoughFunds } from '../../utils' type TBondFormFields = { withAdvancedOptions: boolean @@ -57,29 +59,27 @@ const formatData = (data: TBondFormFields) => { host: data.host, version: data.version, mix_port: data.mixPort, - amount: data.amount, - nodeType: data.nodeType, } if (data.nodeType === EnumNodeType.mixnode) { payload.verloc_port = data.verlocPort payload.http_api_port = data.httpApiPort - return payload as MixNode & { amount: number; nodeType: EnumNodeType } - } - - if (data.nodeType == EnumNodeType.gateway) { + return payload as MixNode + } else { payload.clients_port = data.clientsPort payload.location = data.location - return payload as Gateway & { amount: number; nodeType: EnumNodeType } + return payload as Gateway } } export const BondForm = ({ + disabled, fees, onError, onSuccess, }: { - fees: { [key in EnumNodeType]: Coin } + disabled: boolean + fees?: { [key in EnumNodeType]: Coin } onError: (message?: string) => void onSuccess: (message?: string) => void }) => { @@ -87,6 +87,7 @@ export const BondForm = ({ register, handleSubmit, setValue, + setError, watch, formState: { errors, isSubmitting }, } = useForm({ @@ -94,6 +95,8 @@ export const BondForm = ({ defaultValues, }) + const { getBalance } = useContext(ClientContext) + const watchNodeType = watch('nodeType', defaultValues.nodeType) const watchAdvancedOptions = watch( 'withAdvancedOptions', @@ -101,13 +104,18 @@ export const BondForm = ({ ) const onSubmit = async (data: TBondFormFields) => { + const hasEnoughFunds = await checkHasEnoughFunds(data.amount) + if (!hasEnoughFunds) { + return setError('amount', { message: 'Not enough funds in wallet' }) + } + const formattedData = formatData(data) - await invoke(`bond_${data.nodeType}`, { - [data.nodeType]: formattedData, - bond: { amount: formattedData?.amount, denom: 'punk' }, - }) - .then((res: any) => { - onSuccess(res) + const amount = await majorToMinor(data.amount) + + await bond({ type: data.nodeType, data: formattedData, amount }) + .then(() => { + getBalance.fetchBalance() + onSuccess(`Successfully bonded to ${data.identityKey}`) }) .catch((e) => { onError(e) @@ -129,17 +137,20 @@ export const BondForm = ({ if (nodeType === EnumNodeType.mixnode) setValue('location', undefined) }} + disabled={disabled} /> - - - {`A fee of ${ - watchNodeType === EnumNodeType.mixnode - ? fees.mixnode.amount - : fees.gateway.amount - } PUNK will apply to this transaction`} - - + {fees && ( + + + {`A fee of ${ + watchNodeType === EnumNodeType.mixnode + ? fees.mixnode.amount + : fees.gateway.amount + } PUNK will apply to this transaction`} + + + )} @@ -165,6 +177,7 @@ export const BondForm = ({ error={!!errors.sphinxKey} helperText={errors.sphinxKey?.message} fullWidth + disabled={disabled} /> @@ -183,6 +196,7 @@ export const BondForm = ({ punks ), }} + disabled={disabled} /> @@ -197,6 +211,7 @@ export const BondForm = ({ fullWidth error={!!errors.host} helperText={errors.host?.message} + disabled={disabled} /> @@ -213,6 +228,7 @@ export const BondForm = ({ fullWidth error={!!errors.location} helperText={errors.location?.message} + disabled={disabled} /> )} @@ -228,6 +244,7 @@ export const BondForm = ({ fullWidth error={!!errors.version} helperText={errors.version?.message} + disabled={disabled} /> @@ -275,6 +292,7 @@ export const BondForm = ({ helperText={ errors.mixPort?.message && 'A valid port value is required' } + disabled={disabled} /> {watchNodeType === EnumNodeType.mixnode ? ( @@ -292,6 +310,7 @@ export const BondForm = ({ errors.verlocPort?.message && 'A valid port value is required' } + disabled={disabled} /> @@ -308,6 +327,7 @@ export const BondForm = ({ errors.httpApiPort?.message && 'A valid port value is required' } + disabled={disabled} /> @@ -325,6 +345,7 @@ export const BondForm = ({ errors.clientsPort?.message && 'A valid port value is required' } + disabled={disabled} /> )} @@ -343,7 +364,7 @@ export const BondForm = ({ }} > + } + style={{ margin: theme.spacing(2) }} + > + {`Looks like you already have a ${ownership.nodeType} bonded.`} + + )} {status === EnumRequestStatus.loading && ( { )} {status === EnumRequestStatus.initial && ( { setMessage(e) setStatus(EnumRequestStatus.error) @@ -59,6 +84,7 @@ export const Bond = () => { setMessage(message) setStatus(EnumRequestStatus.success) }} + disabled={ownership?.hasOwnership} /> )} {(status === EnumRequestStatus.error || @@ -88,9 +114,10 @@ export const Bond = () => {
diff --git a/tauri-wallet/src/routes/delegate/DelegateForm.tsx b/tauri-wallet/src/routes/delegate/DelegateForm.tsx index 5b415b00c2..fb32dc2c44 100644 --- a/tauri-wallet/src/routes/delegate/DelegateForm.tsx +++ b/tauri-wallet/src/routes/delegate/DelegateForm.tsx @@ -14,10 +14,10 @@ import { NodeTypeSelector } from '../../components/NodeTypeSelector' import { EnumNodeType, TFee } from '../../types' import { yupResolver } from '@hookform/resolvers/yup' import { validationSchema } from './validationSchema' -import { invoke } from '@tauri-apps/api' import { Alert } from '@material-ui/lab' import { ClientContext } from '../../context/main' -import { majorToMinor } from '../../requests' +import { delegate, majorToMinor } from '../../requests' +import { checkHasEnoughFunds } from '../../utils' type TDelegateForm = { nodeType: EnumNodeType @@ -46,6 +46,7 @@ export const DelegateForm = ({ setValue, watch, handleSubmit, + setError, formState: { errors, isSubmitting }, } = useForm({ defaultValues, @@ -57,15 +58,24 @@ export const DelegateForm = ({ const { getBalance } = useContext(ClientContext) const onSubmit = async (data: TDelegateForm) => { + const hasEnoughFunds = await checkHasEnoughFunds(data.amount) + if (!hasEnoughFunds) { + return setError('amount', { + message: 'Not enough funds in wallet', + }) + } + const amount = await majorToMinor(data.amount) - await invoke(`delegate_to_${data.nodeType}`, { + await delegate({ + type: data.nodeType, identity: data.identity, amount, }) - .then((res: any) => { - console.log(res) - onSuccess(res) + .then((res) => { + onSuccess( + `Successfully delegated ${data.amount} punk to ${res.source_address}` + ) getBalance.fetchBalance() }) .catch((e) => { @@ -83,6 +93,7 @@ export const DelegateForm = ({ setValue('nodeType', nodeType)} + disabled={isSubmitting} /> diff --git a/tauri-wallet/src/routes/delegate/index.tsx b/tauri-wallet/src/routes/delegate/index.tsx index b128c7168c..f329e6c126 100644 --- a/tauri-wallet/src/routes/delegate/index.tsx +++ b/tauri-wallet/src/routes/delegate/index.tsx @@ -8,7 +8,7 @@ import { EnumRequestStatus, RequestStatus, } from '../../components/RequestStatus' -import { Alert } from '@material-ui/lab' +import { Alert, AlertTitle } from '@material-ui/lab' import { TFee } from '../../types' import { getGasFee } from '../../requests' @@ -76,7 +76,12 @@ export const Delegate = () => { An error occurred with the request: {message} } - Success={{message}} + Success={ + + Delegation complete + {message} + + } />
{ setStatus(EnumRequestStatus.initial) }} > - Resend? + Finish
diff --git a/tauri-wallet/src/routes/send/SendWizard.tsx b/tauri-wallet/src/routes/send/SendWizard.tsx index 11468a7e5d..ee03bab9aa 100644 --- a/tauri-wallet/src/routes/send/SendWizard.tsx +++ b/tauri-wallet/src/routes/send/SendWizard.tsx @@ -3,14 +3,14 @@ import { useForm, FormProvider } from 'react-hook-form' import { yupResolver } from '@hookform/resolvers/yup' import { Button, Step, StepLabel, Stepper, Theme } from '@material-ui/core' import { useTheme } from '@material-ui/styles' -import { invoke } from '@tauri-apps/api' import { SendForm } from './SendForm' import { SendReview } from './SendReview' import { SendConfirmation } from './SendConfirmation' import { ClientContext } from '../../context/main' import { validationSchema } from './validationSchema' -import { TauriTxResult } from '../../types/rust/tauritxresult' -import { majorToMinor } from '../../requests' +import { TauriTxResult } from '../../types' +import { majorToMinor, send } from '../../requests' +import { checkHasEnoughFunds } from '../../utils' const defaultValues = { amount: '', @@ -59,31 +59,40 @@ export const SendWizard = () => { } const handleSend = async () => { - setIsLoading(true) - setActiveStep((s) => s + 1) const formState = methods.getValues() - const amount = await majorToMinor(formState.amount) - invoke('send', { - amount, - address: formState.to, - memo: formState.memo, - }) - .then((res: any) => { - const { details } = res as TauriTxResult - setActiveStep((s) => s + 1) - setConfirmedData({ - ...details, - amount: { denom: 'punk', amount: formState.amount }, + const hasEnoughFunds = await checkHasEnoughFunds(formState.amount) + if (!hasEnoughFunds) { + methods.setError('amount', { + message: 'Not enough funds in wallet', + }) + return handlePreviousStep() + } else { + setIsLoading(true) + setActiveStep((s) => s + 1) + const amount = await majorToMinor(formState.amount) + + send({ + amount, + address: formState.to, + memo: formState.memo, + }) + .then((res: any) => { + const { details } = res as TauriTxResult + setActiveStep((s) => s + 1) + setConfirmedData({ + ...details, + amount: { denom: 'Major', amount: formState.amount }, + }) + setIsLoading(false) + getBalance.fetchBalance() }) - setIsLoading(false) - getBalance.fetchBalance() - }) - .catch((e) => { - setRequestError(e) - setIsLoading(false) - console.log(e) - }) + .catch((e) => { + setRequestError(e) + setIsLoading(false) + console.log(e) + }) + } } return ( diff --git a/tauri-wallet/src/routes/unbond/UnbondForm.tsx b/tauri-wallet/src/routes/unbond/UnbondForm.tsx deleted file mode 100644 index 10fa86a8ca..0000000000 --- a/tauri-wallet/src/routes/unbond/UnbondForm.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useState } from 'react' -import { Alert } from '@material-ui/lab' -import { Button, Theme } from '@material-ui/core' -import { useTheme } from '@material-ui/styles' - -export const UnbondForm = () => { - const theme: Theme = useTheme() - return ( -
- - You don't currently have a bonded node - -
- -
-
- ) -} diff --git a/tauri-wallet/src/routes/unbond/index.tsx b/tauri-wallet/src/routes/unbond/index.tsx index ff768ab63e..9ae5ed6f01 100644 --- a/tauri-wallet/src/routes/unbond/index.tsx +++ b/tauri-wallet/src/routes/unbond/index.tsx @@ -1,13 +1,68 @@ -import React from 'react' +import React, { useContext, useEffect, useState } from 'react' import { NymCard } from '../../components' import { UnbondForm } from './UnbondForm' import { Layout } from '../../layouts' +import { useCheckOwnership } from '../../hooks/useCheckOwnership' +import { Alert } from '@material-ui/lab' +import { Box, Button, CircularProgress, Theme } from '@material-ui/core' +import { ClientContext } from '../../context/main' +import { unbond } from '../../requests' +import { useTheme } from '@material-ui/styles' export const Unbond = () => { + const [isLoading, setIsLoading] = useState(false) + const { checkOwnership, ownership } = useCheckOwnership() + const { getBalance } = useContext(ClientContext) + + const theme: Theme = useTheme() + + useEffect(() => { + const initialiseForm = async () => { + await checkOwnership() + } + initialiseForm() + }, [ownership.hasOwnership, checkOwnership]) + return ( - + {ownership?.hasOwnership && ( + { + setIsLoading(true) + await unbond(ownership.nodeType!) + getBalance.fetchBalance() + setIsLoading(false) + }} + > + Unbond + + } + style={{ margin: theme.spacing(2) }} + > + {`Looks like you already have a ${ownership.nodeType} bonded.`} + + )} + {!ownership.hasOwnership && ( + + You don't currently have a bonded node + + )} + {isLoading && ( + + + + )} ) diff --git a/tauri-wallet/src/routes/undelegate/UndelegateForm.tsx b/tauri-wallet/src/routes/undelegate/UndelegateForm.tsx index b724bb5586..61bdf0b920 100644 --- a/tauri-wallet/src/routes/undelegate/UndelegateForm.tsx +++ b/tauri-wallet/src/routes/undelegate/UndelegateForm.tsx @@ -10,12 +10,12 @@ import { } from '@material-ui/core' import { Alert } from '@material-ui/lab' import { useTheme } from '@material-ui/styles' -import { invoke } from '@tauri-apps/api' import { yupResolver } from '@hookform/resolvers/yup' import { validationSchema } from './validationSchema' import { NodeTypeSelector } from '../../components/NodeTypeSelector' import { EnumNodeType, TFee } from '../../types' import { ClientContext } from '../../context/main' +import { minorToMajor, undelegate } from '../../requests' type TFormData = { nodeType: EnumNodeType @@ -50,11 +50,12 @@ export const UndelegateForm = ({ const { getBalance } = useContext(ClientContext) const onSubmit = async (data: TFormData) => { - await invoke(`undelegate_from_${data.nodeType}`, { + await undelegate({ + type: data.nodeType, identity: data.identity, }) - .then((res: any) => { - onSuccess(res) + .then(async (res) => { + onSuccess(`Successfully undelegated from ${res.source_address}`) getBalance.fetchBalance() }) .catch((e) => onError(e)) diff --git a/tauri-wallet/src/routes/undelegate/index.tsx b/tauri-wallet/src/routes/undelegate/index.tsx index f51c4cf3b1..833825ad4b 100644 --- a/tauri-wallet/src/routes/undelegate/index.tsx +++ b/tauri-wallet/src/routes/undelegate/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react' -import { Alert } from '@material-ui/lab' +import { Alert, AlertTitle } from '@material-ui/lab' import { useTheme } from '@material-ui/styles' import { NymCard } from '../../components' import { UndelegateForm } from './UndelegateForm' @@ -76,7 +76,13 @@ export const Undelegate = () => { An error occurred with the request: {message} } - Success={{message}} + Success={ + + {' '} + Undelegation complete + {message} + + } /> )} diff --git a/tauri-wallet/src/types/global.ts b/tauri-wallet/src/types/global.ts index cfb41568f2..672f1629d6 100644 --- a/tauri-wallet/src/types/global.ts +++ b/tauri-wallet/src/types/global.ts @@ -6,8 +6,8 @@ export enum EnumNodeType { } export type TNodeOwnership = { - ownsMixnode: boolean - ownsGateway: boolean + hasOwnership: boolean + nodeType?: EnumNodeType } export type TClientDetails = { diff --git a/tauri-wallet/src/types/rust/index.ts b/tauri-wallet/src/types/rust/index.ts index 5d2ff697fc..8e22182af6 100644 --- a/tauri-wallet/src/types/rust/index.ts +++ b/tauri-wallet/src/types/rust/index.ts @@ -1,8 +1,11 @@ +export * from './account' export * from './balance' export * from './coin' -export * from './mixnode' +export * from './delegationresult' +export * from './denom' export * from './gateway' export * from './mixnode' +export * from './operation' +export * from './stateparams' export * from './tauritxresult' export * from './transactiondetails' -export * from './operation' diff --git a/tauri-wallet/src/utils/index.ts b/tauri-wallet/src/utils/index.ts index f64530eee5..d07271ad0c 100644 --- a/tauri-wallet/src/utils/index.ts +++ b/tauri-wallet/src/utils/index.ts @@ -1,6 +1,7 @@ import { invoke } from '@tauri-apps/api' import bs58 from 'bs58' import { minor, valid } from 'semver' +import { getBalance, majorToMinor } from '../requests' import { Coin } from '../types' export const validateKey = (key: string): boolean => { @@ -75,7 +76,6 @@ export const validateVersion = (version: string): boolean => { const validVersion = valid(version) return validVersion !== null && minorVersion >= 11 } catch (e) { - console.log(e) return false } } @@ -90,3 +90,9 @@ export const validateRawPort = (rawPort: number): boolean => export const truncate = (text: string, trim: number) => text.substring(0, trim) + '...' + +export const checkHasEnoughFunds = async (allocationValue: string) => { + const minorValue = await majorToMinor(allocationValue) + const walletValue = await getBalance() + return !(+walletValue.coin.amount - +minorValue.amount < 0) +}