Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 0 deletions .claude/skills/freighter-best-practices
166 changes: 166 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# Freighter

> Non-custodial Stellar wallet browser extension for Chrome, Firefox, and
> Safari. Monorepo: extension, client SDK (`@stellar/freighter-api`), shared
> utilities, and Docusaurus docs site.

## Glossary

Domain terms you will encounter throughout this codebase:

| Term | Meaning |
| ------------------ | ---------------------------------------------------------------------------------- |
| **Popup** | Browser extension UI — React app at `extension/src/popup/`, runs in its own page |
| **Background** | Service worker at `extension/src/background/` — holds keys, signs, manages storage |
| **Content Script** | Injected into web pages; bridges dApp `window.postMessage` to the extension |
| **dApp** | Decentralized app that connects to Freighter via `@stellar/freighter-api` |
| **XDR** | Stellar binary serialization format used for transactions and ledger entries |
| **Soroban** | Stellar smart contract platform; transactions carry `SorobanData` |
| **Blockaid** | Third-party malicious-transaction scanning service integrated into sign flows |
| **Horizon** | Stellar REST API for querying ledger data and submitting transactions |
| **Mnemonic** | BIP-39 seed phrase used to derive HD wallet private keys |
| **Redux slice** | Feature-scoped reducer + actions created with `createSlice` (Redux Toolkit) |
| **i18n key** | Translation token; all user-facing text must go through `t()` from `react-i18next` |

## Documentation

