Skip to content

Commit

Permalink
chore: display both spendable and actual balance in wallet-ui (#310)
Browse files Browse the repository at this point in the history
* fix: allow multiple consecutive pending transactions in estimateFee and estimateFeeBulk

* fix: remove try catch block. Just check if nonce provided

* test: estimateFee.test.ts should rely only on estimateFeeBulk

* chore: lint + prettier

* refactor: function account nounce on block naming

* refactor: removed gasConsumed and gasPrice in estamiteFee

* fix: nonce undefined

* revert: yarn.lock and snap.manifest.json from main

* fix: use BlockIdentifierEnum

* fix: update packages/starknet-snap/src/utils/starknetUtils.ts

Co-authored-by: Stanley Yuen <[email protected]>

* feat: sets blockIdentifier on getProvider no need for getAccountNonceOnLatest method

* chore: lint + prettier

* fix: pending/latest balance

* feat: support spendable balance in case of unconfirmed amount

* revert: wallet-ui changes

* feat: wallet-ui support spendable balance in case of unconfirmed amount

* test: update getErc20TokenBalance.test.ts

* feat: let consumer app decide the logic to use to display balance

* chore: lint + prettier

* refactor: add getTokenBalanceWithDetails and getTokenBalancesWithDetails helper

* fix: address PR feedback and update implementation

* fix: token-balance type

* fix: address PR feedback and update implementation

* fix: address PR feedback and update implementation

---------

Co-authored-by: Stanley Yuen <[email protected]>
  • Loading branch information
khanti42 and stanleyyconsensys authored Aug 13, 2024
1 parent c7facfc commit ea87d38
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Wrapper,
} from './AssetListItem.style';
import { DoubleIcons } from 'components/ui/atom/DoubleIcons';
import { getHumanReadableAmount } from 'utils/utils';
import { getSpendableTotalBalance } from 'utils/utils';

interface Props extends HTMLAttributes<HTMLDivElement> {
asset: Erc20TokenBalance;
Expand All @@ -34,7 +34,7 @@ export const AssetListItemView = ({
<Column>
<Label>{asset.name}</Label>
<Description>
{getHumanReadableAmount(asset)} {asset.symbol}
{getSpendableTotalBalance(asset)} {asset.symbol}
</Description>
</Column>
</Left>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getAmountPrice } from 'utils/utils';
import { Button } from 'components/ui/atom/Button';
import { AssetQuantity } from 'components/ui/molecule/AssetQuantity';
import { PopIn } from 'components/ui/molecule/PopIn';
import { getHumanReadableAmount } from 'utils/utils';
import { getSpendableTotalBalance } from 'utils/utils';
import { Buttons, HeaderButton, Wrapper } from './Header.style';
import { ReceiveModal } from './ReceiveModal';
import { SendModal } from './SendModal';
Expand Down Expand Up @@ -64,7 +64,9 @@ export const HeaderView = ({ address }: Props) => {
<Wrapper>
<AssetQuantity
USDValue={getUSDValue()}
currencyValue={getHumanReadableAmount(wallet.erc20TokenBalanceSelected)}
currencyValue={getSpendableTotalBalance(
wallet.erc20TokenBalanceSelected,
)}
currency={wallet.erc20TokenBalanceSelected.symbol}
size="big"
centered
Expand Down
42 changes: 20 additions & 22 deletions packages/wallet-ui/src/services/useStarkNetSnap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
} from '../slices/walletSlice';
import Toastr from 'toastr2';
import {
addMissingPropertiesToToken,
hexToString,
retry,
isGTEMinVersion,
getTokenBalanceWithDetails,
} from '../utils/utils';
import { setWalletConnection } from '../slices/walletSlice';
import { Network, VoyagerTransactionType } from '../types';
Expand Down Expand Up @@ -270,26 +270,20 @@ export const useStarkNetSnap = () => {
: (acc as Account).upgradeRequired) ?? false;
}

const tokenBalances = await Promise.all(
tokens.map(async (token) => {
const accountAddr = Array.isArray(acc) ? acc[0].address : acc.address;
return await getTokenBalance(token.address, accountAddr, chainId);
}),
);
const accountAddr = Array.isArray(acc) ? acc[0].address : acc.address;

const tokenUSDPrices = await Promise.all(
// Get all tokens balance, USD value, and format them into Erc20TokenBalance type
const tokensWithBalances: Erc20TokenBalance[] = await Promise.all(
tokens.map(async (token) => {
return await getAssetPriceUSD(token);
const balance = await getTokenBalance(
token.address,
accountAddr,
chainId,
);
const usdPrice = await getAssetPriceUSD(token);
return await getTokenBalanceWithDetails(balance, token, usdPrice);
}),
);

const tokensWithBalances = tokens.map((token, index): Erc20TokenBalance => {
return addMissingPropertiesToToken(
token,
tokenBalances[index],
tokenUSDPrices[index],
);
});
if (networks) {
dispatch(setNetworks(networks));
}
Expand Down Expand Up @@ -625,9 +619,9 @@ export const useStarkNetSnap = () => {
chainId,
);
const usdPrice = await getAssetPriceUSD(token);
const tokenWithBalance: Erc20TokenBalance = addMissingPropertiesToToken(
token,
const tokenWithBalance: Erc20TokenBalance = getTokenBalanceWithDetails(
tokenBalance,
token,
usdPrice,
);
dispatch(upsertErc20TokenBalance(tokenWithBalance));
Expand Down Expand Up @@ -664,9 +658,9 @@ export const useStarkNetSnap = () => {
chainId,
);
const usdPrice = await getAssetPriceUSD(foundTokenWithBalance);
const tokenWithBalance: Erc20TokenBalance = addMissingPropertiesToToken(
foundTokenWithBalance,
const tokenWithBalance: Erc20TokenBalance = getTokenBalanceWithDetails(
tokenBalance,
foundTokenWithBalance,
usdPrice,
);
dispatch(upsertErc20TokenBalance(tokenWithBalance));
Expand Down Expand Up @@ -694,10 +688,14 @@ export const useStarkNetSnap = () => {
},
},
});
return response.balanceLatest;
return response;
} catch (err) {
//eslint-disable-next-line no-console
console.error(err);
return {
balanceLatest: '0x0',
balancePending: '0x0',
};
}
};

