Tweak forms with trim and misc improvements

This commit is contained in:
abefernan 2023-09-07 17:07:51 +02:00
parent 34b5cea418
commit 71bdbe6094
12 changed files with 454 additions and 310 deletions

View File

@ -2,7 +2,7 @@ import { MsgWithdrawDelegatorRewardEncodeObject } from "@cosmjs/stargate";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -23,35 +23,38 @@ const MsgClaimRewardsForm = ({
const [validatorAddress, setValidatorAddress] = useState("");
const [validatorAddressError, setValidatorAddressError] = useState("");
const trimmedInputs = trimStringsObj({ validatorAddress });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { validatorAddress } = trimmedInputs;
const isMsgValid = (): boolean => {
setValidatorAddressError("");
const isMsgValid = (): boolean => {
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
return true;
};
return true;
};
const msgValue = MsgCodecs[MsgTypeUrls.WithdrawDelegatorReward].fromPartial({
delegatorAddress,
validatorAddress,
});
const msgValue = MsgCodecs[MsgTypeUrls.WithdrawDelegatorReward].fromPartial({
delegatorAddress,
validatorAddress,
});
const msg: MsgWithdrawDelegatorRewardEncodeObject = {
typeUrl: MsgTypeUrls.WithdrawDelegatorReward,
value: msgValue,
};
const msg: MsgWithdrawDelegatorRewardEncodeObject = {
typeUrl: MsgTypeUrls.WithdrawDelegatorReward,
value: msgValue,
};
setMsgGetter({ isMsgValid, msg });
} catch {}
}, [chain.addressPrefix, chain.chainId, delegatorAddress, setMsgGetter, validatorAddress]);
setMsgGetter({ isMsgValid, msg });
}, [chain.addressPrefix, chain.chainId, delegatorAddress, setMsgGetter, trimmedInputs]);
return (
<StackableContainer lessPadding lessMargin>
@ -64,7 +67,10 @@ const MsgClaimRewardsForm = ({
label="Validator Address"
name="validator-address"
value={validatorAddress}
onChange={({ target }) => setValidatorAddress(target.value)}
onChange={({ target }) => {
setValidatorAddress(target.value);
setValidatorAddressError("");
}}
error={validatorAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>

View File

@ -1,13 +1,13 @@
import { Decimal } from "@cosmjs/math";
import { EncodeObject } from "@cosmjs/proto-signing";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import {
datetimeLocalFromTimestamp,
timestampFromDatetimeLocal,
} from "../../../../lib/dateHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -36,60 +36,65 @@ const MsgCreateVestingAccountForm = ({
const [amountError, setAmountError] = useState("");
const [endTimeError, setEndTimeError] = useState("");
const trimmedInputs = trimStringsObj({ toAddress, amount, endTime });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { toAddress, amount, endTime } = trimmedInputs;
const isMsgValid = (): boolean => {
setToAddressError("");
setAmountError("");
setEndTimeError("");
const isMsgValid = (): boolean => {
const addressErrorMsg = checkAddress(toAddress, chain.addressPrefix);
if (addressErrorMsg) {
setToAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
return false;
}
const addressErrorMsg = checkAddress(toAddress, chain.addressPrefix);
if (addressErrorMsg) {
setToAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
const timeoutDate = new Date(timestampFromDatetimeLocal(endTime).toNumber());
if (timeoutDate <= new Date()) {
setEndTimeError("End time must be a date in the future");
return false;
}
const timeoutDate = new Date(timestampFromDatetimeLocal(endTime).toNumber());
if (timeoutDate <= new Date()) {
setEndTimeError("End time must be a date in the future");
return false;
}
return true;
};
return true;
};
const amountInAtomics = amount
? Decimal.fromUserInput(amount, Number(chain.displayDenomExponent)).atomics
: "0";
const microCoin = (() => {
try {
return macroCoinToMicroCoin({ denom: chain.displayDenom, amount }, chain.assets);
} catch {
return { denom: chain.displayDenom, amount: "0" };
}
})();
const msgValue = MsgCodecs[MsgTypeUrls.CreateVestingAccount].fromPartial({
fromAddress,
toAddress,
amount: [{ amount: amountInAtomics, denom: chain.denom }],
endTime: timestampFromDatetimeLocal(endTime, "s"),
delayed,
});
const msgValue = MsgCodecs[MsgTypeUrls.CreateVestingAccount].fromPartial({
fromAddress,
toAddress,
amount: [microCoin],
endTime: timestampFromDatetimeLocal(endTime, "s"),
delayed,
});
const msg: EncodeObject = { typeUrl: MsgTypeUrls.CreateVestingAccount, value: msgValue };
const msg: EncodeObject = { typeUrl: MsgTypeUrls.CreateVestingAccount, value: msgValue };
setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
chain.denom,
chain.displayDenomExponent,
chain.displayDenom,
delayed,
endTime,
fromAddress,
setMsgGetter,
toAddress,
trimmedInputs,
]);
return (
@ -103,7 +108,10 @@ const MsgCreateVestingAccountForm = ({
label="Recipient Address"
name="recipient-address"
value={toAddress}
onChange={({ target }) => setToAddress(target.value)}
onChange={({ target }) => {
setToAddress(target.value);
setToAddressError("");
}}
error={toAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -114,7 +122,10 @@ const MsgCreateVestingAccountForm = ({
label={`Amount (${chain.displayDenom})`}
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>
@ -124,7 +135,10 @@ const MsgCreateVestingAccountForm = ({
label="End time"
name="end-time"
value={endTime}
onChange={({ target }) => setEndTime(target.value)}
onChange={({ target }) => {
setEndTime(target.value);
setEndTimeError("");
}}
error={endTimeError}
/>
</div>

View File

@ -1,9 +1,9 @@
import { Decimal } from "@cosmjs/math";
import { MsgDelegateEncodeObject } from "@cosmjs/stargate";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -23,52 +23,57 @@ const MsgDelegateForm = ({ delegatorAddress, setMsgGetter, deleteMsg }: MsgDeleg
const [validatorAddressError, setValidatorAddressError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ validatorAddress, amount });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { validatorAddress, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setValidatorAddressError("");
setAmountError("");
const isMsgValid = (): boolean => {
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
return true;
};
return true;
};
const amountInAtomics = Decimal.fromUserInput(
amount || "0",
Number(chain.displayDenomExponent),
).atomics;
const microCoin = (() => {
try {
return macroCoinToMicroCoin({ denom: chain.displayDenom, amount }, chain.assets);
} catch {
return { denom: chain.displayDenom, amount: "0" };
}
})();
const msgValue = MsgCodecs[MsgTypeUrls.Delegate].fromPartial({
delegatorAddress,
validatorAddress,
amount: { amount: amountInAtomics, denom: chain.denom },
});
const msgValue = MsgCodecs[MsgTypeUrls.Delegate].fromPartial({
delegatorAddress,
validatorAddress,
amount: microCoin,
});
const msg: MsgDelegateEncodeObject = { typeUrl: MsgTypeUrls.Delegate, value: msgValue };
const msg: MsgDelegateEncodeObject = { typeUrl: MsgTypeUrls.Delegate, value: msgValue };
setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
chain.denom,
chain.displayDenomExponent,
chain.displayDenom,
delegatorAddress,
setMsgGetter,
validatorAddress,
trimmedInputs,
]);
return (
@ -82,7 +87,10 @@ const MsgDelegateForm = ({ delegatorAddress, setMsgGetter, deleteMsg }: MsgDeleg
label="Validator Address"
name="validator-address"
value={validatorAddress}
onChange={({ target }) => setValidatorAddress(target.value)}
onChange={({ target }) => {
setValidatorAddress(target.value);
setValidatorAddressError("");
}}
error={validatorAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -93,7 +101,10 @@ const MsgDelegateForm = ({ delegatorAddress, setMsgGetter, deleteMsg }: MsgDeleg
label={`Amount (${chain.displayDenom})`}
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -6,7 +6,7 @@ import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { ChainInfo } from "../../../../context/ChainsContext/types";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import Select from "../../../inputs/Select";
@ -50,12 +50,17 @@ const MsgExecuteContractForm = ({
const [customDenomError, setCustomDenomError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ contractAddress, customDenom, amount });
useEffect(() => {
setContractAddressError("");
setCustomDenomError("");
setAmountError("");
// eslint-disable-next-line no-shadow
const { contractAddress, customDenom, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setContractAddressError("");
setCustomDenomError("");
setAmountError("");
if (jsonError.current) {
return false;
}
@ -115,16 +120,14 @@ const MsgExecuteContractForm = ({
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
contractAddress,
customDenom,
fromAddress,
msgContent,
selectedDenom.value,
setMsgGetter,
trimmedInputs,
]);
return (
@ -138,7 +141,10 @@ const MsgExecuteContractForm = ({
label="Contract Address"
name="contract-address"
value={contractAddress}
onChange={({ target }) => setContractAddress(target.value)}
onChange={({ target }) => {
setContractAddress(target.value);
setContractAddressError("");
}}
error={contractAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -165,6 +171,7 @@ const MsgExecuteContractForm = ({
if (option.value !== customDenomOption.value) {
setCustomDenom("");
}
setCustomDenomError("");
}}
/>
</div>
@ -174,7 +181,10 @@ const MsgExecuteContractForm = ({
label="Custom denom"
name="custom-denom"
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
onChange={({ target }) => {
setCustomDenom(target.value);
setCustomDenomError("");
}}
placeholder={
selectedDenom.value === customDenomOption.value
? "Enter custom denom"
@ -191,7 +201,10 @@ const MsgExecuteContractForm = ({
label="Amount"
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -6,7 +6,7 @@ import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { ChainInfo } from "../../../../context/ChainsContext/types";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import Select from "../../../inputs/Select";
@ -56,15 +56,20 @@ const MsgInstantiateContract2Form = ({
const [customDenomError, setCustomDenomError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ codeId, label, adminAddress, salt, customDenom, amount });
useEffect(() => {
setCodeIdError("");
setLabelError("");
setAdminAddressError("");
setSaltError("");
setCustomDenomError("");
setAmountError("");
// eslint-disable-next-line no-shadow
const { codeId, label, adminAddress, salt, customDenom, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setCodeIdError("");
setLabelError("");
setAdminAddressError("");
setSaltError("");
setCustomDenomError("");
setAmountError("");
if (jsonError.current) {
return false;
}
@ -160,19 +165,14 @@ const MsgInstantiateContract2Form = ({
setMsgGetter({ isMsgValid, msg });
}, [
adminAddress,
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
codeId,
customDenom,
fromAddress,
label,
msgContent,
salt,
selectedDenom.value,
setMsgGetter,
trimmedInputs,
]);
return (
@ -186,7 +186,10 @@ const MsgInstantiateContract2Form = ({
label="Code ID"
name="code-id"
value={codeId}
onChange={({ target }) => setCodeId(target.value)}
onChange={({ target }) => {
setCodeId(target.value);
setCodeIdError("");
}}
error={codeIdError}
/>
</div>
@ -195,7 +198,10 @@ const MsgInstantiateContract2Form = ({
label="Label"
name="label"
value={label}
onChange={({ target }) => setLabel(target.value)}
onChange={({ target }) => {
setLabel(target.value);
setLabelError("");
}}
error={labelError}
/>
</div>
@ -204,7 +210,10 @@ const MsgInstantiateContract2Form = ({
label="Admin Address"
name="admin-address"
value={adminAddress}
onChange={({ target }) => setAdminAddress(target.value)}
onChange={({ target }) => {
setAdminAddress(target.value);
setAdminAddressError("");
}}
error={adminAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -215,7 +224,10 @@ const MsgInstantiateContract2Form = ({
name="salt"
placeholder="E.g. 1bac68"
value={salt}
onChange={({ target }) => setSalt(target.value)}
onChange={({ target }) => {
setSalt(target.value);
setSaltError("");
}}
error={saltError}
/>
</div>
@ -241,6 +253,7 @@ const MsgInstantiateContract2Form = ({
if (option.value !== customDenomOption.value) {
setCustomDenom("");
}
setCustomDenomError("");
}}
/>
</div>
@ -250,7 +263,10 @@ const MsgInstantiateContract2Form = ({
label="Custom denom"
name="custom-denom"
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
onChange={({ target }) => {
setCustomDenom(target.value);
setCustomDenomError("");
}}
placeholder={
selectedDenom.value === customDenomOption.value
? "Enter custom denom"
@ -267,7 +283,10 @@ const MsgInstantiateContract2Form = ({
label="Amount"
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -6,7 +6,7 @@ import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { ChainInfo } from "../../../../context/ChainsContext/types";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import Select from "../../../inputs/Select";
@ -54,14 +54,19 @@ const MsgInstantiateContractForm = ({
const [customDenomError, setCustomDenomError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ codeId, label, adminAddress, customDenom, amount });
useEffect(() => {
setCodeIdError("");
setLabelError("");
setAdminAddressError("");
setCustomDenomError("");
setAmountError("");
// eslint-disable-next-line no-shadow
const { codeId, label, adminAddress, customDenom, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setCodeIdError("");
setLabelError("");
setAdminAddressError("");
setCustomDenomError("");
setAmountError("");
if (jsonError.current) {
return false;
}
@ -136,18 +141,14 @@ const MsgInstantiateContractForm = ({
setMsgGetter({ isMsgValid, msg });
}, [
adminAddress,
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
codeId,
customDenom,
fromAddress,
label,
msgContent,
selectedDenom.value,
setMsgGetter,
trimmedInputs,
]);
return (
@ -161,7 +162,10 @@ const MsgInstantiateContractForm = ({
label="Code ID"
name="code-id"
value={codeId}
onChange={({ target }) => setCodeId(target.value)}
onChange={({ target }) => {
setCodeId(target.value);
setCodeIdError("");
}}
error={codeIdError}
/>
</div>
@ -170,7 +174,10 @@ const MsgInstantiateContractForm = ({
label="Label"
name="label"
value={label}
onChange={({ target }) => setLabel(target.value)}
onChange={({ target }) => {
setLabel(target.value);
setLabelError("");
}}
error={labelError}
/>
</div>
@ -179,7 +186,10 @@ const MsgInstantiateContractForm = ({
label="Admin Address"
name="admin-address"
value={adminAddress}
onChange={({ target }) => setAdminAddress(target.value)}
onChange={({ target }) => {
setAdminAddress(target.value);
setAdminAddressError("");
}}
error={adminAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -206,6 +216,7 @@ const MsgInstantiateContractForm = ({
if (option.value !== customDenomOption.value) {
setCustomDenom("");
}
setCustomDenomError("");
}}
/>
</div>
@ -215,7 +226,10 @@ const MsgInstantiateContractForm = ({
label="Custom denom"
name="custom-denom"
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
onChange={({ target }) => {
setCustomDenom(target.value);
setCustomDenomError("");
}}
placeholder={
selectedDenom.value === customDenomOption.value
? "Enter custom denom"
@ -232,7 +246,10 @@ const MsgInstantiateContractForm = ({
label="Amount"
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -4,7 +4,7 @@ import dynamic from "next/dynamic";
import { useEffect, useRef, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -32,11 +32,16 @@ const MsgMigrateContractForm = ({
const [contractAddressError, setContractAddressError] = useState("");
const [codeIdError, setCodeIdError] = useState("");
const trimmedInputs = trimStringsObj({ contractAddress, codeId });
useEffect(() => {
setCodeIdError("");
setContractAddressError("");
// eslint-disable-next-line no-shadow
const { contractAddress, codeId } = trimmedInputs;
const isMsgValid = (): boolean => {
setContractAddressError("");
setCodeIdError("");
if (jsonError.current) {
return false;
}
@ -74,15 +79,7 @@ const MsgMigrateContractForm = ({
const msg: MsgMigrateContractEncodeObject = { typeUrl: MsgTypeUrls.Migrate, value: msgValue };
setMsgGetter({ isMsgValid, msg });
}, [
chain.addressPrefix,
chain.chainId,
codeId,
contractAddress,
fromAddress,
msgContent,
setMsgGetter,
]);
}, [chain.addressPrefix, chain.chainId, fromAddress, msgContent, setMsgGetter, trimmedInputs]);
return (
<StackableContainer lessPadding lessMargin>
@ -95,7 +92,10 @@ const MsgMigrateContractForm = ({
label="Contract Address"
name="contract-address"
value={contractAddress}
onChange={({ target }) => setContractAddress(target.value)}
onChange={({ target }) => {
setContractAddress(target.value);
setContractAddressError("");
}}
error={contractAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -105,7 +105,10 @@ const MsgMigrateContractForm = ({
label="Code ID"
name="code-id"
value={codeId}
onChange={({ target }) => setCodeId(target.value)}
onChange={({ target }) => {
setCodeId(target.value);
setCodeIdError("");
}}
error={codeIdError}
/>
</div>

View File

@ -1,9 +1,9 @@
import { Decimal } from "@cosmjs/math";
import { EncodeObject } from "@cosmjs/proto-signing";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -29,63 +29,67 @@ const MsgRedelegateForm = ({
const [validatorDstAddressError, setValidatorDstAddressError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ validatorSrcAddress, validatorDstAddress, amount });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { validatorSrcAddress, validatorDstAddress, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setValidatorSrcAddressError("");
setValidatorDstAddressError("");
setAmountError("");
const isMsgValid = (): boolean => {
const srcAddressErrorMsg = checkAddress(validatorSrcAddress, chain.addressPrefix);
if (srcAddressErrorMsg) {
setValidatorSrcAddressError(
`Invalid address for network ${chain.chainId}: ${srcAddressErrorMsg}`,
);
return false;
}
const srcAddressErrorMsg = checkAddress(validatorSrcAddress, chain.addressPrefix);
if (srcAddressErrorMsg) {
setValidatorSrcAddressError(
`Invalid address for network ${chain.chainId}: ${srcAddressErrorMsg}`,
);
return false;
}
const dstAddressErrorMsg = checkAddress(validatorDstAddress, chain.addressPrefix);
if (dstAddressErrorMsg) {
setValidatorDstAddressError(
`Invalid address for network ${chain.chainId}: ${dstAddressErrorMsg}`,
);
return false;
}
const dstAddressErrorMsg = checkAddress(validatorDstAddress, chain.addressPrefix);
if (dstAddressErrorMsg) {
setValidatorDstAddressError(
`Invalid address for network ${chain.chainId}: ${dstAddressErrorMsg}`,
);
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
return true;
};
return true;
};
const amountInAtomics = Decimal.fromUserInput(
amount || "0",
Number(chain.displayDenomExponent),
).atomics;
const microCoin = (() => {
try {
return macroCoinToMicroCoin({ denom: chain.displayDenom, amount }, chain.assets);
} catch {
return { denom: chain.displayDenom, amount: "0" };
}
})();
const msgValue = MsgCodecs[MsgTypeUrls.BeginRedelegate].fromPartial({
delegatorAddress,
validatorSrcAddress,
validatorDstAddress,
amount: { amount: amountInAtomics, denom: chain.denom },
});
const msgValue = MsgCodecs[MsgTypeUrls.BeginRedelegate].fromPartial({
delegatorAddress,
validatorSrcAddress,
validatorDstAddress,
amount: microCoin,
});
const msg: EncodeObject = { typeUrl: MsgTypeUrls.BeginRedelegate, value: msgValue };
const msg: EncodeObject = { typeUrl: MsgTypeUrls.BeginRedelegate, value: msgValue };
setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
chain.denom,
chain.displayDenomExponent,
chain.displayDenom,
delegatorAddress,
setMsgGetter,
validatorDstAddress,
validatorSrcAddress,
trimmedInputs,
]);
return (
@ -99,7 +103,10 @@ const MsgRedelegateForm = ({
label="Source Validator Address"
name="src-validator-address"
value={validatorSrcAddress}
onChange={({ target }) => setValidatorSrcAddress(target.value)}
onChange={({ target }) => {
setValidatorSrcAddress(target.value);
setValidatorSrcAddressError("");
}}
error={validatorSrcAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -109,7 +116,10 @@ const MsgRedelegateForm = ({
label="Destination Validator Address"
name="dst-validator-address"
value={validatorDstAddress}
onChange={({ target }) => setValidatorDstAddress(target.value)}
onChange={({ target }) => {
setValidatorDstAddress(target.value);
setValidatorDstAddressError("");
}}
error={validatorDstAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -120,7 +130,10 @@ const MsgRedelegateForm = ({
label={`Amount (${chain.displayDenom})`}
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -3,7 +3,7 @@ import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { RegistryAsset } from "../../../../types/chainRegistry";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
@ -40,12 +40,17 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
const [customDenomError, setCustomDenomError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ toAddress, customDenom, amount });
useEffect(() => {
setToAddressError("");
setCustomDenomError("");
setAmountError("");
// eslint-disable-next-line no-shadow
const { toAddress, customDenom, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setToAddressError("");
setCustomDenomError("");
setAmountError("");
const addressErrorMsg = checkAddress(toAddress, chain.addressPrefix);
if (addressErrorMsg) {
setToAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
@ -91,15 +96,13 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
customDenom,
fromAddress,
selectedDenom.value,
setMsgGetter,
toAddress,
trimmedInputs,
]);
return (
@ -113,7 +116,10 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
label="Recipient Address"
name="recipient-address"
value={toAddress}
onChange={({ target }) => setToAddress(target.value)}
onChange={({ target }) => {
setToAddress(target.value);
setToAddressError("");
}}
error={toAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -130,6 +136,7 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
if (option.value !== customDenomOption.value) {
setCustomDenom("");
}
setCustomDenomError("");
}}
/>
</div>
@ -139,7 +146,10 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
label="Custom denom"
name="custom-denom"
value={customDenom}
onChange={({ target }) => setCustomDenom(target.value)}
onChange={({ target }) => {
setCustomDenom(target.value);
setCustomDenomError("");
}}
placeholder={
selectedDenom.value === customDenomOption.value
? "Enter custom denom"
@ -156,7 +166,10 @@ const MsgSendForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgSendFormProps)
label="Amount"
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>

View File

@ -2,7 +2,7 @@ import { EncodeObject } from "@cosmjs/proto-signing";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -23,31 +23,32 @@ const MsgSetWithdrawAddressForm = ({
const [withdrawAddress, setWithdrawAddress] = useState("");
const [withdrawAddressError, setWithdrawAddressError] = useState("");
const trimmedInputs = trimStringsObj({ withdrawAddress });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { withdrawAddress } = trimmedInputs;
const isMsgValid = (): boolean => {
setWithdrawAddressError("");
const isMsgValid = (): boolean => {
const addressErrorMsg = checkAddress(withdrawAddress, chain.addressPrefix);
if (addressErrorMsg) {
setWithdrawAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(withdrawAddress, chain.addressPrefix);
if (addressErrorMsg) {
setWithdrawAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
return false;
}
return true;
};
return true;
};
const msgValue = MsgCodecs[MsgTypeUrls.SetWithdrawAddress].fromPartial({
delegatorAddress,
withdrawAddress,
});
const msg: EncodeObject = { typeUrl: MsgTypeUrls.SetWithdrawAddress, value: msgValue };
const msgValue = MsgCodecs[MsgTypeUrls.SetWithdrawAddress].fromPartial({
delegatorAddress,
withdrawAddress,
});
const msg: EncodeObject = { typeUrl: MsgTypeUrls.SetWithdrawAddress, value: msgValue };
setMsgGetter({ isMsgValid, msg });
} catch {}
}, [chain.addressPrefix, chain.chainId, delegatorAddress, setMsgGetter, withdrawAddress]);
setMsgGetter({ isMsgValid, msg });
}, [chain.addressPrefix, chain.chainId, delegatorAddress, setMsgGetter, trimmedInputs]);
return (
<StackableContainer lessPadding lessMargin>
@ -60,7 +61,10 @@ const MsgSetWithdrawAddressForm = ({
label="Withdraw Address"
name="withdraw-address"
value={withdrawAddress}
onChange={({ target }) => setWithdrawAddress(target.value)}
onChange={({ target }) => {
setWithdrawAddress(target.value);
setWithdrawAddressError("");
}}
error={withdrawAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>

View File

@ -6,7 +6,7 @@ import {
datetimeLocalFromTimestamp,
timestampFromDatetimeLocal,
} from "../../../../lib/dateHelpers";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -32,39 +32,48 @@ interface MsgTransferFormProps {
const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFormProps) => {
const { chain } = useChains();
const [sourcePort, setSourcePort] = useState("transfer");
const [sourceChannel, setSourceChannel] = useState("");
const [toAddress, setToAddress] = useState("");
const [denom, setDenom] = useState("");
const [amount, setAmount] = useState("0");
const [toAddress, setToAddress] = useState("");
const [sourcePort, setSourcePort] = useState("transfer");
const [sourceChannel, setSourceChannel] = useState("");
const [timeout, setTimeout] = useState(
datetimeLocalFromTimestamp(Date.now() + humanTimestampOptions[0].value),
);
const [memo, setMemo] = useState("");
const [sourcePortError, setSourcePortError] = useState("");
const [sourceChannelError, setSourceChannelError] = useState("");
const [toAddressError, setToAddressError] = useState("");
const [denomError, setDenomError] = useState("");
const [amountError, setAmountError] = useState("");
const [toAddressError, setToAddressError] = useState("");
const [sourcePortError, setSourcePortError] = useState("");
const [sourceChannelError, setSourceChannelError] = useState("");
const [timeoutError, setTimeoutError] = useState("");
const trimmedInputs = trimStringsObj({
toAddress,
denom,
amount,
sourcePort,
sourceChannel,
timeout,
memo,
});
useEffect(() => {
setSourcePortError("");
setSourceChannelError("");
setDenomError("");
setAmountError("");
setToAddressError("");
setTimeoutError("");
// eslint-disable-next-line no-shadow
const { toAddress, denom, amount, sourcePort, sourceChannel, timeout, memo } = trimmedInputs;
const isMsgValid = (): boolean => {
if (!sourcePort) {
setSourcePortError("Source port is required");
return false;
}
setToAddressError("");
setDenomError("");
setAmountError("");
setSourcePortError("");
setSourceChannelError("");
setTimeoutError("");
if (!sourceChannel) {
setSourceChannelError("Source channel is required");
const addressErrorMsg = checkAddress(toAddress, null); // Allow address from any chain
if (addressErrorMsg) {
setToAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
return false;
}
@ -78,9 +87,13 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
return false;
}
const addressErrorMsg = checkAddress(toAddress, null); // Allow address from any chain
if (addressErrorMsg) {
setToAddressError(`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`);
if (!sourcePort) {
setSourcePortError("Source port is required");
return false;
}
if (!sourceChannel) {
setSourceChannelError("Source channel is required");
return false;
}
@ -106,18 +119,7 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
const msg: MsgTransferEncodeObject = { typeUrl: MsgTypeUrls.Transfer, value: msgValue };
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.chainId,
denom,
fromAddress,
memo,
setMsgGetter,
sourceChannel,
sourcePort,
timeout,
toAddress,
]);
}, [chain.chainId, fromAddress, setMsgGetter, trimmedInputs]);
return (
<StackableContainer lessPadding lessMargin>
@ -130,7 +132,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Recipient Address"
name="recipient-address"
value={toAddress}
onChange={({ target }) => setToAddress(target.value)}
onChange={({ target }) => {
setToAddress(target.value);
setToAddressError("");
}}
error={toAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -140,7 +145,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Denom"
name="denom"
value={denom}
onChange={({ target }) => setDenom(target.value)}
onChange={({ target }) => {
setDenom(target.value);
setDenomError("");
}}
error={denomError}
/>
</div>
@ -150,7 +158,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Amount"
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>
@ -159,7 +170,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Source Port"
name="source-port"
value={sourcePort}
onChange={({ target }) => setSourcePort(target.value)}
onChange={({ target }) => {
setSourcePort(target.value);
setSourcePortError("");
}}
error={sourcePortError}
/>
</div>
@ -168,7 +182,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Source Channel"
name="source-channel"
value={sourceChannel}
onChange={({ target }) => setSourceChannel(target.value)}
onChange={({ target }) => {
setSourceChannel(target.value);
setSourceChannelError("");
}}
error={sourceChannelError}
/>
</div>
@ -179,7 +196,10 @@ const MsgTransferForm = ({ fromAddress, setMsgGetter, deleteMsg }: MsgTransferFo
label="Timeout"
name="timeout"
value={timeout}
onChange={({ target }) => setTimeout(target.value)}
onChange={({ target }) => {
setTimeout(target.value);
setTimeoutError("");
}}
error={timeoutError}
/>
<datalist id="timestamp-options">

View File

@ -1,9 +1,9 @@
import { Decimal } from "@cosmjs/math";
import { MsgUndelegateEncodeObject } from "@cosmjs/stargate";
import { useEffect, useState } from "react";
import { MsgGetter } from "..";
import { useChains } from "../../../../context/ChainsContext";
import { checkAddress, exampleAddress } from "../../../../lib/displayHelpers";
import { macroCoinToMicroCoin } from "../../../../lib/coinHelpers";
import { checkAddress, exampleAddress, trimStringsObj } from "../../../../lib/displayHelpers";
import { MsgCodecs, MsgTypeUrls } from "../../../../types/txMsg";
import Input from "../../../inputs/Input";
import StackableContainer from "../../../layout/StackableContainer";
@ -27,52 +27,57 @@ const MsgUndelegateForm = ({
const [validatorAddressError, setValidatorAddressError] = useState("");
const [amountError, setAmountError] = useState("");
const trimmedInputs = trimStringsObj({ validatorAddress, amount });
useEffect(() => {
try {
// eslint-disable-next-line no-shadow
const { validatorAddress, amount } = trimmedInputs;
const isMsgValid = (): boolean => {
setValidatorAddressError("");
setAmountError("");
const isMsgValid = (): boolean => {
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
const addressErrorMsg = checkAddress(validatorAddress, chain.addressPrefix);
if (addressErrorMsg) {
setValidatorAddressError(
`Invalid address for network ${chain.chainId}: ${addressErrorMsg}`,
);
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
if (!amount || Number(amount) <= 0) {
setAmountError("Amount must be greater than 0");
return false;
}
return true;
};
return true;
};
const amountInAtomics = Decimal.fromUserInput(
amount || "0",
Number(chain.displayDenomExponent),
).atomics;
const microCoin = (() => {
try {
return macroCoinToMicroCoin({ denom: chain.displayDenom, amount }, chain.assets);
} catch {
return { denom: chain.displayDenom, amount: "0" };
}
})();
const msgValue = MsgCodecs[MsgTypeUrls.Undelegate].fromPartial({
delegatorAddress,
validatorAddress,
amount: { amount: amountInAtomics, denom: chain.denom },
});
const msgValue = MsgCodecs[MsgTypeUrls.Undelegate].fromPartial({
delegatorAddress,
validatorAddress,
amount: microCoin,
});
const msg: MsgUndelegateEncodeObject = { typeUrl: MsgTypeUrls.Undelegate, value: msgValue };
const msg: MsgUndelegateEncodeObject = { typeUrl: MsgTypeUrls.Undelegate, value: msgValue };
setMsgGetter({ isMsgValid, msg });
} catch {}
setMsgGetter({ isMsgValid, msg });
}, [
amount,
chain.addressPrefix,
chain.assets,
chain.chainId,
chain.denom,
chain.displayDenomExponent,
chain.displayDenom,
delegatorAddress,
setMsgGetter,
validatorAddress,
trimmedInputs,
]);
return (
@ -86,7 +91,10 @@ const MsgUndelegateForm = ({
label="Validator Address"
name="validator-address"
value={validatorAddress}
onChange={({ target }) => setValidatorAddress(target.value)}
onChange={({ target }) => {
setValidatorAddress(target.value);
setValidatorAddressError("");
}}
error={validatorAddressError}
placeholder={`E.g. ${exampleAddress(0, chain.addressPrefix)}`}
/>
@ -97,7 +105,10 @@ const MsgUndelegateForm = ({
label={`Amount (${chain.displayDenom})`}
name="amount"
value={amount}
onChange={({ target }) => setAmount(target.value)}
onChange={({ target }) => {
setAmount(target.value);
setAmountError("");
}}
error={amountError}
/>
</div>