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 island #1793

Merged
merged 207 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
207 commits
Select commit Hold shift + click to select a range
a4b92ff
initial walletisland ux
brendan-defi Jan 2, 2025
c5e8c94
svgs and util placeholder
brendan-defi Jan 2, 2025
3c81220
fix: imports
brendan-defi Jan 2, 2025
41f0302
playground demo
brendan-defi Jan 2, 2025
6eb0a65
playground options
brendan-defi Jan 2, 2025
219d3cb
demo positioning
brendan-defi Jan 2, 2025
926b985
add walletisland to wallet content options
brendan-defi Jan 3, 2025
fb431be
WalletIslandProps
brendan-defi Jan 3, 2025
5438e48
refactor for new architecture
brendan-defi Jan 3, 2025
7772156
temp animations
brendan-defi Jan 3, 2025
7450114
fix: linters
brendan-defi Jan 3, 2025
402fd3d
fix walletisland provider
brendan-defi Jan 3, 2025
1f66625
qr animations
brendan-defi Jan 3, 2025
cebe403
update animations
brendan-defi Jan 3, 2025
d147139
fix: icon size
brendan-defi Jan 3, 2025
4df7441
content block animations
brendan-defi Jan 3, 2025
7972827
fix: linters
brendan-defi Jan 3, 2025
ac7ae76
fix: test:
brendan-defi Jan 3, 2025
85c9542
fix: tests
brendan-defi Jan 3, 2025
d714873
improve defaults, fix variable spelling
brendan-defi Jan 3, 2025
1079134
fix: tests
brendan-defi Jan 3, 2025
7dfcf70
fixed error handling for useWalletIslandContext
brendan-defi Jan 3, 2025
e327b17
optional containerRef
brendan-defi Jan 3, 2025
3eb9d22
fix: tests
brendan-defi Jan 3, 2025
374617b
fix: linters
brendan-defi Jan 3, 2025
b9841a4
fix: test
brendan-defi Jan 3, 2025
95879d4
fix: lint
brendan-defi Jan 3, 2025
5755efb
fix: lint, test
brendan-defi Jan 3, 2025
ebbeb7a
fix: linters
brendan-defi Jan 3, 2025
9ea9b7c
add types to tests
brendan-defi Jan 3, 2025
a81ba1b
more types in swaptests
brendan-defi Jan 3, 2025
4b26396
portfolio api method and hook
brendan-defi Jan 4, 2025
c178759
portfolio data
brendan-defi Jan 4, 2025
96a5b0f
portfolio refresh, svg sizing
brendan-defi Jan 4, 2025
63cbb94
refresh svg
brendan-defi Jan 4, 2025
ef9f76c
loading state
brendan-defi Jan 4, 2025
a3a39e7
refetch and loading state
brendan-defi Jan 4, 2025
5aa39fa
update query config
brendan-defi Jan 4, 2025
8b2c6bd
fix: linters
brendan-defi Jan 4, 2025
cfa4771
refactored types
brendan-defi Jan 4, 2025
6bf0c6b
removing unused files, adding tests
brendan-defi Jan 4, 2025
8b114ba
add: tests
brendan-defi Jan 4, 2025
843ea68
fix: tests
brendan-defi Jan 4, 2025
6297e13
fix: tests
brendan-defi Jan 4, 2025
a1bb0b7
add test
brendan-defi Jan 4, 2025
49f9343
add tests
brendan-defi Jan 4, 2025
e7367cd
fix: linters
brendan-defi Jan 4, 2025
b7e0355
specify swap tokens
brendan-defi Jan 4, 2025
13af2c3
updated type definitions
brendan-defi Jan 6, 2025
ce617f8
update type usage
brendan-defi Jan 6, 2025
ddc7682
fix: linters
brendan-defi Jan 6, 2025
12a465e
fix tests
brendan-defi Jan 6, 2025
29b6577
fix: types
brendan-defi Jan 6, 2025
69e7b8c
add test
brendan-defi Jan 6, 2025
5f0c269
update spacing
brendan-defi Jan 6, 2025
5f46e71
filter zero-balance tokens
brendan-defi Jan 6, 2025
10dfed1
standard icon treatment
brendan-defi Jan 6, 2025
59ca5a3
add test
brendan-defi Jan 6, 2025
a56d159
fix: linters
brendan-defi Jan 6, 2025
5e4b35c
fix: tests
brendan-defi Jan 6, 2025
9c400d6
address comments
brendan-defi Jan 6, 2025
3bdf6a0
change default playground position
brendan-defi Jan 7, 2025
0ed645f
draggable prop on wallet
brendan-defi Jan 7, 2025
44731f8
wallet island default to-tokens, max-height
brendan-defi Jan 7, 2025
b5185b7
remove ref from walletisland
brendan-defi Jan 7, 2025
395dc10
refactor draggable component to entire wallet
brendan-defi Jan 7, 2025
043de8e
prevent click functionality on drag
brendan-defi Jan 7, 2025
14e9df9
fix: lint
brendan-defi Jan 7, 2025
664c247
fix: types
brendan-defi Jan 7, 2025
a07a4f4
fix: types
brendan-defi Jan 7, 2025
516302c
fix: lint
brendan-defi Jan 7, 2025
bad641f
remove comment, fix lint
brendan-defi Jan 7, 2025
ab23fe2
move walletisland types to wallet/types
brendan-defi Jan 7, 2025
1347b9f
walletisland constants
brendan-defi Jan 7, 2025
4c4d383
add walletisland exports
brendan-defi Jan 7, 2025
533f7fd
draggable functionality on entire wallet
brendan-defi Jan 7, 2025
da0ffb5
fix: lint
brendan-defi Jan 7, 2025
cf518ea
fix walletisland-connectwallet gap
brendan-defi Jan 7, 2025
e39cfc0
fix icon sizes
brendan-defi Jan 7, 2025
a089bfb
update wallet tests
brendan-defi Jan 7, 2025
94ad92f
add test cleanup
brendan-defi Jan 7, 2025
d3a6f72
fix: lint
brendan-defi Jan 7, 2025
ad48add
stronger test cleanup
brendan-defi Jan 7, 2025
20be32a
add test for click prevention on drag
brendan-defi Jan 7, 2025
b19a1db
fix: lint
brendan-defi Jan 7, 2025
a9a6ed1
use standard tailwind
brendan-defi Jan 7, 2025
4add5bf
update get portfolios data structure
brendan-defi Jan 8, 2025
f53a6a7
remove focus, fix spacing, fix animation
brendan-defi Jan 8, 2025
349c9dc
remove focus, fix spacing, fix animation
brendan-defi Jan 8, 2025
0b8ba12
fix spacing
brendan-defi Jan 8, 2025
3339780
fix: tests
brendan-defi Jan 8, 2025
398bfa7
add null portfolios check
brendan-defi Jan 8, 2025
1f621e1
fixed click prevention on non-drag clicks
brendan-defi Jan 8, 2025
46405a9
fix: test
brendan-defi Jan 8, 2025
5bee9e3
remove duplicate theme detection
brendan-defi Jan 8, 2025
fc249b7
remove default swap tokens
brendan-defi Jan 8, 2025
baa3603
rename WalletIslandDefault to WalletIslandDraggable
brendan-defi Jan 8, 2025
86c00df
added WalletIslandFixed
brendan-defi Jan 8, 2025
fac159e
add WalletIslandFixed to playground
brendan-defi Jan 8, 2025
84f57d0
simplify demo
brendan-defi Jan 8, 2025
a37e418
update ock
brendan-defi Jan 8, 2025
eb0f96c
fix tests
brendan-defi Jan 8, 2025
6a34d67
use radiusInner, fix spacing, address comments
brendan-defi Jan 8, 2025
9ef7799
better svg titles
brendan-defi Jan 8, 2025
878c5d8
add startingPosition prop for WalletIslandDraggable
brendan-defi Jan 8, 2025
a727063
better aria-labels
brendan-defi Jan 8, 2025
f0375a5
remove throttle on portfolio refresh
brendan-defi Jan 8, 2025
ce0aefc
extract actions into callbacks
brendan-defi Jan 8, 2025
d78939f
better return value if no portfolios received
brendan-defi Jan 8, 2025
4e3b309
fix: lint
brendan-defi Jan 8, 2025
e733581
fix: tests
brendan-defi Jan 8, 2025
7eb35bb
fix: lint
brendan-defi Jan 8, 2025
a13fc24
update parameter for usePortfolioTokenBalances
brendan-defi Jan 8, 2025
f7b5dc8
fix: tests
brendan-defi Jan 8, 2025
65c522f
fix: test
brendan-defi Jan 8, 2025
24c3ce1
update QrCodeSvg props and value handling
brendan-defi Jan 8, 2025
eca9f4f
fix test
brendan-defi Jan 8, 2025
d5821ab
refactor PressableIcon
brendan-defi Jan 8, 2025
4369727
fix tests
brendan-defi Jan 8, 2025
434424c
fix: lint
brendan-defi Jan 8, 2025
09c4f86
add test
brendan-defi Jan 8, 2025
0806fb1
fix circular deps
brendan-defi Jan 8, 2025
b57f658
fix circular dependencies
brendan-defi Jan 8, 2025
83be94c
min-height on token holdings
brendan-defi Jan 8, 2025
6a9235a
update types
brendan-defi Jan 9, 2025
01fc57c
update tests for new types and filters
brendan-defi Jan 9, 2025
6766c59
remove filters and transform, now performed on backend
brendan-defi Jan 9, 2025
ffbf947
fix tests
brendan-defi Jan 9, 2025
d2df3ec
fix: lint
brendan-defi Jan 9, 2025
99748a3
update type to match data structure
brendan-defi Jan 9, 2025
d9e35aa
animations based on open position
brendan-defi Jan 9, 2025
a7f3999
move positioning useEffect to provider
brendan-defi Jan 9, 2025
717c143
fix tests
brendan-defi Jan 9, 2025
ac91b6e
fix tests
brendan-defi Jan 9, 2025
4a9860e
add tests
brendan-defi Jan 9, 2025
a9b4eba
fix: lints
brendan-defi Jan 9, 2025
7e8d08e
fix test
brendan-defi Jan 9, 2025
0ae9a74
fix test
brendan-defi Jan 9, 2025
9ea71a2
reuse token constants
brendan-defi Jan 9, 2025
e3e9edc
remove outer div
brendan-defi Jan 9, 2025
de32751
address QA comments
brendan-defi Jan 9, 2025
15177f2
use onAnimationEnd instead of timeouts for close handling
brendan-defi Jan 9, 2025
52fef78
update title
brendan-defi Jan 9, 2025
d73b68e
update default start position
brendan-defi Jan 9, 2025
a3d2299
remove empty div when balance is null
brendan-defi Jan 9, 2025
0880133
fix lints and tests
brendan-defi Jan 9, 2025
57125db
fix tests
brendan-defi Jan 9, 2025
dcb4182
fix default position for WalletIslandFixed demo
brendan-defi Jan 9, 2025
a9cdd59
update name and exports
brendan-defi Jan 9, 2025
b9ea440
simplify useQuery action key
brendan-defi Jan 10, 2025
acbfadb
number coercion for type safety
brendan-defi Jan 10, 2025
e3eb032
dedupe coinbaseWalletSvg
brendan-defi Jan 10, 2025
74cf138
ts history uses basescan
brendan-defi Jan 10, 2025
ef8ff14
use coinbase onramp for buy button
brendan-defi Jan 10, 2025
9672d90
fix layout shift on token balances refetch
brendan-defi Jan 10, 2025
2c74045
add tests
brendan-defi Jan 10, 2025
83284d9
fix: lints
brendan-defi Jan 10, 2025
080e1d3
improve token formatting
brendan-defi Jan 10, 2025
39687e3
add modal overlay property
brendan-defi Jan 10, 2025
780929c
useIsModalOpen hook with mutation observer
brendan-defi Jan 10, 2025
c0de59f
fix cursor and draggability issues
brendan-defi Jan 10, 2025
f35a14b
fix property name
brendan-defi Jan 10, 2025
2c4a743
simplify cursor display logic, add tests
brendan-defi Jan 10, 2025
47bc4e1
add tests
brendan-defi Jan 10, 2025
f77ac0f
add test
brendan-defi Jan 10, 2025
0b307fe
fix lints
brendan-defi Jan 10, 2025
2112d0c
update tests
brendan-defi Jan 10, 2025
1fb089b
empty container layout
brendan-defi Jan 10, 2025
1e7f339
fix: lints
brendan-defi Jan 10, 2025
0e494ce
fix tests
brendan-defi Jan 10, 2025
0d7f815
format currency
brendan-defi Jan 10, 2025
d592e09
add explainer comment
brendan-defi Jan 10, 2025
28be9ed
better theme defaults
brendan-defi Jan 10, 2025
0bb9025
fix svgs
brendan-defi Jan 10, 2025
010591d
refetch on window focus
brendan-defi Jan 10, 2025
112fc91
make drag-disable more generic
brendan-defi Jan 10, 2025
ea6bda8
add modal open state, rename subcomponent open/close state
brendan-defi Jan 10, 2025
b41e18f
better names for open/closed states
brendan-defi Jan 10, 2025
1c6e7f3
better url construction
brendan-defi Jan 10, 2025
76a0377
fix tests
brendan-defi Jan 10, 2025
8fb2389
discriminated union on draggable props
brendan-defi Jan 10, 2025
5981ef1
default starting position
brendan-defi Jan 10, 2025
e7d2cef
refactor draggable props
brendan-defi Jan 10, 2025
057226e
center align
brendan-defi Jan 10, 2025
6d35e8a
fix imports
brendan-defi Jan 10, 2025
0c19cdb
disable avatar pointer events
brendan-defi Jan 10, 2025
b1e6265
custom connect button
brendan-defi Jan 10, 2025
f31475d
update names
brendan-defi Jan 11, 2025
4917a52
update tests
brendan-defi Jan 11, 2025
eb5329d
rename, update tests, fix types
brendan-defi Jan 11, 2025
1a412d7
fix lints
brendan-defi Jan 11, 2025
3d345ae
draggable client-side
brendan-defi Jan 12, 2025
3f0f62a
handle ssr
brendan-defi Jan 12, 2025
e78fce4
ssr fixes
brendan-defi Jan 12, 2025
28dd025
fix ssr
brendan-defi Jan 12, 2025
4e1b353
fix window build issues
brendan-defi Jan 13, 2025
c526109
add border to default non-basename avatar
brendan-defi Jan 13, 2025
f354687
wallet utils and tests
brendan-defi Jan 13, 2025
f45cad8
better SSR handling
brendan-defi Jan 13, 2025
debb786
fix: lints
brendan-defi Jan 13, 2025
e0725c6
scrollbar-hidden util
brendan-defi Jan 13, 2025
beeef0e
add resposition on window resize
brendan-defi Jan 14, 2025
8105399
window resize handler
brendan-defi Jan 14, 2025
66276a3
fix: comment improvements
brendan-defi Jan 14, 2025
cf845dd
fix: lints
brendan-defi Jan 14, 2025
d1e1758
fix test
brendan-defi Jan 14, 2025
29dc53e
fix prop name
brendan-defi Jan 14, 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
Binary file modified playground/nextjs-app-router/bun.lockb
Binary file not shown.
17 changes: 13 additions & 4 deletions playground/nextjs-app-router/components/Demo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import SwapDefaultDemo from './demo/SwapDefault';
import TransactionDemo from './demo/Transaction';
import TransactionDefaultDemo from './demo/TransactionDefault';
import WalletDemo from './demo/Wallet';
import WalletAdvancedDefaultDemo from './demo/WalletAdvancedDefault';
import WalletDefaultDemo from './demo/WalletDefault';
import WalletIslandDemo from './demo/WalletIsland';