Expand Down
12 changes: 12 additions & 0 deletions packages/wallet-ui/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type Network = Pick<
export interface Erc20TokenBalance extends Types.Erc20Token {
amount: BigNumber;
usdPrice?: number;
spendableAmount?: BigNumber;
}
export type TransactionStatusOptions =
| 'Received'
Expand All @@ -37,7 +38,18 @@ export enum TransactionStatus { // for retrieving txn from Starknet feeder gatew
REJECTED = 'REJECTED',
}

export enum BalanceType {
Spendable = 'spendable',
Total = 'total',
}

export type {
Erc20Token,
Transaction,
} from '@consensys/starknet-snap/src/types/snapState';

// Define the type for your token balances
export interface TokenBalance {
balancePending: BigNumber;
balanceLatest: BigNumber;
}
42 changes: 40 additions & 2 deletions packages/wallet-ui/src/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
TIMEOUT_DURATION,
MIN_ACC_CONTRACT_VERSION,
} from './constants';
import { Erc20Token, Erc20TokenBalance } from 'types';
import { Erc20Token, Erc20TokenBalance, TokenBalance } from 'types';
import { constants } from 'starknet';

export const shortenAddress = (address: string, num = 3) => {
Expand Down Expand Up @@ -45,19 +45,23 @@ export const isValidAddress = (toCheck: string) => {
export const addMissingPropertiesToToken = (
token: Erc20Token,
balance?: string,
balanceSpendable?: string,
usdPrice?: number,
): Erc20TokenBalance => {
return {
...token,
amount: ethers.BigNumber.from(balance ? balance : '0x0'),
spendableAmount: ethers.BigNumber.from(
balanceSpendable ? balanceSpendable : '0x0',
),
usdPrice: usdPrice,
};
};

export const getHumanReadableAmount = (
asset: Erc20TokenBalance,
assetAmount?: string,
) => {
): string => {
const amountStr = assetAmount
? assetAmount
: ethers.utils.formatUnits(asset.amount, asset.decimals);
Expand All @@ -79,6 +83,24 @@ export const getHumanReadableAmount = (
return amountStr.substring(0, indexDecimal + firstNonZeroIndex + 3);
};

export const getSpendableTotalBalance = (asset: Erc20TokenBalance): string => {
if (asset.spendableAmount === undefined) {
throw new Error('Spendable amount can not be undefined');
}
const spendableAmount = getHumanReadableAmount(
asset,
asset.spendableAmount.toString(),
);

if (asset.spendableAmount === asset.amount) {
return `${spendableAmount}`;
}

const totalAmount = getHumanReadableAmount(asset, asset.amount.toString());

return `${spendableAmount} (${totalAmount})`;
};

export const getMaxDecimalsReadable = (
asset: Erc20TokenBalance,
assetAmount?: string,
Expand Down Expand Up @@ -218,3 +240,19 @@ export const shortenDomain = (domain: string, maxLength = 18) => {
const shortenedPartLength = maxLength - ellipsis.length;
return `${domain.substring(0, shortenedPartLength)}${ellipsis}`;
};

export function getTokenBalanceWithDetails(
tokenBalance: TokenBalance,
token: Erc20Token,
tokenUSDPrice?: number,
): Erc20TokenBalance {
const { balancePending, balanceLatest } = tokenBalance;
const spendableBalance =
balancePending < balanceLatest ? balancePending : balanceLatest;
return addMissingPropertiesToToken(
token,
balanceLatest.toString(),
spendableBalance.toString(),
tokenUSDPrice,
);
}

0 comments on commit ea87d38

Please sign in to comment.