Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check account balance before publishing tx #87

Merged
merged 1 commit into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/api/schemas/strNumber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export const BigIntStringSchema = z.custom<string>((a) => {
});

export const BigIntMin = (min: bigint) =>
z.custom<string>((a) => BigInt(a) >= min, {
z.custom<string>((a) => a && BigInt(a) >= min, {
message: `Should be greater than or equal to ${String(min)}`,
});
20 changes: 17 additions & 3 deletions src/components/sendTx/ConfirmationModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type ConfirmationModalProps = ConfirmationData & {
isOpen: boolean;
estimatedGas: bigint | null;
isLedgerRejected?: boolean;
insufficientFunds: string;
};

const renderTemplateSpecificFields = (form: FormValues) => {
Expand Down Expand Up @@ -222,6 +223,7 @@ function ConfirmationModal({
signatures = undefined,
required = 1,
isMultiSig = false,
insufficientFunds,
isOpen,
onClose,
onSubmit,
Expand Down Expand Up @@ -320,7 +322,13 @@ function ConfirmationModal({
if (!isMultiSig) {
return (
<ButtonGroup isAttached>
<Button variant="whiteModal" onClick={submit} ml={2} mr="1px">
<Button
variant="whiteModal"
onClick={submit}
ml={2}
mr="1px"
isDisabled={insufficientFunds.length > 0}
>
{hasSingleSig ? 'Publish' : 'Sign & Publish'}
</Button>
<Menu>
Expand Down Expand Up @@ -349,10 +357,11 @@ function ConfirmationModal({
return (
<ButtonGroup isAttached>
<Button
colorScheme="blue"
variant="whiteModal"
onClick={mayPublish ? submit : exportSigned}
ml={2}
mr="1px"
isDisabled={mayPublish ? insufficientFunds.length > 0 : false}
>
{/* eslint-disable-next-line no-nested-ternary */}
{hasAllSignatures
Expand All @@ -365,7 +374,7 @@ function ConfirmationModal({
<MenuButton
as={IconButton}
icon={<IconChevronDown />}
colorScheme="blue"
variant="whiteModal"
minW={8}
/>
<MenuList>
Expand Down Expand Up @@ -486,6 +495,11 @@ function ConfirmationModal({
)}
</>
)}
{!!insufficientFunds && (
<Text mt={2} color="brand.red" fontSize="xs">
{insufficientFunds}
</Text>
)}
</ModalBody>
<ModalFooter
justifyContent="space-between"
Expand Down
45 changes: 45 additions & 0 deletions src/components/sendTx/SendTxModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import {
} from '../../utils/account';
import { fromBase64 } from '../../utils/base64';
import { generateAddress, getWords } from '../../utils/bech32';
import { DEFAULT_ACCOUNT_STATES } from '../../utils/constants';
import { fromHexString, toHexString } from '../../utils/hexString';
import { isForeignKey } from '../../utils/keys';
import { formatSmidge } from '../../utils/smh';
Expand All @@ -92,11 +93,13 @@ import ConfirmationModal, { ConfirmationData } from './ConfirmationModal';
import Drain from './Drain';
import MultiSigSpawn from './MultiSigSpawn';
import {
DrainPayload,
DrainSchema,
FormSchema,
FormValues,
MultiSigSpawnSchema,
SingleSigSpawnSchema,
SpendPayload,
SpendSchema,
VaultSpawnSchema,
} from './schemas';
Expand Down Expand Up @@ -194,6 +197,47 @@ function SendTxModal({ isOpen, onClose }: SendTxModalProps): JSX.Element {
isVaultAccount
);

const insufficientFunds = O.mapWithDefault(accountState, '', (acc) => {
if (!estimatedGas) {
return 'Waiting for gas estimation...';
}

const tx = getValues();
const fee =
estimatedGas *
BigInt(Number.isNaN(tx.gasPrice) || !tx.gasPrice ? 1 : tx.gasPrice);
if (BigInt(acc.projected.balance) < fee) {
return 'You have insufficient funds to pay for gas';
}
Comment on lines +209 to +211
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was your intention to display the "insufficient funds for gas" in case the balance is enough to cover the tx amount but the gas fee exceeds the available balance?
Cause now these lines work only in case the gas is the only thing to pay (spawn), but in the regular tx only the "insufficient funds to send this amount" is displayed.
If it's intended to be like that then it's ok, IMHO no need to overcomplicate these alerts, it's just that the PR description left me a bit hesitating about the intent

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got the same after some playing with numbers. Initially it worked.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was your intention to display the "insufficient funds for gas" in case the balance is enough to cover the tx amount but the gas fee exceeds the available balance?

Well, it will be shown for any transaction, when the balance is not enough to pay for a gas. Including the spend tx.
For example, try to send 0 SMH with 10000 Smidge / unit of gas price from the same test account (with 0.001 SMH), and you'll get this error message.

However, for spend tx there is additional condition, that checks that balance is equal or greater than amount + gas. And if it is not — then it will show "insufficient funds to send this amount". Basically it means that you have enough to pay for gas, but you should send less :)

if (SpendSchema.safeParse(tx.payload).success) {
// If it is a Spend tx — check if the balance is enough to send the amount
const txPayload = tx.payload as SpendPayload;
const res =
BigInt(acc.projected.balance) >= BigInt(txPayload.Amount) + fee;
return res ? '' : 'You have insufficient funds to send this amount';
}
if (DrainSchema.safeParse(tx.payload).success) {
// If it is a Drain tx — check that Vault has enough balance to be drained
const txPayload = tx.payload as DrainPayload;
return pipe(
genesisID,
O.flatMap((genId) => getAccountData(genId, txPayload.Vault)),
O.mapWithDefault('', (vault) => {
if (vault.state === DEFAULT_ACCOUNT_STATES) {
// No account data found — do not block the transaction
return '';
}

const res =
BigInt(vault.state.projected.balance) >= BigInt(txPayload.Amount);
return res ? '' : 'The vault has insufficient funds to be drained';
})
);
}
// In any other case
return '';
});

const methodOptions = useMemo(
() =>
[
Expand Down Expand Up @@ -1287,6 +1331,7 @@ function SendTxModal({ isOpen, onClose }: SendTxModalProps): JSX.Element {
isMultiSig={txData.isMultiSig ?? false}
required={txData.required}
isLedgerRejected={isLedgerRejected}
insufficientFunds={insufficientFunds}
/>
)}
</>
Expand Down