Feature/wallet bonding fixes (#5064)

* bonding and unbonding for nym nodes
This commit is contained in:
Fouad
2024-10-30 17:15:38 +00:00
committed by GitHub
parent 07ff2639ec
commit c90ebf0a6a
15 changed files with 203 additions and 118 deletions
+34 -34
View File
@@ -173,7 +173,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -658,7 +658,7 @@ dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1022,7 +1022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
dependencies = [
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1083,7 +1083,7 @@ checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1751,7 +1751,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -1931,7 +1931,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3667,7 +3667,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3854,7 +3854,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -3983,7 +3983,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4140,7 +4140,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4151,9 +4151,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.79"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-ident",
]
@@ -4178,7 +4178,7 @@ dependencies = [
"itertools 0.11.0",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4704,7 +4704,7 @@ dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4798,9 +4798,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
dependencies = [
"serde_derive",
]
@@ -4825,13 +4825,13 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.210"
version = "1.0.214"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -4842,14 +4842,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
name = "serde_json"
version = "1.0.128"
version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [
"itoa 1.0.9",
"memchr",
@@ -4865,7 +4865,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5219,7 +5219,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5262,9 +5262,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.55"
version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0"
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [
"proc-macro2",
"quote",
@@ -5769,7 +5769,7 @@ checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -5856,7 +5856,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6021,7 +6021,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6097,7 +6097,7 @@ checksum = "0ea0b99e8ec44abd6f94a18f28f7934437809dd062820797c52401298116f70e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"termcolor",
]
@@ -6201,7 +6201,7 @@ dependencies = [
"proc-macro-error",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -6316,7 +6316,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"wasm-bindgen-shared",
]
@@ -6350,7 +6350,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -6979,7 +6979,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.55",
"syn 2.0.85",
]
[[package]]
@@ -116,12 +116,12 @@ pub async fn generate_gateway_bonding_msg_payload(
#[tauri::command]
pub async fn generate_nym_node_bonding_msg_payload(
nym_node: NymNode,
nymnode: NymNode,
cost_params: NodeCostParams,
pledge: DecCoin,
state: tauri::State<'_, WalletState>,
) -> Result<String, BackendError> {
nym_node_bonding_msg_payload(nym_node, cost_params, pledge, state).await
nym_node_bonding_msg_payload(nymnode, cost_params, pledge, state).await
}
#[tauri::command]
@@ -2,15 +2,15 @@ import React, { createContext, useContext, useMemo, useState } from 'react';
import { CurrencyDenom } from '@nymproject/types';
import { TBondNymNodeArgs, TBondMixNodeArgs } from 'src/types';
const defaultNymNodeValues: TBondNymNodeArgs['nymNode'] = {
const defaultNymNodeValues: TBondNymNodeArgs['nymnode'] = {
identity_key: 'H6rXWgsW89QsVyaNSS3qBe9zZFLhBS6Gn3YRkGFSoFW9',
custom_http_port: 1,
custom_http_port: null,
host: '1.1.1.1',
};
const defaultCostParams = (denom: CurrencyDenom): TBondNymNodeArgs['costParams'] => ({
interval_operating_cost: { amount: '40', denom },
profit_margin_percent: '10',
profit_margin_percent: '40',
});
const defaultAmount = (denom: CurrencyDenom): TBondMixNodeArgs['pledge'] => ({
@@ -21,14 +21,14 @@ const defaultAmount = (denom: CurrencyDenom): TBondMixNodeArgs['pledge'] => ({
interface FormContextType {
step: 1 | 2 | 3 | 4;
setStep: React.Dispatch<React.SetStateAction<1 | 2 | 3 | 4>>;
nymNodeData: TBondNymNodeArgs['nymNode'];
setNymNodeData: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['nymNode']>>;
nymNodeData: TBondNymNodeArgs['nymnode'];
setNymNodeData: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['nymnode']>>;
costParams: TBondNymNodeArgs['costParams'];
setCostParams: React.Dispatch<React.SetStateAction<TBondNymNodeArgs['costParams']>>;
amountData: TBondMixNodeArgs['pledge'];
setAmountData: React.Dispatch<React.SetStateAction<TBondMixNodeArgs['pledge']>>;
signature?: string;
setSignature: React.Dispatch<React.SetStateAction<string | undefined>>;
signature: string;
setSignature: React.Dispatch<React.SetStateAction<string>>;
onError: (e: string) => void;
}
@@ -41,9 +41,8 @@ const FormContext = createContext<FormContextType>({
setCostParams: () => {},
amountData: defaultAmount('nym'),
setAmountData: () => {},
signature: undefined,
signature: '',
setSignature: () => {},
onError: () => {},
});
@@ -52,10 +51,10 @@ const FormContextProvider = ({ children }: { children: React.ReactNode }) => {
const denom = 'nym';
const [step, setStep] = useState<1 | 2 | 3 | 4>(1);
const [nymNodeData, setNymNodeData] = useState<TBondNymNodeArgs['nymNode']>(defaultNymNodeValues);
const [nymNodeData, setNymNodeData] = useState<TBondNymNodeArgs['nymnode']>(defaultNymNodeValues);
const [costParams, setCostParams] = useState<TBondNymNodeArgs['costParams']>(defaultCostParams(denom));
const [amountData, setAmountData] = useState<TBondNymNodeArgs['pledge']>(defaultAmount(denom));
const [signature, setSignature] = useState<string>();
const [signature, setSignature] = useState('');
const onError = (e: string) => {
console.error(e);
@@ -1,22 +1,12 @@
import React from 'react';
import { Stack, TextField, Box, FormHelperText } from '@mui/material';
import { useForm } from 'react-hook-form';
import { TBondNymNodeArgs } from 'src/types';
import { yupResolver } from '@hookform/resolvers/yup';
import { SimpleModal } from 'src/components/Modals/SimpleModal';
import { CurrencyFormField } from '@nymproject/react/currency/CurrencyFormField';
import { checkHasEnoughFunds } from 'src/utils';
import { nymNodeAmountSchema } from './amountValidationSchema';
const defaultNymNodeCostParamValues: TBondNymNodeArgs['costParams'] = {
profit_margin_percent: '10',
interval_operating_cost: { amount: '40', denom: 'nym' },
};
const defaultNymNodePledgeValue: TBondNymNodeArgs['pledge'] = {
amount: '100',
denom: 'nym',
};
import { useFormContext } from './FormContext';
type NymNodeDataProps = {
onClose: () => void;
@@ -26,6 +16,7 @@ type NymNodeDataProps = {
};
const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
const { setAmountData, setCostParams, amountData, costParams } = useFormContext();
const {
formState: { errors },
register,
@@ -36,21 +27,26 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
} = useForm({
mode: 'all',
defaultValues: {
pledge: defaultNymNodePledgeValue,
...defaultNymNodeCostParamValues,
pledge: amountData,
...costParams,
},
resolver: yupResolver(nymNodeAmountSchema()),
});
console.log(errors, 'errors');
const handleRequestValidation = async () => {
const values = getValues();
const hasSufficientTokens = await checkHasEnoughFunds(values.pledge.amount);
if (hasSufficientTokens) {
handleSubmit(onNext)();
handleSubmit((args) => {
setAmountData(args.pledge);
setCostParams({
profit_margin_percent: args.profit_margin_percent,
interval_operating_cost: args.interval_operating_cost,
});
onNext();
})();
} else {
setError('pledge.amount', { message: 'Not enough tokens' });
}
@@ -77,8 +73,8 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
setValue('pledge.amount', newValue.amount, { shouldValidate: true });
}}
validationError={errors.pledge?.amount?.message}
denom={defaultNymNodePledgeValue.denom}
initialValue={defaultNymNodePledgeValue.amount}
denom={amountData.denom}
initialValue={amountData.amount}
/>
<Box>
@@ -90,8 +86,8 @@ const NymNodeAmount = ({ onClose, onBack, onNext, step }: NymNodeDataProps) => {
setValue('interval_operating_cost', newValue, { shouldValidate: true });
}}
validationError={errors.interval_operating_cost?.amount?.message}
denom={defaultNymNodeCostParamValues.interval_operating_cost.denom}
initialValue={defaultNymNodeCostParamValues.interval_operating_cost.amount}
denom={costParams.interval_operating_cost.denom}
initialValue={costParams.interval_operating_cost.amount}
/>
<FormHelperText>
Monthly operational costs of running your node. If your node is in the active set the amount will be paid
@@ -1,18 +1,12 @@
import React from 'react';
import * as yup from 'yup';
import { Stack, TextField, FormControlLabel, Checkbox } from '@mui/material';
import { useForm } from 'react-hook-form';
import { IdentityKeyFormField } from '@nymproject/react/mixnodes/IdentityKeyFormField';
import { TBondNymNodeArgs } from 'src/types';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';
import { isValidHostname, validateRawPort } from 'src/utils';
import { SimpleModal } from 'src/components/Modals/SimpleModal';
const defaultNymNodeValues: TBondNymNodeArgs['nymNode'] = {
identity_key: 'H6rXWgsW89QsVyaNSS3qBe9zZFLhBS6Gn3YRkGFSoFW9',
custom_http_port: 1,
host: '1.1.1.1',
};
import { useFormContext } from './FormContext';
const yupValidationSchema = yup.object().shape({
identity_key: yup.string().required('Identity key is required'),
@@ -24,8 +18,13 @@ const yupValidationSchema = yup.object().shape({
custom_http_port: yup
.number()
.required('A custom http port is required')
.test('valid-http', 'A valid http port is required', (value) => (value ? validateRawPort(value) : false)),
.nullable()
.test('valid-http', 'A valid http port is required', (value) => {
if (value === null) {
return true;
}
return value ? validateRawPort(value) : false;
}),
});
type NymNodeDataProps = {
@@ -36,6 +35,7 @@ type NymNodeDataProps = {
};
const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
const { setNymNodeData, nymNodeData } = useFormContext();
const {
formState: { errors },
register,
@@ -43,14 +43,17 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
handleSubmit,
} = useForm({
mode: 'all',
defaultValues: defaultNymNodeValues,
defaultValues: nymNodeData,
resolver: yupResolver(yupValidationSchema),
});
const [showAdvancedOptions, setShowAdvancedOptions] = React.useState(false);
const handleNext = async () => {
handleSubmit(onNext)();
handleSubmit((args) => {
setNymNodeData(args);
onNext();
})();
};
return (
@@ -69,7 +72,7 @@ const NymNodeData = ({ onClose, onNext, step }: NymNodeDataProps) => {
required
fullWidth
label="Identity Key"
initialValue={defaultNymNodeValues.identity_key}
initialValue={nymNodeData.identity_key}
errorText={errors.identity_key?.message?.toString()}
onChanged={(value) => setValue('identity_key', value, { shouldValidate: true })}
showTickOnValid={false}
@@ -1,4 +1,5 @@
import React, { useEffect, useState } from 'react';
import * as yup from 'yup';
import { Stack, TextField, Typography } from '@mui/material';
import { useForm } from 'react-hook-form';
import { CopyToClipboard } from 'src/components/CopyToClipboard';
@@ -7,9 +8,11 @@ import { SimpleModal } from 'src/components/Modals/SimpleModal';
import { useBondingContext } from 'src/context';
import { TBondNymNodeArgs } from 'src/types';
import { Signature } from 'src/pages/bonding/types';
import { yupResolver } from '@hookform/resolvers/yup';
import { useFormContext } from './FormContext';
const NymNodeSignature = ({
nymNode,
nymnode,
pledge,
costParams,
step,
@@ -17,27 +20,38 @@ const NymNodeSignature = ({
onClose,
onBack,
}: {
nymNode: TBondNymNodeArgs['nymNode'];
nymnode: TBondNymNodeArgs['nymnode'];
pledge: TBondNymNodeArgs['pledge'];
costParams: TBondNymNodeArgs['costParams'];
step: number;
onNext: (data: Signature) => void;
onNext: () => void;
onClose: () => void;
onBack: () => void;
}) => {
const [message, setMessage] = useState<string>('');
const [error, setError] = useState<string>();
const { generateNymNodeMsgPayload } = useBondingContext();
const { signature, setSignature } = useFormContext();
const yupValidationSchema = yup.object().shape({
signature: yup.string().required('Signature is required'),
});
const {
register,
formState: { errors },
} = useForm<Signature>();
handleSubmit,
} = useForm<Signature>({
defaultValues: {
signature,
},
resolver: yupResolver(yupValidationSchema),
});
const generateMessage = async () => {
try {
const msg = await generateNymNodeMsgPayload({
nymNode,
nymnode,
pledge,
costParams,
});
@@ -53,10 +67,10 @@ const NymNodeSignature = ({
useEffect(() => {
generateMessage();
}, [nymNode, pledge, costParams]);
}, []);
const onSubmit = async (data: Signature) => {
onNext(data);
const handleNext = async () => {
handleSubmit(onNext)();
};
if (error) {
@@ -66,11 +80,7 @@ const NymNodeSignature = ({
return (
<SimpleModal
open
onOk={() =>
onSubmit({
signature: 'signature',
})
}
onOk={handleNext}
onClose={onClose}
header="Bond Nym Node"
subHeader={`Step ${step}/3`}
@@ -95,10 +105,13 @@ const NymNodeSignature = ({
</Stack>
<TextField
{...register('signature')}
onChange={(e) => setSignature(e.target.value)}
id="outlined-multiline-static"
name="signature"
rows={3}
placeholder="Paste Signature"
helperText={errors.signature?.message}
error={Boolean(errors.signature)}
multiline
fullWidth
required
@@ -5,8 +5,8 @@ import { isLessThan, isGreaterThan, validateAmount } from 'src/utils';
const operatingCostAndPmValidation = (params?: TauriContractStateParams) => {
const defaultParams = {
profit_margin_percent: {
minimum: parseFloat(params?.profit_margin.minimum || '0%'),
maximum: parseFloat(params?.profit_margin.maximum || '100%'),
minimum: parseFloat(params?.profit_margin.minimum || '20%'),
maximum: parseFloat(params?.profit_margin.maximum || '50%'),
},
interval_operating_cost: {
@@ -2,18 +2,24 @@ import React, { useContext, useEffect } from 'react';
import { ConfirmTx } from 'src/components/ConfirmTX';
import { ModalListItem } from 'src/components/Modals/ModalListItem';
import { useGetFee } from 'src/hooks/useGetFee';
import { Signature } from 'src/pages/bonding/types';
import { BalanceWarning } from 'src/components/FeeWarning';
import { AppContext } from 'src/context';
import { TBondNymNodeArgs } from 'src/types';
import FormContextProvider, { useFormContext } from '../forms/nym-node/FormContext';
import NymNodeData from '../forms/nym-node/NymNodeData';
import NymNodeAmount from '../forms/nym-node/NymNodeAmount';
import NymNodeSignature from '../forms/nym-node/NymNodeSignature';
export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
export const BondNymNodeModal = ({
onClose,
onBond,
}: {
onClose: () => void;
onBond: (data: TBondNymNodeArgs) => Promise<void>;
}) => {
const { fee, resetFeeState, feeError } = useGetFee();
const { userBalance } = useContext(AppContext);
const { setStep, step, onError, setSignature, amountData, costParams, nymNodeData } = useFormContext();
const { setStep, step, onError, signature, amountData, costParams, nymNodeData } = useFormContext();
useEffect(() => {
if (feeError) {
@@ -21,12 +27,17 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
}
}, [feeError]);
const handleUpdateMixnodeData = async () => {
const handleUpdateNymnodeData = async () => {
setStep(2);
};
const handleUpdateSignature = async (data: Signature) => {
setSignature(data.signature);
const handleBond = async () => {
onBond({
nymnode: nymNodeData,
pledge: amountData,
costParams,
msgSignature: signature,
});
};
const handleConfirm = async () => {};
@@ -51,7 +62,7 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
}
if (step === 1) {
return <NymNodeData onClose={onClose} onBack={onClose} onNext={handleUpdateMixnodeData} step={step} />;
return <NymNodeData onClose={onClose} onBack={onClose} onNext={handleUpdateNymnodeData} step={step} />;
}
if (step === 2) {
@@ -61,10 +72,10 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
if (step === 3) {
return (
<NymNodeSignature
nymNode={nymNodeData}
nymnode={nymNodeData}
pledge={amountData}
costParams={costParams}
onNext={handleUpdateSignature}
onNext={handleBond}
onClose={onClose}
onBack={() => setStep(2)}
step={step}
@@ -75,14 +86,22 @@ export const BondNymNodeModal = ({ onClose }: { onClose: () => void }) => {
return null;
};
export const BondNymNodeModalWithState = ({ open, onClose }: { open: boolean; onClose: () => void }) => {
export const BondNymNode = ({
open,
onClose,
onBond,
}: {
open: boolean;
onClose: () => void;
onBond: (data: TBondNymNodeArgs) => Promise<void>;
}) => {
if (!open) {
return null;
}
return (
<FormContextProvider>
<BondNymNodeModal onClose={onClose} />
<BondNymNodeModal onClose={onClose} onBond={onBond} />
</FormContextProvider>
);
};
@@ -1,10 +1,9 @@
import * as React from 'react';
import { useEffect } from 'react';
import { Typography } from '@mui/material';
import { TBondedNode } from 'src/context';
import { useGetFee } from 'src/hooks/useGetFee';
import { isGateway, isMixnode } from 'src/types';
import { TBondedGateway } from 'src/requests/gatewayDetails';
import { TBondedMixnode } from 'src/requests/mixnodeDetails';
import { ModalFee } from '../../Modals/ModalFee';
import { ModalListItem } from '../../Modals/ModalListItem';
import { SimpleModal } from '../../Modals/SimpleModal';
@@ -16,7 +15,7 @@ import {
} from '../../../requests';
interface Props {
node: TBondedMixnode | TBondedGateway;
node: TBondedNode;
onConfirm: () => Promise<void>;
onClose: () => void;
onError: (e: string) => void;
+37 -3
View File
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/naming-convention */
import React, { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { FeeDetails, TransactionExecuteResult } from '@nymproject/types';
import { isGateway, isMixnode, TUpdateBondArgs, isNymNode, TNymNodeSignatureArgs } from 'src/types';
import { isGateway, isMixnode, TUpdateBondArgs, isNymNode, TNymNodeSignatureArgs, TBondNymNodeArgs } from 'src/types';
import { Console } from 'src/utils/console';
import useGetNodeDetails from 'src/hooks/useGetNodeDetails';
import { TBondedNymNode } from 'src/requests/nymNodeDetails';
@@ -20,6 +20,7 @@ import {
migrateVestedMixnode as tauriMigrateVestedMixnode,
migrateLegacyMixnode as migrateLegacyMixnodeReq,
migrateLegacyGateway as migrateLegacyGatewayReq,
bondNymNode,
} from '../requests';
export type TBondedNode = TBondedMixnode | TBondedGateway | TBondedNymNode;
@@ -31,6 +32,7 @@ export type TBondingContext = {
isVestingAccount: boolean;
refresh: () => void;
unbond: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
bond: (args: TBondNymNodeArgs) => Promise<TransactionExecuteResult | undefined>;
updateBondAmount: (data: TUpdateBondArgs) => Promise<TransactionExecuteResult | undefined>;
redeemRewards: (fee?: FeeDetails) => Promise<TransactionExecuteResult | undefined>;
generateNymNodeMsgPayload: (data: TNymNodeSignatureArgs) => Promise<string | undefined>;
@@ -41,6 +43,9 @@ export type TBondingContext = {
export const BondingContext = createContext<TBondingContext>({
isLoading: true,
refresh: async () => undefined,
bond: async () => {
throw new Error('Not implemented');
},
unbond: async () => {
throw new Error('Not implemented');
},
@@ -70,7 +75,11 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
const { userBalance, clientDetails, network } = useContext(AppContext);
const { bondedNode, isLoading: isBondedNodeLoading } = useGetNodeDetails(clientDetails?.client_address, network);
const {
bondedNode,
isLoading: isBondedNodeLoading,
getNodeDetails,
} = useGetNodeDetails(clientDetails?.client_address, network);
useEffect(() => {
userBalance.fetchBalance();
@@ -91,6 +100,30 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
resetState();
};
const bond = async (data: TBondNymNodeArgs) => {
let tx;
setIsLoading(true);
try {
tx = await bondNymNode({
...data,
costParams: {
...data.costParams,
profit_margin_percent: toPercentFloatString(data.costParams.profit_margin_percent),
},
});
if (clientDetails?.client_address) {
await getNodeDetails(clientDetails?.client_address);
}
} catch (e) {
Console.warn(e);
setError(`an error occurred: ${e as string}`);
} finally {
setIsLoading(false);
}
return tx;
};
const unbond = async (fee?: FeeDetails) => {
let tx;
setIsLoading(true);
@@ -143,7 +176,7 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
try {
const message = await generateNymNodeMsgPayloadReq({
nymNode: data.nymNode,
nymnode: data.nymnode,
pledge: data.pledge,
costParams: {
...data.costParams,
@@ -187,6 +220,7 @@ export const BondingContextProvider: FCWithChildren = ({ children }): JSX.Elemen
isLoading: isLoading || isBondedNodeLoading,
error,
bondedNode,
bond,
unbond,
refresh,
redeemRewards,
+9
View File
@@ -133,6 +133,14 @@ export const MockBondingContextProvider = ({
return TxResultMock;
};
const bond = async (): Promise<TransactionExecuteResult> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
setBondedData(bondedMixnodeMock);
setIsLoading(false);
return TxResultMock;
};
const unbond = async (): Promise<TransactionExecuteResult> => {
setIsLoading(true);
await mockSleep(SLEEP_MS);
@@ -189,6 +197,7 @@ export const MockBondingContextProvider = ({
error,
bondMixnode,
bondGateway,
bond,
unbond,
refresh,
redeemRewards,
@@ -63,6 +63,7 @@ const useGetNodeDetails = (clientAddress?: string, network?: string) => {
bondedNode,
isLoading,
isError,
getNodeDetails,
};
};
+16 -3
View File
@@ -11,14 +11,14 @@ import { ConfirmationDetailProps, ConfirmationDetailsModal } from 'src/component
import { ErrorModal } from 'src/components/Modals/ErrorModal';
import { LoadingModal } from 'src/components/Modals/LoadingModal';
import { AppContext, urls } from 'src/context/main';
import { isGateway, isMixnode, isNymNode, TUpdateBondArgs } from 'src/types';
import { isGateway, isMixnode, isNymNode, TBondNymNodeArgs, TUpdateBondArgs } from 'src/types';
import { BondedGateway } from 'src/components/Bonding/BondedGateway';
import { RedeemRewardsModal } from 'src/components/Bonding/modals/RedeemRewardsModal';
import { VestingWarningModal } from 'src/components/VestingWarningModal';
import MigrateLegacyNode from 'src/components/Bonding/modals/MigrateLegacyNode';
import { BondedNymNode } from 'src/components/Bonding/BondedNymNode';
import { UpdateBondAmountNymNode } from 'src/components/Bonding/modals/UpdateBondAmountNymNode';
import { BondNymNodeModalWithState } from 'src/components/Bonding/modals/BondNymNodeModal';
import { BondNymNode } from 'src/components/Bonding/modals/BondNymNodeModal';
import { BondingContextProvider, useBondingContext } from '../../context';
export const Bonding = () => {
@@ -44,6 +44,7 @@ export const Bonding = () => {
redeemRewards,
updateBondAmount,
refresh,
bond,
migrateVestedMixnode,
migrateLegacyNode,
} = useBondingContext();
@@ -74,6 +75,18 @@ export const Bonding = () => {
setShowMigrateLegacyNodeModal(shouldShowMigrateLegacyNodeModal());
}, [bondedNode]);
const handleBondNymNode = async (data: TBondNymNodeArgs) => {
setShowModal(undefined);
const tx = await bond(data);
if (tx) {
setConfirmationDetails({
status: 'success',
title: 'Bonding successful',
txUrl: `${urls(network).blockExplorer}/transaction/${tx?.transaction_hash}`,
});
}
};
const handleMigrateVestedMixnode = async () => {
setShowMigrationModal(false);
const tx = await migrateVestedMixnode();
@@ -250,7 +263,7 @@ export const Bonding = () => {
/>
)}
<BondNymNodeModalWithState open={showModal === 'bond-nymnode'} onClose={handleCloseModal} />
<BondNymNode open={showModal === 'bond-nymnode'} onClose={handleCloseModal} onBond={handleBondNymNode} />
{showModal === 'update-bond-oversaturated' && uncappedSaturation && (
<BondOversaturatedModal
@@ -7,7 +7,6 @@ import { isMixnode, isNymNode } from 'src/types';
interface Props {
bondedNode: TBondedNode;
onConfirm: () => Promise<void>;
onError: (e: string) => void;
}
@@ -71,7 +70,7 @@ export const NodeUnbondPage = ({ bondedNode, onConfirm, onError }: Props) => {
</Stack>
</Grid>
</Grid>
{isConfirmed && !isNymNode(bondedNode) && (
{isConfirmed && (
<UnbondModal
node={bondedNode}
onConfirm={async () => {
+1 -1
View File
@@ -36,7 +36,7 @@ export type TBondNymNodeArgs = TNymNodeSignatureArgs & {
};
export type TNymNodeSignatureArgs = {
nymNode: NymNode;
nymnode: NymNode;
costParams: NodeCostParams;
pledge: DecCoin;
};