Skip to content

Commit

Permalink
fix(minifront): #1598: display non-native-fee-warning per account (#1614
Browse files Browse the repository at this point in the history
)

* fix(minifront): #1593: display non-native-fee-warning per account

* fix(minifront): #1598: fix linter

* chore: changeset

* fix(#1598): check for alt token balances before rendering non-native-fee-warning

* chore: format

* fix(minifront): #1598: comments

* fix(minifront): #1598: fix display of the fee warning

* fix(#1598): refactor
  • Loading branch information
VanishMax authored Aug 2, 2024
1 parent ecc548e commit f983923
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-jokes-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'minifront': patch
---

Fix NonNativeFeeWarning not being displayed
1 change: 1 addition & 0 deletions apps/minifront/src/components/send/send-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ export const SendForm = () => {
<NonNativeFeeWarning
balancesResponses={transferableBalancesResponses?.data}
amount={Number(amount)}
source={selection}
/>

<GasFee
Expand Down
108 changes: 87 additions & 21 deletions apps/minifront/src/components/shared/non-native-fee-warning.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,89 @@
import { BalancesResponse } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/view/v1/view_pb.js';
import { Metadata } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/asset/v1/asset_pb.js';
import { getAssetIdFromValueView } from '@penumbra-zone/getters/value-view';
import { getAssetId } from '@penumbra-zone/getters/metadata';
import { useStakingTokenMetadata } from '../../state/shared';
import { ReactNode } from 'react';
import { ReactNode, useCallback, useEffect, useState } from 'react';
import {
getAddressIndex,
getAmount,
getAssetIdFromBalancesResponseOptional,
} from '@penumbra-zone/getters/balances-response';
import { viewClient } from '../../clients.ts';
import { GasPrices } from '@buf/penumbra-zone_penumbra.bufbuild_es/penumbra/core/component/fee/v1/fee_pb';
import { getAssetId } from '@penumbra-zone/getters/metadata';

const hasTokenBalance = ({
source,
balancesResponses = [],
gasPrices,
stakingAssetMetadata,
}: {
source?: BalancesResponse;
balancesResponses: BalancesResponse[];
gasPrices: GasPrices[];
stakingAssetMetadata?: Metadata;
}): boolean => {
const account = getAddressIndex.optional()(source)?.account;
if (typeof account === 'undefined') {
return false;
}

// Finds the UM token in the user's account balances
const hasStakingToken = balancesResponses.some(
asset =>
getAssetIdFromValueView
.optional()(asset.balanceView)
?.equals(getAssetId.optional()(stakingAssetMetadata)) &&
getAddressIndex.optional()(asset)?.account === account,
);

if (hasStakingToken) {
return false;
}

const hasStakingToken = (
balancesResponses: BalancesResponse[] = [],
stakingAssetMetadata?: Metadata,
): boolean => {
return balancesResponses.some(asset =>
getAssetIdFromValueView
.optional()(asset.balanceView)
?.equals(getAssetId.optional()(stakingAssetMetadata)),
const accountAssets = balancesResponses.filter(
balance => getAddressIndex.optional()(balance)?.account === account,
);
// Finds the alt tokens in the user's account balances that can be used for fees
const hasAltTokens = accountAssets.some(balance => {
const amount = getAmount(balance);
const hasBalance = amount.lo !== 0n || amount.hi !== 0n;
if (!hasBalance) {
return false;
}

return gasPrices.some(price =>
price.assetId?.equals(getAssetIdFromBalancesResponseOptional(balance)),
);
});

return hasAltTokens;
};

export const useShouldRender = (balancesResponses: BalancesResponse[] = [], amount: number) => {
const stakingTokenMetadata = useStakingTokenMetadata();
const userHasStakingToken = hasStakingToken(balancesResponses, stakingTokenMetadata.data);
const showNonNativeFeeWarning = amount > 0 && !userHasStakingToken;
const useGasPrices = () => {
const [prices, setPrices] = useState<GasPrices[]>([]);

const fetchGasPrices = useCallback(async () => {
const res = await viewClient.gasPrices({});
setPrices(res.altGasPrices);
}, []);

return showNonNativeFeeWarning;
useEffect(() => {
void fetchGasPrices();
}, [fetchGasPrices]);

return prices;
};

/**
* Renders a non-native fee warning if A) the user does not have any balance of
* the staking token to use for fees, and B) the amount the user has entered for
* a transaction (e.g., send or swap) is nonzero -- i.e., a fee will be
* required.
* Renders a non-native fee warning if
* 1. the user does not have any balance (in the selected account) of the staking token to use for fees
* 2. the user does not have sufficient balances in alternative tokens to cover the fees
*/
export const NonNativeFeeWarning = ({
balancesResponses,
balancesResponses = [],
amount,
source,
wrap = children => children,
}: {
/**
Expand All @@ -45,6 +96,11 @@ export const NonNativeFeeWarning = ({
* determine whether the warning should render.
*/
amount: number;
/**
* A source token – helps determine whether the user has UM token
* in the same account as `source` to use for fees.
*/
source?: BalancesResponse;
/*
* Since this component determines for itself whether to render, a parent
* component can't optionally render wrapper markup depending on whether this
Expand All @@ -57,13 +113,23 @@ export const NonNativeFeeWarning = ({
* <NonNativeFeeWarning
* balancesResponses={balancesResponses}
* amount={amount}
* source={selectedBalancesResponse}
* wrap={children => <div className='mt-5'>{children}</div>}
* />
* ```
*/
wrap?: (children: ReactNode) => ReactNode;
}) => {
const shouldRender = useShouldRender(balancesResponses, amount);
const gasPrices = useGasPrices();
const stakingTokenMetadata = useStakingTokenMetadata();
const shouldRender =
!!amount &&
hasTokenBalance({
source,
balancesResponses,
gasPrices,
stakingAssetMetadata: stakingTokenMetadata.data,
});

if (!shouldRender) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const TokenSwapInput = () => {
<NonNativeFeeWarning
balancesResponses={balancesResponses?.data}
amount={Number(amount)}
source={assetIn}
wrap={children => (
<>
{/* This div adds an empty line */} <div className='h-4' />
Expand Down

0 comments on commit f983923

Please sign in to comment.