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

feat(wallet): update balance finder style #4948

Merged
merged 70 commits into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
6b464c8
feat: update buttons and title
evavirseda Jan 20, 2025
da7e22d
fix accordion
evavirseda Jan 21, 2025
c5e8e7b
feat: update styles
evavirseda Jan 21, 2025
90a6e0d
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
evavirseda Jan 21, 2025
9bd0183
feat(core): add StardustIndexerClient
panteleymonchuk Jan 22, 2025
8ca684e
feat(dashboard): add stardust objects from indexer.
panteleymonchuk Jan 22, 2025
d071500
feat: update balance finder fetch to include all relevant assets WIP
cpl121 Jan 22, 2025
e4e7c69
feat(core): enhance StardustIndexerClient with basic output mapping a…
panteleymonchuk Jan 22, 2025
ac691a6
Merge branch 'develop' into tooling-wallet/update-balance-finder-fetc…
cpl121 Jan 23, 2025
e8439d7
Merge branch 'develop' into tooling-wallet/add-objects-from-indexer
cpl121 Jan 23, 2025
f4713c5
Merge branch 'tooling-wallet/add-objects-from-indexer' into tooling-w…
cpl121 Jan 23, 2025
8884242
feat(dashboard): add NFT resolved outputs retrieval and mapping funct…
panteleymonchuk Jan 23, 2025
adedf67
Merge remote-tracking branch 'origin/develop' into tooling-wallet/add…
panteleymonchuk Jan 23, 2025
01975e8
refactor(core, dashboard): unify output types and enhance mapping fun…
panteleymonchuk Jan 23, 2025
151fd0b
Merge branch 'tooling-wallet/add-objects-from-indexer' into tooling-w…
cpl121 Jan 24, 2025
6c9c84a
feat(wallet-dashboard, core): enhance Stardust output handling and ad…
panteleymonchuk Jan 24, 2025
88f9c02
Merge remote-tracking branch 'origin/develop' into tooling-wallet/add…
panteleymonchuk Jan 24, 2025
a945c1c
Merge branch 'tooling-wallet/add-objects-from-indexer' into tooling-w…
cpl121 Jan 27, 2025
47bebe0
Merge branch 'develop' into tooling-wallet/add-objects-from-indexer
panteleymonchuk Jan 27, 2025
3bb1c6f
fix(wallet): include stardustIndexerClient to fetch shared objects in…
cpl121 Jan 27, 2025
f176c64
fix(core, dashboard): pr fixes
panteleymonchuk Jan 27, 2025
ccf79dd
fix(wallet, core): move stardust indexer provider to core
cpl121 Jan 27, 2025
b867ce5
fix(wallet): improve balance finder logic
cpl121 Jan 27, 2025
1eb257c
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
evavirseda Jan 28, 2025
3e002b4
feat(sdk): add metadata configuration for stardustIndexer in .env.def…
panteleymonchuk Jan 28, 2025
cf895b3
Merge branch 'develop' into tooling-wallet/add-objects-from-indexer
panteleymonchuk Jan 28, 2025
3a88c54
Merge branch 'tooling-wallet/add-objects-from-indexer' into tooling-w…
cpl121 Jan 28, 2025
387ff25
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
evavirseda Jan 28, 2025
026efe8
refactor(core): rename limit parameter to page_size in StardustIndexe…
panteleymonchuk Jan 28, 2025
4c523eb
refactor(core): update PageParams interface and adjust pagination par…
panteleymonchuk Jan 28, 2025
926413d
Merge remote-tracking branch 'origin/develop' into tooling-wallet/add…
panteleymonchuk Jan 28, 2025
b4c3ede
refactor(core): update return_amount
panteleymonchuk Jan 28, 2025
7dabd6e
feat: add logic to display data
evavirseda Jan 28, 2025
303563e
refactor(core): update StardustIndexerClient methods and rename hook …
panteleymonchuk Jan 28, 2025
9bdab66
refactor(dashboard): remove unused variables in useGetAllStardustShar…
panteleymonchuk Jan 28, 2025
234c459
fix: add missing 0x to stardust package id
begonaalvarezd Jan 28, 2025
3d50207
refactor(core, dashboard): update Stardust package ID format, improve…
panteleymonchuk Jan 28, 2025
8289238
Merge branch 'tooling-wallet/add-objects-from-indexer' into tooling-w…
cpl121 Jan 28, 2025
2e9c439
fix(wallet): flagged as address to import
cpl121 Jan 28, 2025
1d7b9a8
Merge remote-tracking branch 'origin/develop' into tooling-wallet/upd…
begonaalvarezd Jan 29, 2025
b21d83d
Merge branches 'tooling-wallet/update-balance-finder-fetch-relevant-a…
cpl121 Jan 29, 2025
c0ad98c
fix(wallet): add constant to set a limiit of the objects to fetch in …
cpl121 Jan 29, 2025
6204e2f
fix build
evavirseda Jan 30, 2025
8916076
remove debris
evavirseda Jan 30, 2025
b6b3001
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
evavirseda Jan 30, 2025
3de116d
Merge branch 'develop' into tooling-wallet/update-balance-finder-fetc…
evavirseda Jan 30, 2025
055378c
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
evavirseda Jan 30, 2025
a7b1bd1
fixes
evavirseda Jan 30, 2025
ef086be
Merge branch 'develop' into tooling-wallet/update-balance-finder-fetc…
evavirseda Jan 30, 2025
774ccd6
fix(wallet): rename boolean objects
cpl121 Jan 30, 2025
f0a014b
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
cpl121 Jan 30, 2025
4655200
Merge branch 'develop' of github.com:iotaledger/iota into tooling-wal…
cpl121 Jan 30, 2025
e7ea0a3
Merge branch 'tooling-wallet/update-balance-finder-fetch-relevant-ass…
cpl121 Jan 30, 2025
fd79be2
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
cpl121 Jan 30, 2025
8fb4e5f
rename constants and use object per request constant
evavirseda Jan 30, 2025
a1ed19e
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
evavirseda Jan 31, 2025
37ccd0c
fix hooks error
evavirseda Jan 31, 2025
5bd8701
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
evavirseda Jan 31, 2025
6eecee4
fixes
evavirseda Jan 31, 2025
d3a4e9a
feat: add useGetOwnedObjectsMultipleAddr hook
evavirseda Feb 3, 2025
b999bc3
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
evavirseda Feb 3, 2025
f0baa55
Merge branch 'develop' into tooling-wallet/update-balance-finder-view
cpl121 Feb 3, 2025
fa62fc2
fix error
evavirseda Feb 3, 2025
5536015
fixes
evavirseda Feb 3, 2025
c038748
cleanup
evavirseda Feb 3, 2025
2f469b5
fix(dashboard): add a optional chaining
cpl121 Feb 3, 2025
364a26e
Merge branch 'tooling-wallet/update-balance-finder-view' of github.co…
cpl121 Feb 3, 2025
662b862
feat: add new hook to get sharedobjects
evavirseda Feb 4, 2025
da0fc6a
refactor: Grouped fetch of addresses balances
marc2332 Feb 4, 2025
37dd82d
rename var
marc2332 Feb 4, 2025
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
1 change: 0 additions & 1 deletion apps/core/src/components/collapsible/Collapsible.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ export function Collapsible({
return (
<Accordion hideBorder={hideBorder}>
<AccordionHeader
hideBorder={hideBorder}
hideArrow={hideArrow}
isExpanded={isOpen ?? open}
onToggle={() => handleOpenChange(!open)}
Expand Down
18 changes: 10 additions & 8 deletions apps/core/src/hooks/useFormatCoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,7 @@ export function useCoinMetadata(coinType?: string | null) {

// Optimize the known case of IOTA to avoid a network call:
if (coinType === IOTA_TYPE_ARG) {
const metadata: CoinMetadata = {
id: null,
decimals: 9,
description: '',
iconUrl: null,
name: 'IOTA',
symbol: 'IOTA',
};
const metadata: CoinMetadata = IOTA_COIN_METADATA;

return metadata;
}
Expand Down Expand Up @@ -100,6 +93,15 @@ export function useCoinMetadata(coinType?: string | null) {
});
}

export const IOTA_COIN_METADATA: CoinMetadata = {
id: null,
decimals: 9,
description: '',
iconUrl: null,
name: 'IOTA',
symbol: 'IOTA',
};

// TODO #1: This handles undefined values to make it easier to integrate with
// the reset of the app as it is today, but it really shouldn't in a perfect world.
export function useFormatCoin(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export function CollapsibleCard({
<div className="relative w-full">
<Accordion hideBorder={hideBorder}>
<AccordionHeader
hideBorder={hideBorder}
hideArrow={hideArrow}
isExpanded={open}
onToggle={() => setOpen(!open)}
Expand Down
7 changes: 1 addition & 6 deletions apps/explorer/src/pages/transaction-result/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,7 @@ function Event({ event, divider }: { event: IotaEvent; divider: boolean }): JSX.
isTruncated
/>
<Accordion hideBorder>
<AccordionHeader
hideBorder
hideArrow
isExpanded={open}
onToggle={() => setOpen(!open)}
>
<AccordionHeader hideArrow isExpanded={open} onToggle={() => setOpen(!open)}>
<div className="flex w-full flex-row justify-between gap-xxxs pl-xxs text-neutral-40 dark:text-neutral-60">
<span className="text-body-md">
{open ? 'Hide' : 'View'} Event Data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ function ObjectDetailPanel({ panelContent, headerContent }: ObjectDetailPanelPro
const [open, setOpen] = useState(false);
return (
<Accordion hideBorder>
<AccordionHeader hideBorder hideArrow isExpanded={open} onToggle={() => setOpen(!open)}>
<AccordionHeader hideArrow isExpanded={open} onToggle={() => setOpen(!open)}>
<div className="flex w-full flex-row items-center justify-between px-md--rs">
<div className="flex flex-row gap-xxxs text-neutral-40 dark:text-neutral-60">
<span className="text-body-md">Object</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,14 @@ export function AccordionHeader({
children,
isExpanded,
hideArrow,
hideBorder,
}: PropsWithChildren<AccordionHeaderProps>) {
return (
<div
onClick={onToggle}
className={cx(
'state-layer relative flex cursor-pointer items-center justify-between gap-md py-sm--rs',
'state-layer relative flex cursor-pointer items-center justify-between gap-md overflow-hidden rounded-xl py-sm--rs',
{
'pr-md--rs': !hideArrow,
'rounded-xl': hideBorder,
},
)}
>
Expand Down Expand Up @@ -100,8 +98,8 @@ export function Accordion({
}): React.JSX.Element {
return (
<div
className={cx('overflow-hidden rounded-xl bg-neutral-100 dark:bg-neutral-6', {
' border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8':
className={cx('rounded-xl bg-neutral-100 dark:bg-neutral-6', {
'border border-shader-neutral-light-8 dark:border-shader-neutral-dark-8':
!hideBorder,
})}
>
Expand Down
187 changes: 158 additions & 29 deletions apps/wallet/src/ui/app/components/accounts/AccountBalanceItem.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,175 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { formatAddress } from '@iota/iota-sdk/utils';
import { useCopyToClipboard } from '_hooks';
import { type SerializedUIAccount } from '_src/background/accounts/account';
import { useBalance, useFormatCoin } from '@iota/core';
import { Copy } from '@iota/apps-ui-icons';
import { Panel, ButtonUnstyled } from '@iota/apps-ui-kit';
import {
COIN_TYPE,
Collapsible,
formatBalance,
IOTA_COIN_METADATA,
STARDUST_BASIC_OUTPUT_TYPE,
STARDUST_NFT_OUTPUT_TYPE,
TIMELOCK_IOTA_TYPE,
TIMELOCK_STAKED_TYPE,
useBalance,
useFormatCoin,
} from '@iota/core';
import { TriangleDown } from '@iota/apps-ui-icons';
import clsx from 'clsx';
import { Badge, BadgeType } from '@iota/apps-ui-kit';
import { formatAddress, IOTA_TYPE_ARG } from '@iota/iota-sdk/utils';
import {
useGetOwnedObjectsMultipleAddresses,
useGetSharedObjectsMultipleAddresses,
} from '../../hooks';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useIotaClientContext } from '@iota/dapp-kit';

interface AccountBalanceItemProps {
account: SerializedUIAccount;
accounts: SerializedUIAccount[];
accountIndex: string;
}

export function AccountBalanceItem({ account }: AccountBalanceItemProps): JSX.Element {
const copyAddress = useCopyToClipboard(account.address, {
copySuccessMessage: 'Address copied',
});
const { data: balance } = useBalance(account.address, {
refetchInterval: false,
const OBJECT_PER_REQ = 1;

export function AccountBalanceItem({
accounts,
accountIndex,
}: AccountBalanceItemProps): JSX.Element {
const addresses = accounts.map(({ address }) => address);

const queryClient = useQueryClient();
const iotaContext = useIotaClientContext();

const { data: sumOfBalances } = useQuery({
queryKey: ['getBalance', ...addresses],
async queryFn() {
return await Promise.all(
addresses.map(async (address) => {
const params = {
coinType: IOTA_TYPE_ARG,
owner: address!,
};
return queryClient.ensureQueryData({
queryKey: [iotaContext.network, 'getBalance'],
queryFn: () => iotaContext.client.getBalance(params),
});
}),
);
},
select(balances) {
const balance = balances.reduce((acc, { totalBalance }) => {
return BigInt(acc) + BigInt(totalBalance);
}, BigInt(0));
const [formatted, symbol] = formatBalance(balance, IOTA_COIN_METADATA.decimals);
return `${formatted} ${symbol}`;
},
gcTime: 0,
staleTime: 0,
});

const totalBalance = balance?.totalBalance || '0';
const coinType = balance?.coinType;
const [formatted, symbol] = useFormatCoin(BigInt(totalBalance), coinType);
const { data: ownedObjects } = useGetOwnedObjectsMultipleAddresses(
addresses,
{
MatchNone: [
{ StructType: COIN_TYPE },
{ StructType: TIMELOCK_IOTA_TYPE },
{ StructType: TIMELOCK_STAKED_TYPE },
{ StructType: STARDUST_BASIC_OUTPUT_TYPE },
{ StructType: STARDUST_NFT_OUTPUT_TYPE },
],
},
OBJECT_PER_REQ,
);

const { data: vestingObjects } = useGetOwnedObjectsMultipleAddresses(
addresses,
{
MatchAny: [{ StructType: TIMELOCK_IOTA_TYPE }, { StructType: TIMELOCK_STAKED_TYPE }],
},
OBJECT_PER_REQ,
);

const { data: stardustOwnedObjects } = useGetOwnedObjectsMultipleAddresses(
addresses,
{
MatchAny: [
{ StructType: STARDUST_BASIC_OUTPUT_TYPE },
{ StructType: STARDUST_NFT_OUTPUT_TYPE },
],
},
OBJECT_PER_REQ,
);

const { data: stardustSharedObjects } = useGetSharedObjectsMultipleAddresses(
addresses,
OBJECT_PER_REQ,
);

const hasMigrationObjects =
!!stardustOwnedObjects?.pages?.[0]?.[0]?.data?.length ||
!!stardustSharedObjects?.pages?.[0]?.length;

const hasAccountAssets = !!ownedObjects?.pages?.[0]?.[0]?.data?.length;

const hasVestingObjects = !!vestingObjects?.pages?.[0]?.[0]?.data?.length;

return (
<Panel hasBorder>
<div className="group flex cursor-pointer flex-col gap-sm rounded-xl px-md py-sm">
<div className="flex w-full flex-row items-center justify-between">
<div className="text-steel-dark flex gap-xs leading-none">
<span className="text-body-md">{formatAddress(account.address)}</span>
<div className="flex gap-xxs opacity-0 duration-100 group-hover:opacity-100">
<ButtonUnstyled onClick={copyAddress}>
<Copy className="h-2.5 w-2.5" />
</ButtonUnstyled>
<Collapsible
defaultOpen
hideArrow
render={({ isOpen }) => (
<div className="relative flex min-h-[52px] w-full items-center justify-between gap-1 py-2 pl-1 pr-sm text-neutral-10 dark:text-neutral-92">
<div className="flex items-center gap-xxs">
<TriangleDown
className={clsx(
'h-5 w-5 ',
isOpen
? 'rotate-0 transition-transform ease-linear'
: '-rotate-90 transition-transform ease-linear',
)}
/>
<div className="flex flex-col items-start gap-xxs">
<div className="text-title-md">Wallet {Number(accountIndex) + 1}</div>
<span className="text-body-sm text-neutral-40 dark:text-neutral-60">
{accounts.length} {accounts.length > 1 ? 'addresses' : 'address'}
</span>
</div>
</div>
<div className="flex flex-col items-end gap-xxs">
<span>{sumOfBalances}</span>
<div className="flex flex-row gap-xxs">
{hasAccountAssets && <Badge type={BadgeType.Neutral} label="Assets" />}
{hasVestingObjects && (
<Badge type={BadgeType.Neutral} label="Vesting" />
)}
{hasMigrationObjects && (
<Badge type={BadgeType.Neutral} label="Migration" />
)}
</div>
</div>

<span className="text-body-sm">
{formatted} {symbol}
</span>
</div>
)}
>
<div className="flex flex-col gap-y-sm p-sm pl-lg text-body-md text-neutral-10 dark:text-neutral-92">
{accounts.map(({ address, id }) => (
<AddressItem key={id} address={address} />
))}
</div>
</Panel>
</Collapsible>
);
}

export function AddressItem({ address }: { address: string }): JSX.Element {
const { data: balance } = useBalance(address!);
const totalBalance = balance?.totalBalance || '0';
const coinType = balance?.coinType || '';
const [formatted, symbol] = useFormatCoin(BigInt(totalBalance), coinType);

return (
<div className="flex w-full flex-row justify-between">
<span>{formatAddress(address)}</span>
<span>{`${formatted} ${symbol}`}</span>
</div>
);
}
2 changes: 2 additions & 0 deletions apps/wallet/src/ui/app/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ export * from './useSigner';
export * from './useStorageMigrationStatus';
export * from './useUnlockMutation';
export * from './useUnlockedGuard';
export * from './useGetOwnedObjectsMultipleAddresses';
export * from './useGetSharedObjectsMultipleAddresses';
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) 2024 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import { useIotaClient } from '@iota/dapp-kit';
import { type PaginatedObjectsResponse, type IotaObjectDataFilter } from '@iota/iota-sdk/client';
import { useInfiniteQuery } from '@tanstack/react-query';

const MAX_OBJECTS_PER_REQ = 6;

export function useGetOwnedObjectsMultipleAddresses(
addresses: string[],
filter?: IotaObjectDataFilter,
maxObjectRequests = MAX_OBJECTS_PER_REQ,
) {
const client = useIotaClient();

return useInfiniteQuery<PaginatedObjectsResponse[]>({
initialPageParam: null,
queryKey: ['get-owned-objects', addresses, filter, maxObjectRequests],
queryFn: async ({ pageParam }) => {
try {
const responses = await Promise.all(
addresses.map((address) =>
client.getOwnedObjects({
owner: address,
filter,
options: {
showType: true,
showContent: true,
showDisplay: true,
},
limit: maxObjectRequests,
cursor: pageParam as string | null,
}),
),
);
return responses.flat();
} catch (error) {
return [];
}
},
staleTime: 10 * 1000,
enabled: addresses.length > 0,
getNextPageParam: (lastPage) => {
const nextCursor = lastPage.find((res) => res.hasNextPage)?.nextCursor;
return nextCursor || null;
},
});
}
Loading
Loading