diff --git a/CHANGELOG.md b/CHANGELOG.md index d517c8131..2d608af3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,35 @@ and this project adheres to ## [Unreleased] +## [2.3.6] - 2026-08-12 + +### Fixed + +- Fixed pasting a Solana ANT address being silently ignored when bringing your + own ANT. A 32-byte Solana pubkey base58-encodes to 43 or 44 characters, but + these inputs still enforced Arweave's fixed 43-character transaction ID + length, so most addresses were rejected. `ValidationInput` discards input + that exceeds `maxCharLength` or fails `customPattern` without surfacing an + error, so the field simply appeared to ignore the paste. + - Name reassignment ("Use existing ANT"): the destination ANT address field + capped entry at 43 characters via `ARNS_TX_ID_ENTRY_REGEX`. + - Name registration (Advanced Options): the ANT selector capped entry at 43 + characters, validated input as an Arweave transaction ID, and gated its + "Import" button on `isArweaveTransactionID` — so bringing your own ANT + could not be completed at all. +- Both inputs now validate against the base58 Solana address format and accept + the full 32–44 character range. +- Name reassignment no longer enables confirmation for a well-formed address + that has no ANT record. The destination lookup settles to "not loading, no + data" in that case, which previously let an irreversible reassignment + proceed toward a nonexistent destination. + +### Changed + +- Added `SOLANA_ADDRESS_ENTRY_REGEX` and `SOLANA_ADDRESS_MAX_LENGTH` for inputs + that accept Solana addresses. Arweave data pointers (target IDs, undername + records, logos) keep the existing 43-character constants. + ## [2.3.5] - 2026-08-11 ### Changed diff --git a/CLAUDE.md b/CLAUDE.md index 06e230b17..34aedb0ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,382 +1,288 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with -code in this repository. +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview -This is a React application for the Ar.io Name System (ArNS) Registry, -allowing users to search for and purchase Names. Built with Vite, React 18, -TypeScript, and TailwindCSS. +React app for the Ar.io Name System (ArNS) Registry — search for, purchase, and +manage ArNS names. Vite + React 18 + TypeScript + TailwindCSS. -## Development Commands - -### Setup and Running - -```bash -yarn # Install dependencies -yarn dev # Start development server (sets NODE_ENV=prod, VITE_GITHUB_HASH=local) -yarn build # Build for production (Vite build with increased memory) -yarn build:production # Build with VITE_ENVIRONMENT=production -yarn build:develop # Build with VITE_ENVIRONMENT=develop -yarn preview # Preview production build -``` - -### Testing - -```bash -yarn test # Run Jest unit tests -yarn test:updateSnapshot # Update Jest snapshots -yarn test:coverage # Run tests with coverage report (80% threshold for branches/functions/lines) -yarn test:playwright # Run Playwright e2e tests -``` +**The backend is Solana-only.** The codebase went through a "de-AO refactor": +ArNS records and ANTs live in Solana programs (ANTs are Metaplex Core NFTs), not +in AO processes. Comments referencing "the de-AO refactor" mark code touched by +that migration. Arweave is still used for data storage/retrieval (Turbo uploads, +logo images, GraphQL), but not for contract state. -To run a single test file: +## Development Commands ```bash -# Cross-platform (recommended) -yarn cross-env NODE_ENV=test jest path/to/test.test.ts - -# Or use npx -npx cross-env NODE_ENV=test jest path/to/test.test.ts +yarn # Install +yarn dev # Dev server (NODE_ENV=prod, VITE_GITHUB_HASH=local) +yarn build # Production build (32GB max-old-space-size) +yarn build:production # VITE_ENVIRONMENT=production +yarn build:develop # VITE_ENVIRONMENT=develop +yarn preview # Preview built output + +yarn test # Jest unit tests +yarn test:coverage # With coverage (80% threshold: branches/functions/lines) +yarn test:updateSnapshot # Update snapshots +yarn test:playwright # Playwright e2e + +yarn lint:check / lint:fix # Biome (lint:fix uses --unsafe) +yarn format:check / format:fix # Biome formatter + +yarn storybook # Storybook on :6006 +yarn publish:arweave # Build + `ario-deploy deploy --arns-name $VITE_ARNS_NAME` ``` -### Code Quality +Run a single test file: ```bash -yarn lint:check # Check for linting errors with Biome -yarn lint:fix # Auto-fix linting errors with Biome -yarn format:check # Check code formatting with Biome -yarn format:fix # Auto-format code with Biome -yarn pre-commit # Run lint-staged (triggered by Husky pre-commit hook) +npx cross-env NODE_ENV=test jest src/utils/searchUtils/searchUtils.test.ts ``` -### Other Commands +`NODE_ENV=test` is required — Jest uses `tsconfig.test.json` and a custom +`import.meta` AST transformer (`tests/common/import-meta-transformer.js`) to make +Vite's `import.meta.env` work under CommonJS. -```bash -yarn storybook # Start Storybook dev server on port 6006 -yarn build-storybook # Build Storybook -yarn docs:serve # Generate and serve TypeDoc documentation -yarn clean # Remove dist directory -``` +Biome is the source of truth for lint/format. `.eslintrc` and `.prettierrc` are +vestigial — don't wire new tooling to them. ## Architecture -### State Management - -The application uses React Context API with reducer pattern for state -management. There are multiple domain-specific contexts, each with their own -reducer: - -- **GlobalState** (`src/state/contexts/GlobalState.tsx`): Gateway configuration, - AO network settings, Turbo network configuration, ArIO contract, blockchain - height, process IDs -- **WalletState**: Wallet connection, balance, address -- **ArNSState**: ArNS records and domain-related state -- **TransactionState**: Transaction tracking and history -- **RegistrationState**: Domain registration flow state -- **ModalState**: Modal visibility and content - -All contexts are initialized in `src/main.tsx` with a nested provider structure. -State is persisted to localStorage for settings via the `useSyncSettings` hook. - -### Data Layer - -**Arweave Data Providers** (`src/services/arweave/`): - -- `ArweaveCompositeDataProvider`: Main data provider that aggregates multiple - sources -- `SimpleArweaveDataProvider`: Basic Arweave data fetching - -**React Query** (`@tanstack/react-query`): - -- Used extensively for server state management via custom hooks in `src/hooks/` -- Configured in `src/utils/network.ts` with `queryClient` -- IndexedDB persister available but currently commented out in main.tsx - -**Custom Hooks** (`src/hooks/`): - -- Follow the naming pattern `use.tsx` -- Examples: `useArNSRecord`, `useGateways`, `useTurboArNSClient`, - `usePrimaryName` -- Encapsulate data fetching, transformations, and domain logic - -### AO Integration - -The app integrates with the AO (Arweave Operating System) ecosystem: - -- **AR.IO SDK** (`@ar.io/sdk/web`): Primary interface for ArNS operations, - gateways, and ArIO contracts -- **AO Connect** (`@permaweb/aoconnect`): AO process communication -- Process IDs configured for ARIO and ANT Registry -- ANT (Ar.io Name Token) operations handled via `AoANTHandler` +### Solana backend and SDK construction + +`src/utils/solana.ts` owns the active Solana config (network, RPC URL, program +IDs, ARIO mint). Key points: + +- Config is **runtime-switchable** via `setSolanaConfig()` — the Settings → + Network page exposes `devnet` / `mainnet-beta` presets plus per-program-ID + overrides. Switching invalidates the memoized RPC clients, so always read + through `getActiveSolanaConfig()` / `getSolanaRpc()` / + `getSolanaRpcSubscriptions()` rather than caching module-level constants. +- The exported `SOLANA_NETWORK` / `SOLANA_RPC_URL` / `SOLANA_PROGRAM_IDS` + constants are the *initial* env-derived values only. Use them for bootstrap, + not for live reads. + +`src/utils/sdk-init.ts` is the chokepoint for building `@ar.io/sdk` clients: +`buildArio`, `buildArioRead`, `buildAnt`, `buildAntRead`, plus the +`isSolanaWallet()` type guard. Read paths work without a wallet (read-only +client against the configured RPC), so unauthenticated browsing keeps working. +Add new SDK instantiation here rather than calling `ARIO.init` / `ANT.init` +inline. + +`isSolanaWallet()` checks *both* `tokenType === 'solana'` and a present +`solanaSigner` — Phantom attaches `signTransaction` a tick after `connected` +flips, so write helpers must fall back to read-only during that window. + +### ANT ACL drift (`src/utils/aclSync.ts`) + +The on-chain ANT ACL (paginated `AclConfig` / `AclPage` PDAs) is an +*eventually-consistent* index of which ANTs a wallet owns. Raw Metaplex Core +transfers move the asset immediately but do not update the ACL, so it lags in +both directions — a received name is invisible until synced, a sent name lingers. +`computeAclDrift()` establishes ground truth via a `getProgramAccounts` owner +scan against `MPL_CORE_PROGRAM_ID`, filtered by the `ANT Program` Metaplex +attribute. If names appear missing or stale in Manage, this is the first place to +look. + +### Wallets — Solana only + +`WALLET_TYPES` has a single member: `SOLANA`. Wallet discovery goes through +`@solana/wallet-adapter-react` using the **Wallet Standard registry** — +`main.tsx` passes `wallets={[]}` on purpose; Phantom, Solflare, Backpack, Glow +etc. self-register. Do not add legacy per-wallet adapter packages. + +`autoConnect` must stay `true`: the wallet-adapter UI picker only calls +`select(name)` on click, never `adapter.connect()`. With `autoConnect=false` +clicking a wallet silently does nothing. + +`SolanaWalletConnector` (`src/services/wallets/`) is a thin shim implementing the +app's `ArNSWalletConnector` interface over the adapter. Its important output is +`solanaSigner` — a `@solana/kit` `TransactionSigner` built by +`walletAdapterToKitSigner.ts` — which is what gets handed to the SDK. +`PrivateKeySolanaWalletConnector` backs the devtools-only private-key login at +`/settings/devtools`. + +`ArNSWalletConnector` still carries `contractSigner` / `turboSigner` fields from +the multi-chain era; the Solana connector leaves them `undefined`. + +### Dead EVM code + +wagmi is still a dependency and `src/utils/baseNetwork.ts`, +`BaseTokenPurchaseService`, and Base-token branches in Checkout still reference +it — but **`WagmiProvider` was removed from the app shell**. Calling a wagmi hook +crashes with `WagmiProviderNotFoundError`. Affected files stub the hooks out with +a `NOTE (de-AO refactor)` comment; the resulting `undefined` values flow into +EVM-funded branches that are unreachable from the Solana-only UI. Do not +"restore" these imports without re-adding the provider. + +### Critical polyfills in `src/main.tsx` + +Three shims run **before** anything else and must not be reordered or removed: + +1. `BigInt.prototype.toJSON` — `@ar.io/sdk`'s Solana backend calls + `JSON.stringify` on simulation errors containing BigInts. Without this the + diagnostic stringify throws and the real on-chain failure is swallowed. +2. `Buffer.read/writeBigUInt64LE` — the ESM `buffer@6.0.3` path strips these; + without them `getBalance` throws and checkout silently shows "0 ARIO". +3. Ed25519 WebCrypto polyfill — feature-detected, then `@solana/webcrypto-ed25519-polyfill` + is installed for Chrome <137 / Firefox. Required by `generateKeyPairSigner` + during ANT spawn. React only mounts inside `ed25519PolyfillReady.finally()`. + +### State management + +React Context + reducer per domain, all nested in `main.tsx` (order matters — +`WalletState` reads from `GlobalState`): + +`QueryClientProvider` → `SolanaWalletShell` → `GlobalState` → `WalletState` → +`ArNSState` → `TransactionState` → `RegistrationState` → antd `ConfigProvider` → +`ModalState` → `App` + +- **GlobalState**: gateways, Turbo network, `solanaConfig`, ARIO contract + instance. Persists to `localStorage` under `arns-app-settings` (see + `useSyncSettings`). +- **WalletState**: bridges `useWallet()` from wallet-adapter into + `SolanaWalletConnector`, rebuilds the ARIO contract when the signer or + `solanaConfig` changes, persists `walletType`. + +Write flows go through `src/state/actions/` — `dispatchANTInteraction`, +`dispatchArIOInteraction`, `dispatchArNSUpdate`, `dispatchArIOContract`. These +require a connected Solana wallet with a signer and throw otherwise. Interaction +names are the `ANT_INTERACTION_TYPES` / `ARNS_INTERACTION_TYPES` enums in +`src/types.ts`. + +### Data fetching + +React Query for all server state. `queryClient` in `src/utils/network.ts` +(`gcTime` 1 day, `staleTime` 5 min, `refetchOnWindowFocus: false` — deliberately +tuned to stop refetch storms). An IndexedDB persister is implemented +(`createIDBPersister`) but not currently wired up in `main.tsx`. + +Domain logic lives in `src/hooks/use.tsx`. Arweave data retrieval goes +through `ArweaveCompositeDataProvider` / `SimpleArweaveDataProvider` in +`src/services/arweave/`. ### Routing -Hash-based routing using React Router v6 (`createHashRouter`): - -- Routes defined in `src/App.tsx` with lazy loading for pages -- Main routes: - - `/` — Home/search - - `/connect` — Wallet connection modal - - `/register/:name` — Name registration - - `/checkout` — Payment checkout - - `/manage` — Redirects to `/manage/names` - - `/manage/:path` — Asset management (names or ants tab) - - `/manage/ants/:id` — Manage specific ANT - - `/manage/ants/:id/undernames` — ANT undernames - - `/manage/names/:name` — Manage specific domain - - `/manage/names/:name/extend` — Extend lease - - `/manage/names/:name/upgrade-undernames` — Increase undernames - - `/manage/names/:name/undernames` — Domain undernames - - `/transaction/review`, `/transaction/complete` — Transaction flow - - `/returned-names` — Returned Names Protocol page - - `/prices` — Pricing info - - `/settings/network`, `/settings/devtools` — Settings (separate layout) - -### Wallet Integration - -Supports multiple wallet types via: - -**Arweave Wallets**: -- **Wander** (formerly ArConnect) - Primary Arweave wallet (`arconnect`) -- **Arweave.app** (`arweave-wallet-connector`) -- **Beacon** - Mobile wallet connector - -**Ethereum Wallets** (via Rainbow Kit + Wagmi): -- **Rainbow Kit** (`@rainbow-me/rainbowkit`) - Multi-wallet modal supporting 100+ wallets -- **Wagmi** for Ethereum wallet state and interactions -- Configured in `src/main.tsx` with `getDefaultConfig()` -- Supported chains: Ethereum mainnet, Base, and Polygon -- WalletConnect Project ID: hardcoded in `src/utils/constants.ts` - -**Wallet Connectors** (`src/services/wallets/`): -- `WanderWalletConnector` - Wander/ArConnect -- `ArweaveAppWalletConnector` - arweave.app -- `BeaconWalletConnector` - Beacon mobile -- `EthWalletConnector` - Generic Ethereum (works with any wagmi connector) - -**Key Types** (`src/types.ts`): -- `ArNSWalletConnector` interface defines the common contract for all wallets -- `WALLET_TYPES` enum: `WANDER`, `ARWEAVE_APP`, `ETHEREUM`, `BEACON` -- `AoAddress` = `EthAddress | ArweaveTransactionID` (union type for all addresses) -- `EthAddress` = `` `0x${string}` `` (Ethereum address format) -- Wallet type persisted to `localStorage` as `walletType` key - -Wallet state managed in `WalletState` context with reconnection logic for each -wallet type - -### Payment Systems - -- **Turbo SDK** (`@ardrive/turbo-sdk`): Credits and uploads - - Web-specific upload implementation uses `@ardrive/turbo-sdk/web` - - Custom type definitions in `src/types/turbo.ts` provide type safety for web - upload operations (SDK doesn't export web-specific types) - - Image uploads handled via `useUploadArNSLogo` hook with progress tracking -- **Stripe** (`@stripe/react-stripe-js`, `@stripe/stripe-js`): Fiat payments -- Stripe initialized in App.tsx with network-specific publishable key - -### Styling - -- **TailwindCSS** with custom configuration (`tailwind.config.mjs`) -- **Ant Design** (`antd`) for UI components with custom theming -- **Radix UI** for headless components (checkbox, radio, select, switch) -- **Framer Motion** for animations -- CSS modules pattern: component folders contain `styles.css` - -### Linting (Biome) - -- Single quotes for JavaScript/TypeScript (`quoteStyle: "single"`) -- Import organization enabled (auto-sorted on format) -- `noExplicitAny` and `noEmptyBlockStatements` are turned off -- `noUnusedVariables` is an error — clean up unused imports/vars - -## File Organization Rules +Hash router (`createHashRouter`) with lazy-loaded pages, defined in `App.tsx`. +Two top-level layouts: `Layout` for the app, and a separate `SettingsLayout` for +`/settings/network` and `/settings/devtools`. Breadcrumbs come from per-route +`handle.crumbs` functions; `ANT_FLAG` is a sentinel the Breadcrumbs component +resolves to the ANT's display name. -### Components +### Payments -- Create a folder for each component containing: - - `.tsx` - exported component file - - `__tests__/` - testing folder - - `styles.css` - component styles -- Test files named: `.test.ts` +- **Turbo SDK** (`@ardrive/turbo-sdk/web`) — credits, uploads. Logo uploads via + `useUploadArNSLogo` with progress tracking. +- **Stripe** — fiat, initialized in `App.tsx` keyed on + `turboNetwork.STRIPE_PUBLISHABLE_KEY` so a network switch remounts `Elements`. +- Checkout payment methods: `crypto` (SOL/ARIO), `credits` (Turbo), `card` + (Stripe). Base-token branches exist but are unreachable (see Dead EVM code). -### Utilities +Turbo's web build has different type signatures than Node. Import types from +`@ardrive/turbo-sdk/web` (re-exported via `src/types/turbo.ts`) and use +`as unknown as TurboWebAuthenticatedClient` when creating authenticated clients +for web upload — the web `uploadFile` takes `File` directly. +`useUploadArNSLogo.tsx` is the reference usage. -- Place in `src/utils/` folder with its own testing folder -- Path alias: `@src/utils/*` -- Image utilities in `src/utils/imageUtils.ts` for validation, compression, and - dimension checking - -### Types - -- TypeScript type definitions in `src/types/` for external libraries that don't - export needed types -- Example: `src/types/turbo.ts` provides web-specific Turbo SDK types - -### Images - -- Location: `assets/images//` -- `dark/` - dark mode specific images -- `light/` - light mode specific images -- `common/` - shared across themes - -### Translations - -- Location: `assets/translations/` -- Named: `.json` - -## Error Handling and Notifications - -### Event Emitter Pattern - -Import `eventEmitter` from `src/utils/events.ts` to emit notifications. - -### Error Types (`src/utils/errors.ts`) - -- **NotificationOnlyError**: Shows notification only (no external error reporting) - - Subclasses: `ValidationError`, `WanderError`, `ArweaveAppError`, - `MetamaskError`, `EthereumWalletError`, `BeaconError`, `InsufficientFundsError`, - `WalletNotInstalledError`, `UpgradeRequiredError`, `ANTStateError` -- **Standard Error**: Logged to console for debugging - -Use `NotificationOnlyError` for expected/user-facing errors. Use standard -`Error` for unexpected errors that should be logged. - -## Build and Deployment - -### Production Build - -- TypeScript compilation followed by Vite build -- Source maps enabled -- Large memory allocation: `--max-old-space-size=32768` - -### Environment Variables - -- Defined in Vite config, not exposed via process.env for security -- Key variables: `VITE_ARWEAVE_HOST`, `VITE_ARWEAVE_GRAPHQL_URL`, - `VITE_HYPERBEAM_URL`, `VITE_ARNS_NAME` -- Build-time variables: `VITE_ENVIRONMENT` (production/develop), - `VITE_NODE_ENV`, `VITE_GITHUB_HASH` (set to `local` in dev) +### Styling -### Arweave Deployment +TailwindCSS (`tailwind.config.mjs`) + Ant Design with heavy token overrides in +`main.tsx`'s `ConfigProvider` (antd is themed via CSS custom properties like +`var(--primary)`, `var(--card-bg)`). Radix UI for headless primitives, Framer +Motion for animation. Per-component `styles.css`. -```bash -yarn publish:arweave # Deploys to Arweave using permaweb-deploy with ARNS name -``` +## Conventions -### CI/CD +### File organization -Workflows in `.github/workflows/`: +- **Components**: one folder each, containing `.tsx`, `styles.css`, + and `__tests__/`. Test files named `.test.ts(x)`. +- **Utils**: `src/utils/`, with colocated or sibling tests. +- **Types for external libs that don't export what we need**: `src/types/`. +- **Images**: `assets/images/{dark,light,common}/`. +- **Translations**: `assets/translations/.json`. -- `build_and_test.yml` - Build and test on PR -- `pr.yml` - PR checks -- `staging_deploy.yml` - Staging deployments -- `production.yml` - Production deployments +### Errors and notifications -## Path Aliases +Import `eventEmitter` from `src/utils/events.ts` to raise notifications. -Configured in `tsconfig.json` and `vite.config.ts`: +`NotificationOnlyError` (`src/utils/errors.ts`) for expected, user-facing +problems — shows a notification, no console noise. Subclasses include +`ValidationError`, `InsufficientFundsError`, `WalletNotInstalledError`, +`ANTStateError`, `BaseTokenError`, `TopUpError`, and several legacy wallet errors +(`WanderError`, `MetamaskError`, `BeaconError`, …) kept from the multi-chain era. +Use a plain `Error` for unexpected failures that should be logged. -- `@src/*` → `./src/*` -- `@tests/*` → `./tests/*` +### Gateway URL routing -## Git Hooks +Different domains for different jobs — using the wrong one is a real bug: -- **pre-commit**: Runs `lint-staged` which lints TypeScript files and formats - all files -- **commit-msg**: Uses `commitlint` with conventional commit format - - Standard types: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore` - - No max length restrictions on header or body +- **`arweave.net`** — Arweave L1 GraphQL only (`ARWEAVE_HOST`, + `ARWEAVE_GRAPHQL_URL`). +- **`turbo-gateway.com`** — Arweave data retrieval (`DEFAULT_ARWEAVE`, + `NETWORK_DEFAULTS.DATA.HOST`, `TURBO.GATEWAY_URL`, static HTML assets). +- **`ar.io`** — ArNS name links (`NETWORK_DEFAULTS.ARNS.HOST`). -## Feature Flags +### Feature flag: `ARNS_PURCHASES_DISABLED` -### ArNS Name Purchases (`ARNS_PURCHASES_DISABLED`) +In `src/utils/constants.ts`, read at runtime — no other changes needed to toggle. -When `true`, flows that create new ANTs are blocked: new name registration -(`BUY_RECORD`) and upgrade to permabuy (`UPGRADE_NAME`). Affected components: -Register, Checkout, HomeSearch, ReturnedNamesTable, and the "Permanently Buy" -tab on ExtendLease. Disabled buttons show a tooltip from +When `true`, flows that mint new ANTs are blocked: `BUY_RECORD` and +`UPGRADE_NAME`. Affects Register, Checkout, HomeSearch, ReturnedNamesTable, and +the "Permanently Buy" tab on ExtendLease. Disabled controls show `ARNS_PURCHASES_DISABLED_TOOLTIP`. -Flows that operate on existing ANTs are **not** affected: lease extensions -(`EXTEND_LEASE`) and undername upgrades (`INCREASE_UNDERNAMES`). - -- **Location:** `src/utils/constants.ts` -- **To disable purchases:** set `ARNS_PURCHASES_DISABLED = true` -- **To enable purchases:** set `ARNS_PURCHASES_DISABLED = false` - -No other code changes are required; the flag is read at runtime. - -## Gateway URL Routing +Flows on *existing* ANTs are unaffected: `EXTEND_LEASE`, `INCREASE_UNDERNAMES`. -Different gateway domains are used for different purposes: +## Configuration -- **GraphQL** (`arweave.net`): Arweave L1 GraphQL queries — `ARWEAVE_HOST`, - `ARWEAVE_GRAPHQL_URL`, NetworkSettings suConnect -- **Transaction/data fetching** (`turbo-gateway.com`): Arweave data retrieval — - `DEFAULT_ARWEAVE`, `TURBO.GATEWAY_URL`, static HTML asset URLs -- **ArNS name links** (`ar.io`): ArNS domain resolution — - `NETWORK_DEFAULTS.ARNS.HOST`, GlobalState `gateway` default +### Environment variables -When adding new URLs, use the correct domain for the purpose. Do not use -`arweave.net` for data fetching or `turbo-gateway.com` for ArNS links. +Vite only exposes an explicit allowlist — never widen the `define` block in +`vite.config.ts` to the whole `process.env`. -## Important Implementation Notes - -### Smartweave Contract Deployment - -Contracts are deployed manually to avoid the Warp Deploy Plugin which doesn't -implement tree shaking. This saves ~2MB in build size. Switch to Warp core -package deployment when tree shaking is supported or when deployment becomes -more complex (e.g., L2 usage). - -### ANT Version Compatibility - -Minimum ANT version: 16 (`MIN_ANT_VERSION` in `src/utils/constants.ts`) All -workflows (reassign, release, etc.) must be compatible with this version. - -### Node Polyfills - -Vite is configured with `vite-plugin-node-stdlib-browser` for Node.js polyfills -required by Arweave libraries. +- Solana: `VITE_SOLANA_NETWORK`, `VITE_SOLANA_RPC_URL`, + `VITE_ARIO_CORE_PROGRAM_ID`, `VITE_ARIO_GAR_PROGRAM_ID`, + `VITE_ARIO_ARNS_PROGRAM_ID`, `VITE_ARIO_ANT_PROGRAM_ID`, + `VITE_ARIO_MINT_ADDRESS` +- Arweave/Turbo: `VITE_ARWEAVE_HOST`, `VITE_ARWEAVE_GRAPHQL_URL`, + `VITE_HYPERBEAM_URL`, `VITE_ARNS_NAME` +- Build: `VITE_ENVIRONMENT` (production/develop), `VITE_NODE_ENV`, + `VITE_GITHUB_HASH` -### TypeScript Configuration +`VITE_SOLANA_RPC_URL` is read with `||`, not `??`, on purpose — CI injects `""` +when the secret is unset. -- Target: ESNext -- Module resolution: bundler -- Strict mode enabled -- No emit (Vite handles compilation) +### Path aliases -### Ethereum Wallet Signer Architecture +`@src/*` → `./src/*`, `@tests/*` → `./tests/*` (declared in both `tsconfig.json` +and `vite.config.ts`; Jest mirrors them in `moduleNameMapper`). -The `EthWalletConnector` creates two types of signers for Ethereum wallets: +### Vite specifics -- **Turbo Signer** (`TurboArNSSigner`): For logo uploads via Turbo SDK -- **AO Signer** (`ContractSigner`): For ArNS/ANT interactions via AO messaging +- `esbuild: false` and `optimizeDeps.esbuildOptions.target: 'esnext'` — but build + output must avoid top-level await (Safari 14 / es2020), which is why the + Ed25519 polyfill uses an IIFE wrapper. +- `vite-plugin-node-stdlib-browser` supplies Node polyfills for Arweave libs. +- `server.allowedHosts` includes ngrok wildcards for tunnelled dev sessions. -Both use `InjectedEthereumSigner` from AR.IO SDK. Public key is derived by: -1. Signing message: "Sign this message to connect to ArNS.app" -2. Recovering public key from signature using `viem/recoverPublicKey` -3. Storing in signer for subsequent AO data item signing +### Jest specifics -### Turbo SDK Type Safety +`transformIgnorePatterns` explicitly un-ignores `@ar.io`, `@permaweb`, +`arbundles`, `@dha-team/arbundles`, `arweave-wallet-connector`, and `wagmi` — +these ship ESM that must be transformed. Add new ESM-only deps here when they +break tests. `@ar.io/solana-contracts` subpaths are remapped to their built +`lib/*/index.js`. -The Turbo SDK's web implementation has different type signatures than its -Node.js counterpart. To maintain type safety: +## Git hooks -- Import types directly from `@ardrive/turbo-sdk/web` (TurboUploadDataItemResponse, - TurboUploadEventsAndPayloads, DataItemOptions) -- `src/types/turbo.ts` re-exports these types for convenience -- Use type assertions (`as unknown as TurboWebAuthenticatedClient`) when creating - TurboFactory.authenticated clients for web upload operations since the web - implementation's `uploadFile` method accepts `File` objects directly -- The `useUploadArNSLogo` hook (src/hooks/useUploadArNSLogo.tsx) demonstrates - proper usage pattern for web uploads with progress tracking +- **pre-commit**: `lint-staged` → `biome check --write --unsafe` + `biome format --write` +- **commit-msg**: commitlint, conventional commits (`feat`, `fix`, `docs`, + `style`, `refactor`, `test`, `chore`), no length limits -### Rainbow Kit / Wagmi Configuration +## CI/CD -Rainbow Kit is configured in `src/main.tsx` with `getDefaultConfig()`: -- WalletConnect Project ID is hardcoded in `src/utils/constants.ts` as `WALLETCONNECT_PROJECT_ID` -- Supported chains: Ethereum mainnet, Base, and Polygon -- The provider hierarchy is: WagmiProvider → QueryClientProvider → RainbowKitProvider → App contexts +`.github/workflows/`: `build_and_test.yml`, `pr-preview.yaml`, +`staging_deploy.yml`, `production.yml`. diff --git a/package.json b/package.json index 31f7223eb..b6d0ce2ad 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arns-vite-react", "private": true, - "version": "2.3.5", + "version": "2.3.6", "homepage": ".", "scripts": { "build": "yarn clean && cross-env NODE_OPTIONS=--max-old-space-size=32768 vite build", diff --git a/src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx b/src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx index 97b5d1f64..f67f877b1 100644 --- a/src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx +++ b/src/components/inputs/text/NameTokenSelector/NameTokenSelector.tsx @@ -10,14 +10,25 @@ import { SolanaAddress } from '../../../../services/solana/SolanaAddress'; import { useGlobalState } from '../../../../state/contexts/GlobalState'; import { useWalletState } from '../../../../state/contexts/WalletState'; import { AoAddress, VALIDATION_INPUT_TYPES } from '../../../../types'; -import { isArweaveTransactionID, wrapAntId } from '../../../../utils'; -import { ARWEAVE_TX_LENGTH } from '../../../../utils/constants'; +import { isValidSolanaAddress, wrapAntId } from '../../../../utils'; +import { + SOLANA_ADDRESS_ENTRY_REGEX, + SOLANA_ADDRESS_MAX_LENGTH, +} from '../../../../utils/constants'; import eventEmitter from '../../../../utils/events'; import { CloseIcon, HamburgerOutlineIcon } from '../../../icons'; import { Loader } from '../../../layout'; import ValidationInput from '../ValidationInput/ValidationInput'; import './styles.css'; +/** + * An ANT id. Solana mint pubkeys are the norm post-de-AO; the Arweave arm + * remains for legacy ids. `wrapAntId` returns this union, so the token-list + * flow must carry it end to end — annotating these as Arweave-only was + * merely papered over by a cast. + */ +type AntId = ArweaveTransactionID | SolanaAddress; + type NameTokenDetails = { [id: string]: { owner: string; @@ -103,7 +114,7 @@ function NameTokenSelector({ async function getTokenList( address: AoAddress | undefined, - imports: Array = [], + imports: Array = [], ) { try { setLoading(true); @@ -111,11 +122,11 @@ function NameTokenSelector({ throw new Error('No address provided'); } - const fetchedprocessIds: Array = []; + const fetchedprocessIds: Array = []; const validImports = imports.length ? await Promise.all( - imports.map(async (id: ArweaveTransactionID) => { + imports.map(async (id: AntId) => { try { const contract = await buildAntRead({ processId: id.toString(), @@ -136,8 +147,8 @@ function NameTokenSelector({ } }), ).then( - (ids: Array) => - ids.filter((id) => !!id) as ArweaveTransactionID[], + (ids: Array) => + ids.filter((id) => !!id) as AntId[], ) : []; @@ -155,7 +166,7 @@ function NameTokenSelector({ ); const contracts: { - processId: ArweaveTransactionID; + processId: AntId; names: Record; owner: string; controllers: string[]; @@ -364,7 +375,11 @@ function NameTokenSelector({ showValidationIcon={true} setValue={(v) => handleTokenSearch(v)} value={searchText ?? ''} - maxCharLength={ARWEAVE_TX_LENGTH} + // An ANT id is a Solana mint pubkey (base58, 32–44 chars), not an + // Arweave TX ID. The previous 43-char cap silently swallowed the + // paste for most addresses. + maxCharLength={SOLANA_ADDRESS_MAX_LENGTH} + customPattern={SOLANA_ADDRESS_ENTRY_REGEX} placeholder={ selectedToken ? selectedToken.name?.length @@ -373,9 +388,11 @@ function NameTokenSelector({ : 'Add an Ar.io Name Token (ANT)' } validationPredicates={{ - [VALIDATION_INPUT_TYPES.ARWEAVE_ID]: { - fn: (id: string) => { - return arweaveDataProvider.validateArweaveId(id); + [VALIDATION_INPUT_TYPES.SOLANA_ADDRESS]: { + fn: async (id: string) => { + if (!isValidSolanaAddress(id)) { + throw new Error('Invalid Token Address'); + } }, }, }} @@ -407,7 +424,7 @@ function NameTokenSelector({ ) : searchText && validImport === false ? ( <> ) : searchText && - isArweaveTransactionID(searchText) && + isValidSolanaAddress(searchText) && !Object.keys(tokens ?? []).includes(searchText) ? (