diff --git a/bun.lockb b/bun.lockb index a8dde1c5..6ab9498f 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/admins/components/stakingParams.tsx b/components/admins/components/stakingParams.tsx index e4cd7dbb..5c4b845a 100644 --- a/components/admins/components/stakingParams.tsx +++ b/components/admins/components/stakingParams.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { ParamsSDKType } from '@chalabi/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; +import { ParamsSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; import { UpdateStakingParamsModal } from '../modals/updateStakingParamsModal'; interface StakingParamsProps { diff --git a/components/admins/components/validatorList.tsx b/components/admins/components/validatorList.tsx index ab18cbbb..a7f0193b 100644 --- a/components/admins/components/validatorList.tsx +++ b/components/admins/components/validatorList.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect, useMemo } from 'react'; import { ValidatorDetailsModal } from '../modals/validatorModal'; import { WarningModal } from '../modals/warningModal'; -import { ValidatorSDKType } from '@chalabi/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; +import { ValidatorSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; import ProfileAvatar from '@/utils/identicon'; import Image from 'next/image'; import { TruncatedAddressWithCopy } from '@/components/react/addressCopy'; @@ -22,7 +22,6 @@ export default function ValidatorList({ admin, activeValidators, pendingValidators, - isLoading, }: ValidatorListProps) { const [active, setActive] = useState(true); const [searchTerm, setSearchTerm] = useState(''); @@ -122,9 +121,13 @@ export default function ValidatorList({ {active ? 'No active validators found' : 'No pending validators'} ) : ( - +
- + @@ -145,6 +148,8 @@ export default function ValidatorList({ key={validator.operator_address} className="bg-[#FFFFFFCC] dark:bg-[#FFFFFF0F] hover:bg-[#FFFFFF66] dark:hover:bg-[#FFFFFF1A] text-black dark:text-white rounded-lg cursor-pointer" onClick={() => handleRowClick(validator)} + role="row" + aria-label={`Validator ${validator.description.moniker}`} > {truncateString(denom.symbol, 20)} diff --git a/components/factory/forms/BurnForm.tsx b/components/factory/forms/BurnForm.tsx index e5c48db7..1bd1a4c6 100644 --- a/components/factory/forms/BurnForm.tsx +++ b/components/factory/forms/BurnForm.tsx @@ -1,12 +1,12 @@ -import React, { useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { chainName } from '@/config'; import { useTokenFactoryBalance, useFeeEstimation, useTx } from '@/hooks'; -import { cosmos, osmosis, liftedinit } from '@chalabi/manifestjs'; +import { cosmos, osmosis, liftedinit } from '@liftedinit/manifestjs'; import { MdContacts } from 'react-icons/md'; import { shiftDigits } from '@/utils'; -import { Any } from '@chalabi/manifestjs/dist/codegen/google/protobuf/any'; -import { MsgBurnHeldBalance } from '@chalabi/manifestjs/dist/codegen/liftedinit/manifest/v1/tx'; +import { Any } from '@liftedinit/manifestjs/dist/codegen/google/protobuf/any'; +import { MsgBurnHeldBalance } from '@liftedinit/manifestjs/dist/codegen/liftedinit/manifest/v1/tx'; import { MultiBurnModal } from '../modals/multiMfxBurnModal'; import { useToast } from '@/contexts'; import { Formik, Form } from 'formik'; @@ -14,7 +14,7 @@ import Yup from '@/utils/yupExtensions'; import { NumberInput, TextInput } from '@/components/react/inputs'; import { ExtendedMetadataSDKType, truncateString } from '@/utils'; import { TailwindModal } from '@/components/react/modal'; - +//TODO: burn target validation interface BurnPair { address: string; amount: string; @@ -59,8 +59,9 @@ export default function BurnForm({ const isMFX = denom.base.includes('mfx'); const { balance: recipientBalance } = useTokenFactoryBalance(recipient ?? '', denom.base); - const balanceNumber = parseFloat( - shiftDigits(isMFX ? recipientBalance?.amount || '0' : balance, -exponent) + const balanceNumber = useMemo( + () => parseFloat(shiftDigits(isMFX ? recipientBalance?.amount || '0' : balance, -exponent)), + [recipientBalance?.amount, balance, exponent, isMFX] ); const BurnSchema = Yup.object().shape({ @@ -75,7 +76,7 @@ export default function BurnForm({ }); const handleBurn = async () => { - if (!amount || isNaN(Number(amount))) { + if (!amount || Number.isNaN(Number(amount))) { return; } setIsSigning(true); diff --git a/components/factory/forms/ConfirmationForm.tsx b/components/factory/forms/ConfirmationForm.tsx index f3c83a2a..3c3352de 100644 --- a/components/factory/forms/ConfirmationForm.tsx +++ b/components/factory/forms/ConfirmationForm.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { TokenFormData } from '@/helpers/formReducer'; import { useFeeEstimation } from '@/hooks/useFeeEstimation'; import { useTx } from '@/hooks/useTx'; -import { osmosis } from '@chalabi/manifestjs'; +import { osmosis } from '@liftedinit/manifestjs'; import { chainName } from '@/config'; export default function ConfirmationForm({ @@ -56,7 +56,7 @@ export default function ConfirmationForm({ aliases: [], }, { - denom: formData.subdenom.slice(1), + denom: formData.subdenom.slice(1).toUpperCase(), exponent: 6, aliases: [], }, @@ -64,7 +64,7 @@ export default function ConfirmationForm({ base: fullDenom, display: formData.display, name: formData.name, - symbol: formData.symbol || formData.display, // Use display as fallback if symbol is not set + symbol: formData.symbol || formData.display, uri: formData.uri, uriHash: formData.uriHash, }, diff --git a/components/factory/forms/CreateDenom.tsx b/components/factory/forms/CreateDenom.tsx index 4668b757..baa12bcd 100644 --- a/components/factory/forms/CreateDenom.tsx +++ b/components/factory/forms/CreateDenom.tsx @@ -15,6 +15,7 @@ export default function CreateDenom({ formData: TokenFormData; dispatch: React.Dispatch; }>) { + const [isSimulating, setIsSimulating] = React.useState(false); const { simulateDenomCreation } = useSimulateDenomCreation(); const DenomSchema = Yup.object().shape({ @@ -24,10 +25,14 @@ export default function CreateDenom({ .min(4, 'Subdenom must be at least 4 characters') .max(44, 'Subdenom must not exceed 44 characters') .noProfanity('Profanity is not allowed') - .simulateDenomCreation( - () => simulateDenomCreation(formData.subdenom), - `The denom ${formData.subdenom} already exists` - ), + .simulateDenomCreation(async () => { + setIsSimulating(true); + try { + return await simulateDenomCreation(formData.subdenom); + } finally { + setIsSimulating(false); + } + }, `The denom ${formData.subdenom} already exists`), }); return ( @@ -63,6 +68,14 @@ export default function CreateDenom({ }); setFieldValue('subdenom', e.target.value); }} + rightElement={ + isSimulating && ( +
+ + checking availability... +
+ ) + } /> {/* Token Exponents Section */} diff --git a/components/factory/forms/MintForm.tsx b/components/factory/forms/MintForm.tsx index 09efacdd..9256b4cb 100644 --- a/components/factory/forms/MintForm.tsx +++ b/components/factory/forms/MintForm.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { chainName } from '@/config'; import { useFeeEstimation, useTx } from '@/hooks'; -import { osmosis } from '@chalabi/manifestjs'; +import { osmosis } from '@liftedinit/manifestjs'; import { shiftDigits } from '@/utils'; import { MdContacts } from 'react-icons/md'; @@ -53,7 +53,7 @@ export default function MintForm({ }); const handleMint = async () => { - if (!amount || isNaN(Number(amount))) { + if (!amount || Number.isNaN(Number(amount))) { return; } setIsSigning(true); @@ -91,7 +91,7 @@ export default function MintForm({
{isMFX && !isAdmin ? (
- You must be apart of the admin group to mint MFX. + You must be a member of the admin group to mint MFX.
) : ( <> diff --git a/components/factory/forms/TokenDetailsForm.tsx b/components/factory/forms/TokenDetailsForm.tsx index fbf1860c..946f6661 100644 --- a/components/factory/forms/TokenDetailsForm.tsx +++ b/components/factory/forms/TokenDetailsForm.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Formik, Form } from 'formik'; import Yup from '@/utils/yupExtensions'; import { TokenAction, TokenFormData } from '@/helpers/formReducer'; -import { DenomUnit } from '@chalabi/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank'; +import { DenomUnit } from '@liftedinit/manifestjs/dist/codegen/cosmos/bank/v1beta1/bank'; import { TextInput, TextArea } from '@/components/react/inputs'; export default function TokenDetails({ @@ -24,17 +24,20 @@ export default function TokenDetails({ .noProfanity() .test('display-contains-subdenom', 'Display must contain subdenom', function (value) { const subdenom = this.parent.subdenom; - return value.toLowerCase().includes(subdenom.slice(1).toLowerCase()); + return !subdenom || value.toLowerCase().includes(subdenom.slice(1).toLowerCase()); }), description: Yup.string() .required('Description is required') .min(10, 'Description must be at least 10 characters long') .noProfanity(), name: Yup.string().required('Name is required').noProfanity(), - uri: Yup.string().url('Must be a valid URL'), + uri: Yup.string() + .url('Must be a valid URL') + .matches(/^https:\/\//i, 'URL must use HTTPS protocol') + .matches(/\.(jpg|jpeg|png|gif)$/i, 'URL must point to an image file'), }); - const updateField = (field: keyof TokenFormData, value: any) => { + const updateField = (field: keyof TokenFormData, value: TokenFormData[keyof TokenFormData]) => { dispatch({ type: 'UPDATE_FIELD', field, value }); }; @@ -47,6 +50,7 @@ export default function TokenDetails({ { denom: formData.subdenom.slice(1), exponent: 6, aliases: [] }, ]; updateField('denomUnits', denomUnits); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [formData.subdenom, address]); return ( @@ -73,6 +77,7 @@ export default function TokenDetails({ name="subdenom" disabled={true} value={formData.subdenom} + aria-label="Token subdenom" />
@@ -95,6 +101,7 @@ export default function TokenDetails({ updateField('symbol', value.toUpperCase()); handleChange(e); }} + aria-label="Token ticker" />
Moniker
diff --git a/components/admins/modals/__tests__/updateStakingParamsModal.test.tsx b/components/admins/modals/__tests__/updateStakingParamsModal.test.tsx index 207da39c..9e90916f 100644 --- a/components/admins/modals/__tests__/updateStakingParamsModal.test.tsx +++ b/components/admins/modals/__tests__/updateStakingParamsModal.test.tsx @@ -4,7 +4,7 @@ import { screen, fireEvent, cleanup } from '@testing-library/react'; import { UpdateStakingParamsModal } from '@/components/admins/modals/updateStakingParamsModal'; import matchers from '@testing-library/jest-dom/matchers'; import { renderWithChainProvider } from '@/tests/render'; -import { DurationSDKType } from '@chalabi/manifestjs/src/codegen/google/protobuf/duration'; +import { DurationSDKType } from '@liftedinit/manifestjs/src/codegen/google/protobuf/duration'; expect.extend(matchers); diff --git a/components/admins/modals/updateStakingParamsModal.tsx b/components/admins/modals/updateStakingParamsModal.tsx index e13b145f..b0b5c3d8 100644 --- a/components/admins/modals/updateStakingParamsModal.tsx +++ b/components/admins/modals/updateStakingParamsModal.tsx @@ -1,9 +1,9 @@ import { chainName } from '@/config'; import { useFeeEstimation, useTx } from '@/hooks'; -import { strangelove_ventures, cosmos, manifest } from '@chalabi/manifestjs'; -import { ParamsSDKType } from '@chalabi/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; -import { MsgUpdateStakingParams } from '@chalabi/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; -import { Any } from '@chalabi/manifestjs/dist/codegen/google/protobuf/any'; +import { strangelove_ventures, cosmos } from '@liftedinit/manifestjs'; +import { ParamsSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/staking/v1beta1/staking'; +import { MsgUpdateStakingParams } from '@liftedinit/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; +import { Any } from '@liftedinit/manifestjs/dist/codegen/google/protobuf/any'; import React, { useState, useEffect } from 'react'; interface UpdateStakingParamsModalProps { diff --git a/components/admins/modals/validatorModal.tsx b/components/admins/modals/validatorModal.tsx index b3d062d9..2f55fc57 100644 --- a/components/admins/modals/validatorModal.tsx +++ b/components/admins/modals/validatorModal.tsx @@ -6,11 +6,11 @@ import { BsThreeDots } from 'react-icons/bs'; import { DescriptionModal } from './descriptionModal'; import { chainName } from '@/config'; import { useTx, useFeeEstimation } from '@/hooks'; -import { strangelove_ventures } from '@chalabi/manifestjs'; +import { strangelove_ventures } from '@liftedinit/manifestjs'; import { useChain } from '@cosmos-kit/react'; -import { cosmos } from '@chalabi/manifestjs'; -import { Any } from '@chalabi/manifestjs/dist/codegen/google/protobuf/any'; -import { MsgSetPower } from '@chalabi/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; +import { cosmos } from '@liftedinit/manifestjs'; +import { Any } from '@liftedinit/manifestjs/dist/codegen/google/protobuf/any'; +import { MsgSetPower } from '@liftedinit/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; import { Formik, Form, Field, ErrorMessage, FieldProps } from 'formik'; import * as Yup from 'yup'; import { calculateIsUnsafe } from '@/utils/maths'; diff --git a/components/admins/modals/warningModal.tsx b/components/admins/modals/warningModal.tsx index 4aeff8f7..53731a9c 100644 --- a/components/admins/modals/warningModal.tsx +++ b/components/admins/modals/warningModal.tsx @@ -1,8 +1,8 @@ import { chainName } from '@/config'; import { useFeeEstimation, useTx } from '@/hooks'; -import { cosmos, strangelove_ventures } from '@chalabi/manifestjs'; -import { Any } from '@chalabi/manifestjs/dist/codegen/google/protobuf/any'; -import { MsgRemoveValidator } from '@chalabi/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; +import { cosmos, strangelove_ventures } from '@liftedinit/manifestjs'; +import { Any } from '@liftedinit/manifestjs/dist/codegen/google/protobuf/any'; +import { MsgRemoveValidator } from '@liftedinit/manifestjs/dist/codegen/strangelove_ventures/poa/v1/tx'; import { useChain } from '@cosmos-kit/react'; import React, { useState } from 'react'; import { PiWarning } from 'react-icons/pi'; diff --git a/components/bank/components/sendBox.tsx b/components/bank/components/sendBox.tsx index 84509e1c..a29f9898 100644 --- a/components/bank/components/sendBox.tsx +++ b/components/bank/components/sendBox.tsx @@ -3,7 +3,7 @@ import SendForm from '../forms/sendForm'; import IbcSendForm from '../forms/ibcSendForm'; import { PiCaretDownBold } from 'react-icons/pi'; import Image from 'next/image'; -import { CoinSDKType } from '@chalabi/manifestjs/dist/codegen/cosmos/base/v1beta1/coin'; +import { CoinSDKType } from '@liftedinit/manifestjs/dist/codegen/cosmos/base/v1beta1/coin'; import { CombinedBalanceInfo } from '@/utils/types'; export interface IbcChain { diff --git a/components/bank/forms/ibcSendForm.tsx b/components/bank/forms/ibcSendForm.tsx index 376bc57d..0bbf9fa8 100644 --- a/components/bank/forms/ibcSendForm.tsx +++ b/components/bank/forms/ibcSendForm.tsx @@ -1,7 +1,7 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { chainName } from '@/config'; import { useFeeEstimation, useTx } from '@/hooks'; -import { ibc } from '@chalabi/manifestjs'; +import { ibc } from '@liftedinit/manifestjs'; import { getIbcInfo } from '@/utils'; import { PiCaretDownBold } from 'react-icons/pi'; import { MdContacts } from 'react-icons/md'; @@ -16,7 +16,9 @@ import { shiftDigits, truncateString } from '@/utils'; import { SearchIcon } from '@/components/icons'; import { MFX_TOKEN_DATA } from '@/utils/constants'; // Import MFX_TOKEN_DATA import { TailwindModal } from '@/components/react/modal'; +import { formatTokenDisplayName } from '@/utils'; +//TODO: use formatTokenDisplayName instead of repeating format export default function IbcSendForm({ address, destinationChain, @@ -51,10 +53,14 @@ export default function IbcSendForm({ const [isContactsOpen, setIsContactsOpen] = useState(false); // Adjusted filter logic to handle undefined metadata - const filteredBalances = balances?.filter(token => { - const displayName = token.metadata?.display ?? token.denom; - return displayName.toLowerCase().includes(searchTerm.toLowerCase()); - }); + const filteredBalances = useMemo( + () => + balances?.filter(token => { + const displayName = token.metadata?.display ?? token.denom; + return displayName.toLowerCase().includes(searchTerm.toLowerCase()); + }), + [balances, searchTerm] + ); // Set initialSelectedToken to 'mfx' if available const initialSelectedToken = @@ -78,20 +84,14 @@ export default function IbcSendForm({ const balance = parseFloat(selectedToken.amount) / Math.pow(10, exponent); return value <= balance; }) - .test('leave-for-fees', '', function (value) { + .test('leave-for-fees', 'Insufficient balance for fees', function (value) { const { selectedToken } = this.parent; if (!selectedToken || !value || selectedToken.denom !== 'umfx') return true; const exponent = selectedToken.metadata?.denom_units[1]?.exponent ?? 6; const balance = parseFloat(selectedToken.amount) / Math.pow(10, exponent); - if (value > balance - 0.09) { - setFeeWarning('Remember to leave tokens for fees!'); - } else { - setFeeWarning(''); - } - - return true; + return value <= balance - 0.09; }), selectedToken: Yup.object().required('Please select a token'), memo: Yup.string().max(255, 'Memo must be less than 255 characters'), @@ -181,6 +181,10 @@ export default function IbcSendForm({
- - {Number(shiftDigits(denom.totalSupply, -denom.denom_units[1]?.exponent)).toLocaleString( - undefined, - { - maximumFractionDigits: denom.denom_units[1]?.exponent ?? 6, - } - )} - - - {truncateString(denom.display, 10).toUpperCase()} + {formatAmount(totalSupply)} + + {truncateString(denom?.display ?? '', 10).toUpperCase()}
- - {Number(shiftDigits(denom.balance, -denom.denom_units[1]?.exponent)).toLocaleString( - undefined, - { - maximumFractionDigits: denom.denom_units[1]?.exponent ?? 6, - } - )} - - - {truncateString(denom.display, 10).toUpperCase()} + {formatAmount(balance)} + + {truncateString(denom?.display ?? '', 10).toUpperCase()}