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 = ({
}}
>
{
- const [status, setStatus] = useState(EnumRequestStatus.loading)
+ const [status, setStatus] = useState(EnumRequestStatus.initial)
const [message, setMessage] = useState()
const [fees, setFees] = useState()
+ const { checkOwnership, ownership } = useCheckOwnership()
+ const { getBalance } = useContext(ClientContext)
+
const theme: Theme = useTheme()
useEffect(() => {
- const getFees = async () => {
- const mixnode = await getGasFee('BondMixnode')
- const gateway = await getGasFee('BondGateway')
- setFees({
- mixnode: mixnode,
- gateway: gateway,
- })
- setStatus(EnumRequestStatus.initial)
+ if (status === EnumRequestStatus.initial) {
+ const initialiseForm = async () => {
+ await checkOwnership()
+ setFees({
+ mixnode: await getGasFee('BondMixnode'),
+ gateway: await getGasFee('BondGateway'),
+ })
+ setStatus(EnumRequestStatus.initial)
+ }
+ initialiseForm()
}
+ }, [status])
- getFees()
- }, [])
-
- console.log(fees, status, message)
return (
+ {ownership?.hasOwnership && (
+ {
+ setStatus(EnumRequestStatus.loading)
+ await unbond(ownership.nodeType!)
+ getBalance.fetchBalance()
+ setStatus(EnumRequestStatus.initial)
+ }}
+ >
+ Unbond
+
+ }
+ 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 = () => {
{
setStatus(EnumRequestStatus.initial)
+ checkOwnership()
}}
>
- Resend?
+ Again?
>
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