Skip to content

Commit 2fd1de3

Browse files
authored
feat: CP-11868 Next Gen Assets tab (#442)
1 parent d1c62dd commit 2fd1de3

File tree

20 files changed

+735
-85
lines changed

20 files changed

+735
-85
lines changed

CLAUDE.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
77
Core Extension is a non-custodial browser extension for Chromium browsers built using Manifest V3. It enables users to seamlessly and securely interact with Web3 applications powered by Avalanche, supporting Bitcoin, Ethereum, and Solana networks.
88

99
The project uses a monorepo structure with yarn workspaces and contains two main applications:
10+
1011
- **Legacy**: The current production version (React 17, older architecture) in `apps/legacy/`
1112
- **Next**: The next-generation version (React 19, modern architecture) in `apps/next/`
1213

1314
## Essential Commands
1415

1516
### Development
17+
1618
```bash
1719
# Start development (defaults to legacy)
1820
yarn dev
@@ -27,6 +29,7 @@ yarn setup
2729
```
2830

2931
### Building
32+
3033
```bash
3134
# Build for production
3235
yarn build # Legacy (default)
@@ -44,6 +47,7 @@ yarn zip:next # Next-gen zip for Chrome Store
4447
```
4548

4649
### Testing & Quality
50+
4751
```bash
4852
# Run tests
4953
yarn test # Legacy tests
@@ -64,13 +68,16 @@ yarn scanner:next # Next-gen i18n scan
6468
```
6569

6670
### Build System
71+
6772
Built with Rsbuild (Rspack-based) instead of Webpack. Each package has its own build configuration:
73+
6874
- Apps use environment-specific configs (`rsbuild.{app}.{env}.ts`)
6975
- Packages have shared common configs with dev/prod variants
7076

7177
## Architecture
7278

7379
### Extension Structure (Manifest V3)
80+
7481
The extension follows the standard Manifest V3 architecture with 4 isolated components:
7582

7683
1. **Service Worker** (`packages/service-worker/`): Background script handling business logic, network communication, transaction signing, and encrypted storage
@@ -79,6 +86,7 @@ The extension follows the standard Manifest V3 architecture with 4 isolated comp
7986
4. **Injected Provider** (`packages/inpage/`): EIP-1193 compliant provider injected into web pages for dApp communication
8087

8188
### Monorepo Packages
89+
8290
- `@core/common`: Shared utilities, constants, and helper functions
8391
- `@core/messaging`: Cross-context communication system with JSON serialization
8492
- `@core/service-worker`: Background script services and business logic
@@ -89,9 +97,11 @@ The extension follows the standard Manifest V3 architecture with 4 isolated comp
8997
- `@core/offscreen`: Offscreen document for secure DOM operations
9098

9199
### Service Architecture
100+
92101
The service worker uses dependency injection (TSyringe) with service-oriented architecture:
93102

94103
**Core Services:**
104+
95105
- `AccountsService`: Multi-wallet account management
96106
- `BalancesService`: Token balances and portfolio aggregation
97107
- `NetworkService`: Blockchain network configuration and switching
@@ -104,74 +114,87 @@ The service worker uses dependency injection (TSyringe) with service-oriented ar
104114
- `StorageService`: Encrypted data persistence
105115

106116
**Architecture Patterns:**
117+
107118
- **Handlers**: RPC method processors combining service functionality
108119
- **Events**: Pub/sub system for cross-component communication
109120
- **Middleware**: Request processing pipeline (auth, logging, permissions)
110121

111122
### Extension Entry Points
123+
112124
The frontend supports multiple contexts determined by `isSpecificContextContainer.ts`:
125+
113126
- **POPUP**: Main extension UI (browser icon click)
114127
- **CONFIRM**: Approval windows for dApp interactions
115128
- **HOME**: Fullscreen onboarding experience
116129

117130
## Development Guidelines
118131

119132
### Critical Manifest V3 Constraints
133+
120134
- **No XMLHttpRequest**: Use `fetch()` only
121135
- **No DOM/window**: Use offscreen documents for DOM operations
122136
- **Service Worker Restarts**: Background script can restart anytime - never rely on in-memory state
123137
- **Strict CSP**: No eval(), limited inline scripts
124138
- **Storage**: Always use encrypted `StorageService`, never direct chrome.storage
125139

126140
### Security Requirements
141+
127142
- **Encrypt Everything**: All storage must be encrypted via `StorageService`
128143
- **No Plain Text Secrets**: Never expose private keys outside respective services
129144
- **Password Fields**: Use password inputs for sensitive data (Chrome caches regular inputs)
130145
- **No Frontend Storage**: Never use localStorage - all state in encrypted background storage
131146

132147
### Code Organization
148+
133149
- **Single Purpose Services**: Keep services focused, combine functionality in handlers
134150
- **Clean State**: Reset all state when wallet locks
135151
- **Event-Driven Communication**: Services communicate via events, not direct calls
136152
- **Handler-Service Pattern**: Handlers orchestrate multiple services for complex operations
137153

138154
### Multi-App Development
155+
139156
- Legacy and next-gen apps share the same service worker and core packages
140157
- Test changes in both applications when modifying shared functionality
141158
- Service worker changes affect both apps simultaneously
142159
- Use appropriate commands: `yarn dev:legacy` vs `yarn dev:next`
143160

144161
### Component Library
162+
145163
- Uses [K2 Components](https://k2-components.pages.dev/) for UI consistency
146164
- Avoid overriding MUI classes - update K2 or Figma instead
147165
- Ask in `k2-product-design-system` Slack for design system questions
148166

149167
### Environment Setup
168+
150169
- Copy `.env.example` to `.env.dev` for development
151170
- Requires Node.js 20.18.0 and Yarn 4.7.0 (managed by Volta)
152171
- Requires access to `@avalabs` packages on npmjs.com with 2FA
153172

154173
### Chrome Extension Development
174+
155175
1. Build: `yarn dev` (legacy) or `yarn dev:next` (next-gen)
156176
2. Chrome: Go to `chrome://extensions/`, enable Developer mode
157177
3. Load: Click "Load unpacked", select `dist/` (legacy) or `dist-next/` (next-gen) folder
158178
4. Hot reload: Changes reload automatically during development
159179
5. Service Worker: Requires extension reload for background script changes
160180

161181
### Blockchain Network Support
182+
162183
- **Avalanche**: C-Chain, P-Chain, X-Chain (primary focus)
163184
- **Bitcoin**: Native Bitcoin and testnet support
164185
- **Ethereum**: EVM-compatible chains
165186
- **Solana**: Next-gen app only
166187

167188
### Testing Strategy
189+
168190
- Unit tests co-located with source (`.test.ts` files)
169191
- Integration tests for service interactions
170192
- Mocks in `__mocks__/` directories
171193
- Jest testing framework across all packages
172194
- Test both legacy and next-gen when changing shared code
173195

174196
### Release Process
197+
175198
- Uses semantic versioning with conventional commits
176199
- `feat:` = minor version bump
177200
- `fix:` = patch version bump
@@ -180,6 +203,7 @@ The frontend supports multiple contexts determined by `isSpecificContextContaine
180203
- Production: Manual GitHub Action trigger
181204

182205
### Common Gotchas
206+
183207
- Service worker 5-minute idle timeout and random restarts
184208
- EIP-1193 provider race conditions with other wallets (MetaMask, Rabby)
185209
- WebUSB limitations in service worker (requires frontend for Ledger)
@@ -188,9 +212,10 @@ The frontend supports multiple contexts determined by `isSpecificContextContaine
188212
- Content Security Policy restrictions in Manifest V3
189213

190214
### Performance Considerations
215+
191216
- Balance polling service manages network requests efficiently
192217
- Use incremental promise resolution for batch operations
193218
- Implement proper cleanup in service constructors
194219
- Avoid memory leaks in long-running background processes
195220

196-
Always run `yarn lint` and `yarn typecheck` before committing changes.
221+
Always run `yarn lint` and `yarn typecheck` before committing changes.

apps/next/src/hooks/useAllTokensFromEnabledNetworks.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ import { useNetworkContext } from '@core/ui';
22
import { useAllTokens } from './useAllTokens';
33

44
// TODO: Currently the hook is using favoriteNetwork. It should be changed to enabledNetworks once added.
5-
export const useAllTokensFromEnabledNetworks = () => {
6-
const { favoriteNetworks } = useNetworkContext();
5+
export const useAllTokensFromEnabledNetworks = (
6+
onlyTokensWithBalances?: boolean,
7+
hideMalicious?: boolean,
8+
) => {
9+
const { enabledNetworks } = useNetworkContext();
10+
const tokens = useAllTokens(enabledNetworks, hideMalicious);
711

8-
return useAllTokens(favoriteNetworks, false);
12+
return !onlyTokensWithBalances
13+
? tokens
14+
: tokens.filter((token) => token.balance);
915
};

apps/next/src/hooks/useTokensForAccount.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ const isNativeToken = (
140140
): token is FungibleTokenBalance & { type: TokenType.NATIVE } =>
141141
token.type === TokenType.NATIVE;
142142

143-
const isAvaxToken = (
143+
export const isAvaxToken = (
144144
token: FungibleTokenBalance,
145145
): token is FungibleTokenBalance & { type: TokenType.NATIVE } =>
146146
token.type === TokenType.NATIVE && token.symbol === 'AVAX';

apps/next/src/index.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,400..700;1,14..32,400..700&display=swap"
1616
/>
1717
<style>
18-
* {
19-
scrollbar-width: thin;
20-
scrollbar-color: #949497 rgba(0, 0, 0, 0.05);
21-
}
18+
* {
19+
scrollbar-width: thin;
20+
scrollbar-color: #949497 rgba(0, 0, 0, 0.05);
21+
}
2222
html {
2323
height: 100%;
2424
}

apps/next/src/localization/locales/en/translation.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"Add using Ledger": "Add using Ledger",
5757
"Address copied!": "Address copied!",
5858
"All": "All",
59+
"All Networks": "All Networks",
5960
"All keys contained in this file are already imported.": "All keys contained in this file are already imported.",
6061
"All networks": "All networks",
6162
"All types": "All types",
@@ -103,6 +104,7 @@
103104
"Average password - this will do": "Average password - this will do",
104105
"Awesome!": "Awesome!",
105106
"Back": "Back",
107+
"Balance": "Balance",
106108
"Balance change unavailable.": "Balance change unavailable.",
107109
"Balances loading...": "Balances loading...",
108110
"Best price available": "Best price available",
@@ -380,6 +382,7 @@
380382
"Ledger requires you to set up a wallet policy in the Bitcoin app. Please approve or reject this action on your Ledger device.": "Ledger requires you to set up a wallet policy in the Bitcoin app. Please approve or reject this action on your Ledger device.",
381383
"Ledger {{number}}": "Ledger {{number}}",
382384
"Lending": "Lending",
385+
"Let's go": "Let's go",
383386
"Let's go!": "Let's go!",
384387
"Light": "Light",
385388
"Limit exceeded": "Limit exceeded",
@@ -442,6 +445,7 @@
442445
"No account is active": "No account is active",
443446
"No active network": "No active network",
444447
"No address": "No address",
448+
"No assets yet": "No assets yet",
445449
"No collectibles": "No collectibles",
446450
"No connected sites": "No connected sites",
447451
"No custom headers are configured.": "No custom headers are configured.",
@@ -455,10 +459,12 @@
455459
"Normal": "Normal",
456460
"Not connected": "Not connected",
457461
"Notify me when it’s done": "Notify me when it’s done",
462+
"On-ramp using Core in two minutes": "On-ramp using Core in two minutes",
458463
"Only Keystore files from the Avalanche Wallet are supported": "Only Keystore files from the Avalanche Wallet are supported",
459464
"Only keystore files exported from the Avalanche Wallet are supported.": "Only keystore files exported from the Avalanche Wallet are supported.",
460465
"Oops! \n Something went wrong": "Oops! \n Something went wrong",
461466
"Oops! It seems like you have no internet connection. Please try again later.": "Oops! It seems like you have no internet connection. Please try again later.",
467+
"Oops! Something went wrong": "Oops! Something went wrong",
462468
"Open any authenticator app and scan the QR code below or enter the code manually": "Open any authenticator app and scan the QR code below or enter the code manually",
463469
"Open the Avalanche app on your Keystone device in order to continue with this transaction": "Open the Avalanche app on your Keystone device in order to continue with this transaction",
464470
"Open the Solana app on your Ledger device": "Open the Solana app on your Ledger device",
@@ -531,6 +537,7 @@
531537
"Protocol": "Protocol",
532538
"Provider": "Provider",
533539
"Public key not found": "Public key not found",
540+
"Quantity": "Quantity",
534541
"Quote includes an {{coreFee}} Core fee": "Quote includes an {{coreFee}} Core fee",
535542
"RPC URL reset successfully": "RPC URL reset successfully",
536543
"Rate": "Rate",
@@ -696,6 +703,7 @@
696703
"To use Solana in Core you will need to add an account from your Ledger device. You can always add this later at any time": "To use Solana in Core you will need to add an account from your Ledger device. You can always add this later at any time",
697704
"Token": "Token",
698705
"Token Added": "Token Added",
706+
"Token Price": "Token Price",
699707
"Token already exists in the wallet.": "Token already exists in the wallet.",
700708
"Token contract address": "Token contract address",
701709
"Token name": "Token name",

apps/next/src/pages/Portfolio/components/ManageTokens/components/TokenSwitchList.tsx

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { useAllTokensFromEnabledNetworks } from '@/hooks/useAllTokensFromEnabledNetworks';
22
import { Box } from '@avalabs/k2-alpine';
3-
import { TokenType } from '@avalabs/vm-module-types';
4-
import { isTokenMalicious } from '@core/common';
53
import { FungibleTokenBalance } from '@core/types';
64
import { FC, useMemo } from 'react';
75
import { FixedSizeList, ListChildComponentProps } from 'react-window';
@@ -16,41 +14,29 @@ interface Props {
1614

1715
export const TokenSwitchList: FC<Props> = ({ filter, spam }) => {
1816
const [height, containerRef] = useContainerHeight<HTMLDivElement>(400);
19-
const tokensWithBalances = useAllTokensFromEnabledNetworks();
17+
const tokensWithBalances = useAllTokensFromEnabledNetworks(false, !spam);
2018

21-
const nonNative = useMemo(() => {
22-
return tokensWithBalances.filter(
23-
(token) => token.type !== TokenType.NATIVE,
24-
);
25-
}, [tokensWithBalances]);
26-
27-
const spamless = useMemo(
28-
() =>
29-
spam ? nonNative : nonNative.filter((token) => !isTokenMalicious(token)),
30-
[spam, nonNative],
31-
);
32-
33-
const filtered = useMemo(
19+
const filteredTokensList = useMemo(
3420
() =>
3521
filter
36-
? spamless.filter((token) => {
22+
? tokensWithBalances.filter((token) => {
3723
const normalizedFilter = filter.toLowerCase();
3824
return (
3925
token.name.toLowerCase().includes(normalizedFilter) ||
4026
token.symbol.toLowerCase().includes(normalizedFilter)
4127
);
4228
})
43-
: spamless,
44-
[filter, spamless],
29+
: tokensWithBalances,
30+
[filter, tokensWithBalances],
4531
);
4632

4733
return (
4834
<Box height={1} ref={containerRef}>
4935
<FixedSizeList
5036
height={height}
5137
width="100%"
52-
itemData={filtered}
53-
itemCount={filtered.length}
38+
itemData={filteredTokensList}
39+
itemCount={filteredTokensList.length}
5440
itemSize={54}
5541
overscanCount={5}
5642
style={{ overflow: 'auto', scrollbarWidth: 'none' }}

apps/next/src/pages/Portfolio/components/PortfolioHome/PortfolioHome.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ export const PortfolioHome: FC = () => {
127127
const TabsContainer = styled(Stack)(({ theme }) => ({
128128
position: 'sticky',
129129
bottom: 0,
130-
zIndex: 100,
131130
paddingTop: theme.spacing(1),
132131
background: `linear-gradient(180deg, ${getHexAlpha(theme.palette.background.default, 0)} 0%, ${theme.palette.background.default} 16px)`,
133-
132+
zIndex: theme.zIndex.appBar,
134133
'> div': {
135134
background: 'unset',
136135
},

0 commit comments

Comments
 (0)