const activeComponentMapping: Record<OnchainKitComponent, React.FC> = {
[OnchainKitComponent.Buy]: BuyDemo,
Expand All @@ -31,6 +33,8 @@ const activeComponentMapping: Record<OnchainKitComponent, React.FC> = {
[OnchainKitComponent.SwapDefault]: SwapDefaultDemo,
[OnchainKitComponent.Wallet]: WalletDemo,
[OnchainKitComponent.WalletDefault]: WalletDefaultDemo,
[OnchainKitComponent.WalletIsland]: WalletIslandDemo,
[OnchainKitComponent.WalletAdvancedDefault]: WalletAdvancedDefaultDemo,
[OnchainKitComponent.TransactionDefault]: TransactionDefaultDemo,
[OnchainKitComponent.NFTMintCard]: NFTMintCardDemo,
[OnchainKitComponent.NFTCard]: NFTCardDemo,
Expand All @@ -39,7 +43,7 @@ const activeComponentMapping: Record<OnchainKitComponent, React.FC> = {
[OnchainKitComponent.IdentityCard]: IdentityCardDemo,
};

function Demo() {
export default function Demo() {
const { activeComponent } = useContext(AppContext);
const [isDarkMode, setIsDarkMode] = useState(true);
const [sideBarVisible, setSideBarVisible] = useState(true);
Expand Down Expand Up @@ -140,12 +144,17 @@ function Demo() {
</div>
</div>
<div className="linear-gradient(to_bottom,#f0f0f0_1px,transparent_1px)] flex flex-1 flex-col bg-[linear-gradient(to_right,#f0f0f0_1px,transparent_1px), bg-[size:6rem_4rem]">
<div className="flex h-full w-full flex-col items-center justify-center">
<div
className={cn(
'flex h-full w-full flex-col items-center',
activeComponent === OnchainKitComponent.WalletAdvancedDefault
? 'mt-12 justify-start'
: 'justify-center',
)}
>
{ActiveComponent && <ActiveComponent />}
</div>
</div>
</>
);
}

export default Demo;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { WalletAdvancedDefault } from '@coinbase/onchainkit/wallet';

export default function WalletAdvancedDefaultDemo() {
return (
<div className="mx-auto flex justify-end">
<WalletAdvancedDefault />
</div>
);
}
5 changes: 5 additions & 0 deletions playground/nextjs-app-router/components/demo/WalletIsland.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { WalletIsland } from '@coinbase/onchainkit/wallet';

export default function WalletIslandDemo() {
return <WalletIsland />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export function ActiveComponent() {
<SelectItem value={OnchainKitComponent.WalletDefault}>
WalletDefault
</SelectItem>
<SelectItem value={OnchainKitComponent.WalletAdvancedDefault}>
WalletAdvancedDefault
</SelectItem>
<SelectItem value={OnchainKitComponent.WalletIsland}>
WalletIsland
</SelectItem>
<SelectItem value={OnchainKitComponent.NFTCard}>NFT Card</SelectItem>
<SelectItem value={OnchainKitComponent.NFTCardDefault}>
NFT Card Default
Expand Down
24 changes: 16 additions & 8 deletions playground/nextjs-app-router/onchainkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/onchainkit",
"version": "0.36.0",
"version": "0.36.4",
"type": "module",
"repository": "https://github.com/coinbase/onchainkit.git",
"license": "MIT",
Expand Down Expand Up @@ -30,15 +30,16 @@
"watch:tailwind": "tailwind -i ./src/styles/index.css -o ./src/tailwind.css --watch"
},
"peerDependencies": {
"@types/react": "^18",
"react": "^18",
"react-dom": "^18"
"@types/react": "^18 || ^19",
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
},
"dependencies": {
"@tanstack/react-query": "^5",
"clsx": "^2.1.1",
"graphql": "^14 || ^15 || ^16",
"graphql-request": "^6.1.0",
"qrcode": "^1.5.4",
"tailwind-merge": "^2.3.0",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.21.33",
Expand All @@ -59,6 +60,7 @@
"@storybook/test-runner": "^0.19.1",
"@testing-library/jest-dom": "^6.4.6",
"@testing-library/react": "^14.2.0",
"@types/qrcode": "^1",
"@types/react": "^18",
"@types/react-dom": "^18",
"@vitest/coverage-v8": "^2.0.5",
Expand Down Expand Up @@ -119,6 +121,12 @@
"import": "./esm/core/api/index.js",
"default": "./esm/core/api/index.js"
},
"./buy": {
"types": "./esm/buy/index.d.ts",
"module": "./esm/buy/index.js",
"import": "./esm/buy/index.js",
"default": "./esm/buy/index.js"
},
"./checkout": {
"types": "./esm/checkout/index.d.ts",
"module": "./esm/checkout/index.js",
Expand All @@ -138,10 +146,10 @@
"default": "./esm/fund/index.js"
},
"./identity": {
"types": "./esm/identity/index.d.ts",
"module": "./esm/identity/index.js",
"import": "./esm/identity/index.js",
"default": "./esm/identity/index.js"
"types": "./esm/ui/react/identity/index.d.ts",
"module": "./esm/ui/react/identity/index.js",
"import": "./esm/ui/react/identity/index.js",
"default": "./esm/ui/react/identity/index.js"
},
"./nft": {
"types": "./esm/ui/react/nft/index.d.ts",
Expand Down
2 changes: 2 additions & 0 deletions playground/nextjs-app-router/types/onchainkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export enum OnchainKitComponent {
TransactionDefault = 'transaction-default',
Wallet = 'wallet',
WalletDefault = 'wallet-default',
WalletIsland = 'wallet-island',
WalletAdvancedDefault = 'wallet-advanced-default',
NFTCard = 'nft-card',
NFTCardDefault = 'nft-card-default',
NFTMintCard = 'nft-mint-card',
Expand Down
126 changes: 126 additions & 0 deletions src/core-react/wallet/hooks/usePortfolioTokenBalances.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { getPortfolioTokenBalances } from '@/core/api/getPortfolioTokenBalances';
import type {
PortfolioTokenBalances,
PortfolioTokenWithFiatValue,
} from '@/core/api/types';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { usePortfolioTokenBalances } from './usePortfolioTokenBalances';

vi.mock('@/core/api/getPortfolioTokenBalances');

const mockAddress: `0x${string}` = '0x123';
const mockTokens: PortfolioTokenWithFiatValue[] = [
{
address: '0x123',
chainId: 8453,
decimals: 6,
image: '',
name: 'Token',
symbol: 'TOKEN',
cryptoBalance: 100,
fiatBalance: 100,
},
];
const mockPortfolioTokenBalances: PortfolioTokenBalances = {
address: mockAddress,
portfolioBalanceInUsd: 100,
tokenBalances: mockTokens,
};

const createWrapper = () => {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
return ({ children }: { children: React.ReactNode }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
};

describe('usePortfolioTokenBalances', () => {
beforeEach(() => {
vi.clearAllMocks();
});

it('should fetch token balances successfully', async () => {
vi.mocked(getPortfolioTokenBalances).mockResolvedValueOnce({
portfolios: [mockPortfolioTokenBalances],
});

const { result } = renderHook(
() => usePortfolioTokenBalances({ address: mockAddress }),
{ wrapper: createWrapper() },
);

expect(result.current.isLoading).toBe(true);

await waitFor(() => expect(result.current.isSuccess).toBe(true));

expect(getPortfolioTokenBalances).toHaveBeenCalledWith({
addresses: [mockAddress],
});

expect(result.current.data).toEqual(mockPortfolioTokenBalances);
});

it('should handle API errors', async () => {
vi.mocked(getPortfolioTokenBalances).mockResolvedValueOnce({
code: 'API Error',
error: 'API Error',
message: 'API Error',
});

const { result } = renderHook(
() => usePortfolioTokenBalances({ address: mockAddress }),
{ wrapper: createWrapper() },
);

await waitFor(() => expect(result.current.isError).toBe(true));

expect(result.current.error).toBeInstanceOf(Error);
expect(result.current.error?.message).toBe('API Error');
});

it('should not fetch when address is empty', () => {
renderHook(
() => usePortfolioTokenBalances({ address: '' as `0x${string}` }),
{
wrapper: createWrapper(),
},
);

expect(getPortfolioTokenBalances).not.toHaveBeenCalled();
});

it('should not fetch when address is undefined', () => {
renderHook(() => usePortfolioTokenBalances({ address: undefined }), {
wrapper: createWrapper(),
});

expect(getPortfolioTokenBalances).not.toHaveBeenCalled();
});

it('should return empty data when portfolios is empty', async () => {
vi.mocked(getPortfolioTokenBalances).mockResolvedValueOnce({
portfolios: [],
});

const { result } = renderHook(
() => usePortfolioTokenBalances({ address: mockAddress }),
{ wrapper: createWrapper() },
);

await waitFor(() => expect(result.current.isSuccess).toBe(true));

expect(result.current.data).toEqual({
address: '0x123',
portfolioBalanceUsd: 0,
tokenBalances: [],
});
});
});
41 changes: 41 additions & 0 deletions src/core-react/wallet/hooks/usePortfolioTokenBalances.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { getPortfolioTokenBalances } from '@/core/api/getPortfolioTokenBalances';
import type { PortfolioTokenBalances } from '@/core/api/types';
import { isApiError } from '@/core/utils/isApiResponseError';
import { type UseQueryResult, useQuery } from '@tanstack/react-query';
import type { Address } from 'viem';

export function usePortfolioTokenBalances({
address,
}: {
address: Address | undefined | null;
}): UseQueryResult<PortfolioTokenBalances> {
return useQuery({
queryKey: ['usePortfolioTokenBalances', address],
queryFn: async () => {
brendan-defi marked this conversation as resolved.
Show resolved Hide resolved
const response = await getPortfolioTokenBalances({
addresses: [address as Address], // Safe to coerce to Address because useQuery's enabled flag will prevent the query from running if address is undefined
});

if (isApiError(response)) {
throw new Error(response.message);
}

if (response.portfolios.length === 0) {
return {
address,
portfolioBalanceUsd: 0,
tokenBalances: [],
};
}

return response.portfolios[0];
},
retry: false,
enabled: !!address,
refetchOnWindowFocus: true, // refresh on window focus
staleTime: 1000 * 60 * 5, // refresh on mount every 5 minutes
refetchOnMount: true,
refetchInterval: 1000 * 60 * 15, // refresh in background every 15 minutes
refetchIntervalInBackground: true,
});
}
Loading
Loading