- [User Guides](https://docs.freighter.app/docs/guide/introduction)
- [API Playgrounds](https://docs.freighter.app/docs/playground/getAddress)
- [Extension Dev Guide](./extension/README.md)
- [E2E Testing Guide](./extension/e2e-tests/README.md)
- [Style Guide](./extension/STYLEGUIDE.MD)
- [Localization](./extension/LOCALIZATION.MD)
- [Hardware Wallet Integration](./extension/INTEGRATING_HARDWARE_WALLET.MD)
- [Soroswap Integration](./extension/INTEGRATING_SOROSWAP.MD)
- [@stellar/freighter-api SDK](./@stellar/freighter-api/README.md)
- [Getting Started](./README.md)
- [Contributing](./CONTRIBUTING.MD)

## Quick Reference

| Item | Value |
| ----------------- | ------------------------------------------------------------ |
| Language | TypeScript, React |
| Node | >= 22 (`.nvmrc`) |
| Package Manager | Yarn 4.10.0 (workspaces) |
| State Management | Redux Toolkit |
| Testing | Jest (unit), Playwright (e2e) |
| Linting | ESLint flat config + Prettier |
| Default Branch | `master` |
| Branch Convention | `type/description` (`feature/`, `fix/`, `chore/`, `bugfix/`) |

## Build & Test Commands

```bash
yarn setup # Install + allow scripts
yarn start # Dev mode (all workspaces)
yarn start:extension # Extension only
yarn build:extension # Dev build
yarn build:extension:experimental # Experimental features enabled
yarn build:extension:production # Minified production build
yarn build:extension:translations # Auto-generate translation keys
yarn test # Jest watch mode
yarn test:ci # Jest CI mode (use before pushing)
yarn test:e2e # Playwright e2e
```

## Environment Setup

Create `extension/.env`:

```env
INDEXER_URL=https://freighter-backend-prd.stellar.org/api/v1
INDEXER_V2_URL=https://freighter-backend-v2.stellar.org/api/v1
```

## Repository Structure

```
freighter/
├── extension/
│ ├── src/popup/ # React UI
│ ├── src/background/ # Service worker (keys, signing, storage)
│ ├── src/contentScript/ # dApp ↔ extension bridge
│ ├── e2e-tests/ # Playwright tests
│ └── public/ # Static assets, manifest
├── @stellar/freighter-api/ # npm SDK for dApp integration
├── @shared/ # Shared api, constants, helpers
├── docs/ # Docusaurus site
├── config/ # Shared Jest/Babel/Webpack configs
└── .github/
├── workflows/ # 10 CI/CD workflows
└── agents/ # Playwright test AI agents (generator, healer, planner)
```

## Architecture

Three runtime contexts communicate exclusively via message passing:

1. **Popup** — React UI, dispatches to background via
`chrome.runtime.sendMessage`
2. **Background** — service worker, processes messages, never directly touches
the DOM
3. **Content Script** — injected per tab, relays between `window.postMessage`
and background

Dev server URLs:

| URL | Purpose |
| ----------------------------------- | ----------------------------------------- |
| `localhost:9000` | Extension popup (hot reload) |
| `localhost:9000/#/debug` | Blockaid debug panel (dev only) |
| `localhost:9000/#/integration-test` | Integration test helper (clears app data) |

Background and content script changes require `yarn build:extension` + extension
reload. Popup changes hot reload automatically.

## Security-Sensitive Areas

Do not modify these without fully understanding the security implications:

- `extension/src/background/` — private keys, signing, encrypted storage
- `extension/src/contentScript/` — postMessage attack surface from dApps
- `extension/public/static/manifest/` — extension permissions and CSP
- `@shared/constants/` — network endpoints, key derivation parameters
- Any code touching `chrome.storage` or key material

## Known Complexity / Gotchas

- **Message passing** uses typed enums. Follow existing patterns — do not add
raw string messages.
- **Build variants** (standard / experimental / production) have different
feature flags; test the right one.
- **Translations** require running `yarn build:extension:translations` after
adding any `t()` call; the pre-commit hook handles this automatically but
re-run if the hook is skipped.
- **Soroswap** swap logic lives in `popup/helpers/sorobanSwap.ts` — three
chained async calls; debug with logs before refactoring.
- **Store submissions** use separate CI workflows per platform (Chrome, Firefox,
Safari, npm).
- **AI test agents** live in `.github/agents/` — don't delete or rename without
updating the workflows.

## Pre-submission Checklist

```bash
yarn test:ci # All unit tests must pass
yarn build:extension # Build must succeed (catches type errors)
```

## Best Practices Entry Points

Read the relevant file when working in that area:

| Concern | Entry Point | When to Read |
| -------------------- | ------------------------------------------------------------------- | -------------------------------------------------------- |
| Code Style | `docs/skills/freighter-best-practices/references/code-style.md` | Writing or reviewing any code |
| Architecture | `docs/skills/freighter-best-practices/references/architecture.md` | Adding features, understanding context/message flow |
| Security | `docs/skills/freighter-best-practices/references/security.md` | Touching keys, messages, storage, or dApp interactions |
| Testing | `docs/skills/freighter-best-practices/references/testing.md` | Writing or fixing tests |
| Performance | `docs/skills/freighter-best-practices/references/performance.md` | Optimizing renders, bundle size, or load times |
| Error Handling | `docs/skills/freighter-best-practices/references/error-handling.md` | Adding error states, catch blocks, or user-facing errors |
| Internationalization | `docs/skills/freighter-best-practices/references/i18n.md` | Adding or modifying user-facing strings |
| Messaging | `docs/skills/freighter-best-practices/references/messaging.md` | Adding popup/background/content script communication |
| Git & PR Workflow | `docs/skills/freighter-best-practices/references/git-workflow.md` | Branching, committing, opening PRs, CI |
| Dependencies | `docs/skills/freighter-best-practices/references/dependencies.md` | Adding, updating, or auditing packages |
| Anti-Patterns | `docs/skills/freighter-best-practices/references/anti-patterns.md` | Code review, avoiding common mistakes |
41 changes: 41 additions & 0 deletions docs/skills/freighter-best-practices/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
name: freighter-best-practices
description:
Comprehensive best practices for the Freighter browser extension. Covers code
style, architecture, security, testing, performance, error handling, i18n,
messaging, git workflow, dependencies, and anti-patterns. Use when writing new
features, reviewing PRs, adding message handlers, working with Redux,
modifying content scripts, handling keys, building transactions, or any
development work in the freighter extension repo. Triggers on any task
touching the freighter codebase.
---

# Freighter Best Practices

Freighter is a non-custodial Stellar wallet shipped as a browser extension
(Manifest V3) for Chrome, Firefox, and Safari. The repository is a Yarn
workspaces monorepo with four main workspaces:

1. **extension/** -- the browser extension itself (popup, background service
worker, content scripts)
2. **@stellar/freighter-api/** -- the npm SDK that dApps use to interact with
Freighter
3. **@shared/** -- shared packages (api, constants, helpers) consumed by
multiple workspaces
4. **docs/** -- documentation and skills

## Reference Guide

| Concern | File | When to Read |
| -------------------- | ---------------------------- | -------------------------------------------------------- |
| Code Style | references/code-style.md | Writing or reviewing any code |
| Architecture | references/architecture.md | Adding features, understanding the codebase |
| Security | references/security.md | Touching keys, messages, storage, or dApp interactions |
| Testing | references/testing.md | Writing or fixing tests |
| Performance | references/performance.md | Optimizing renders, bundle size, or load times |
| Error Handling | references/error-handling.md | Adding error states, catch blocks, or user-facing errors |
| Internationalization | references/i18n.md | Adding or modifying user-facing strings |
| Messaging | references/messaging.md | Adding popup<>background<>content script communication |
| Git & PR Workflow | references/git-workflow.md | Branching, committing, opening PRs, CI |
| Dependencies | references/dependencies.md | Adding, updating, or auditing packages |
| Anti-Patterns | references/anti-patterns.md | Code review, avoiding common mistakes |
164 changes: 164 additions & 0 deletions docs/skills/freighter-best-practices/references/anti-patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
# Anti-Patterns -- Freighter Extension

Common mistakes to watch for during code review and development.

## 1. Suppressing exhaustive-deps

```typescript
// eslint-disable-next-line react-hooks/exhaustive-deps
```

Found ~50 times in the codebase. This indicates tight coupling between hooks and
external state, or an intentional override of the dependency array.

**For new code:** fix the dependency array instead of suppressing the warning.
Refactor the hook to accept stable references, or use `useCallback`/`useMemo` to
stabilize dependencies.

**If truly needed:** add a comment explaining WHY the suppression is necessary.
A bare `eslint-disable` without explanation is not acceptable.

## 2. Non-null Assertions on Optional Data

```typescript
// WRONG: crashes silently if icons[asset] is undefined
const icon = icons[asset]!;
```

Always handle the missing case explicitly:

```typescript
// CORRECT
const icon = icons[asset];
if (!icon) {
return <DefaultIcon />;
}
```

## 3. Type Assertion Workarounds

```typescript
// WRONG: forced cast indicates incomplete types
const payload = action?.payload as typeof action.payload & { extra?: string };
```

This pattern works around incomplete type definitions. Fix the types at the
source instead of casting at the usage site. Update the action creator's return
type, the thunk's generic parameters, or the slice's state type.

## 4. Module-Level State in Background

```typescript
// WRONG: this variable will be lost when the MV3 worker restarts
let cachedData = {};

export const handler = () => {
cachedData.foo = "bar"; // lost on next restart
};
```

MV3 service workers are ephemeral. Chrome can terminate and restart them at any
time. Any variable declared outside a function will be reset.

**Use `chrome.storage.session`** for ephemeral data or
**`chrome.storage.local`** for persistent data.

## 5. Direct chrome.runtime.sendMessage from Popup

```typescript
// WRONG: bypasses the shared API layer
chrome.runtime.sendMessage({ type: SERVICE_TYPES.GET_DATA });
```

Always use the `sendMessageToBackground()` wrapper from `@shared/api/internal`:

```typescript
// CORRECT
import { sendMessageToBackground } from "@shared/api/internal";
await sendMessageToBackground({ type: SERVICE_TYPES.GET_DATA });
```

The wrapper provides consistent typing, response parsing, and error handling.

## 6. Hardcoded URL Paths in Navigation

```typescript
// WRONG
navigate("/account/send");
```

Use the `ROUTES` enum and `navigateTo()` helper:

```typescript
// CORRECT
navigateTo(ROUTES.sendPayment);
```

## 7. Inline Object/Function Creation in JSX Props

```typescript
// WRONG: new object/function on every render, defeats memo()
<AssetList
filter={{ type: "native" }}
onSelect={(asset) => handleSelect(asset)}
/>
```

Extract to `useMemo` and `useCallback`:

```typescript
// CORRECT
const filter = useMemo(() => ({ type: "native" }), []);
const onSelect = useCallback((asset) => handleSelect(asset), [handleSelect]);
<AssetList filter={filter} onSelect={onSelect} />
```

## 8. Non-Serializable Objects in Redux Store

```typescript
// WRONG: Stellar SDK objects contain methods and circular references
state.transaction = new Transaction(xdr);
```

Store serializable representations only:

```typescript
// CORRECT: store the XDR string, reconstruct the object when needed
state.transactionXdr = transaction.toXDR();
```

## 9. Silent Error Swallowing

```typescript
// WRONG: error is completely lost
try {
await riskyOperation();
} catch (e) {
// do nothing
}
```

At minimum, report to Sentry:

```typescript
// CORRECT
try {
await riskyOperation();
} catch (error) {
captureException(error);
}
```

## 10. TODO Comments Without Tracking

```typescript
// TODO: refactor this later
```

If leaving a TODO, create a GitHub issue and reference it:

```typescript
// TODO(#1234): refactor balance calculation to use BigNumber
```

This ensures TODOs are tracked and not forgotten.
Loading
Loading