* chore: migrate tx-builder from safe-apps repo to this monorepo create-react-app is deprecated and I’ve migrated the code to vite. web3 is deprecated and I’ve moved the code to ethersjs the cypress tests were broken and I dropped them original code used mui4 -> here we’ve migrated to mui6 * fix(tx-builder): remove build from tx-builder * fix(tx-builder): vite config * fix(tx-builder): linting * fix(tx-builder): fix typo * fix(tx-builder): don’t mutate array * fix(tx-builder): fix exponential backtracking * fix(tx-builder): git workflows * fix(tx-builder): fix lint setup * fix(tx-builder): align package dependencies * fix(tx-builder): invalid react-dom version * fix(tx-builder): git workflow * fix(tx-builder): drag and drop was not working react-beautiful-dnd was no longer supported and it was not compatible with react 19. Moved to @hello-pangea/dnd * fix(tx-builder): fix pallet issues
13 KiB
Implementation Plan: Migrate tx-builder to safe-wallet-monorepo
Branch: 001-migrate-tx-builder | Date: 2026-01-12 | Spec: spec.md
Input: Feature specification from /specs/001-migrate-tx-builder/spec.md
Summary
Migrate the tx-builder Safe App from safe-react-apps repository to safe-wallet-monorepo as a standalone Vite-powered SPA. The migration includes updating React 17→19, standardizing on MUI v6 (current codebase has mixed v4/v6 imports), ethers v5→v6, and creating an independent CI/CD deployment workflow.
Technical Context
Language/Version: TypeScript 5.9 (matching monorepo) Primary Dependencies:
- React 19.1.0 (from monorepo)
- MUI v6 (
@mui/material^6.3.0) - ethers 6.14.3 (from monorepo)
- Vite 6.x (bundler)
- react-router-dom ^6.x
- react-hook-form ^7.x
- @safe-global/safe-apps-sdk ^9.1.0
- @safe-global/safe-apps-react-sdk (latest compatible)
- styled-components ^5.x or migrate to emotion
Storage: Browser localStorage (via localforage for transaction library) Testing: Jest + MSW + React Testing Library (matching monorepo patterns) Target Platform: Modern browsers (Chrome, Firefox, Safari, Edge) Project Type: Standalone SPA within monorepo (apps/tx-builder) Performance Goals: Build < 3 min, dev server start < 30s, bundle size within 20% of current Constraints: Must work as Safe App in iframe, preserve all existing functionality Scale/Scope: ~104 TSX files, 6 pages, 46 components with MUI dependencies
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
| Principle | Status | Notes |
|---|---|---|
| I. Type Safety | ✅ PASS | Will use TypeScript 5.9, no any types, proper interfaces |
| II. Branch Protection | ✅ PASS | Working on feature branch 001-migrate-tx-builder |
| III. Cross-Platform | ✅ PASS | tx-builder is web-only, no shared package changes |
| IV. Testing Discipline | ✅ PASS | Will use MSW for network mocks, faker for test data |
| V. Feature Organization | ⚠️ N/A | tx-builder is standalone app, not a feature in web app |
| VI. Theme System | ⚠️ PARTIAL | tx-builder has its own theme; can adopt @safe-global/theme tokens later (P3 story) |
Gate Result: PASS - No violations requiring justification.
Project Structure
Documentation (this feature)
specs/001-migrate-tx-builder/
├── plan.md # This file
├── research.md # Phase 0 output
├── data-model.md # Phase 1 output
├── quickstart.md # Phase 1 output
├── contracts/ # Phase 1 output (Safe Apps SDK interface)
└── tasks.md # Phase 2 output (created by /speckit.tasks)
Source Code (repository root)
apps/tx-builder/
├── package.json # @safe-global/tx-builder workspace package
├── vite.config.ts # Vite configuration
├── tsconfig.json # TypeScript config extending monorepo
├── cypress.config.ts # Cypress E2E configuration
├── index.html # Vite entry HTML
├── public/
│ ├── manifest.json # Safe App manifest
│ └── tx-builder.png # App icon
├── src/
│ ├── main.tsx # App entry (renamed from index.tsx)
│ ├── App.tsx # Router setup
│ ├── vite-env.d.ts # Vite type declarations
│ ├── test-utils.tsx # Test render helpers with providers
│ ├── components/ # UI components (migrated from MUI v4 to v6)
│ │ ├── buttons/
│ │ ├── forms/
│ │ │ ├── SolidityForm.tsx
│ │ │ ├── SolidityForm.test.tsx # Colocated test
│ │ │ ├── validations/
│ │ │ │ └── validations.test.ts # Colocated test
│ │ │ └── fields/
│ │ │ └── fields.test.ts # Colocated test
│ │ ├── modals/
│ │ ├── Header.tsx
│ │ ├── Header.test.tsx # Colocated test
│ │ ├── Icon/
│ │ └── FixedIcon/
│ ├── pages/ # Route pages
│ │ ├── Dashboard.tsx
│ │ ├── CreateTransactions.tsx
│ │ ├── ReviewAndConfirm.tsx
│ │ ├── TransactionLibrary.tsx
│ │ ├── SaveTransactionLibrary.tsx
│ │ └── EditTransactionLibrary.tsx
│ ├── hooks/ # Custom React hooks
│ ├── store/ # React Context providers (not Redux)
│ ├── lib/ # Business logic
│ │ ├── batches/
│ │ ├── simulation/
│ │ ├── checksum.ts
│ │ ├── checksum.test.ts # Colocated test (convert from .js)
│ │ └── storage.ts
│ ├── theme/ # MUI v6 theme configuration
│ ├── routes/ # Route definitions
│ ├── typings/ # TypeScript type definitions
│ ├── utils/
│ │ ├── utils.ts
│ │ └── utils.test.ts # Colocated test
│ └── mocks/ # MSW handlers (new)
│ └── handlers.ts
├── cypress/ # E2E tests
│ ├── e2e/
│ │ └── tx-builder.spec.cy.ts # Main E2E test (17 cases)
│ ├── fixtures/
│ │ ├── test-working-batch.json
│ │ ├── test-mainnet-batch.json
│ │ ├── test-modified-batch.json
│ │ ├── test-invalid-batch.json
│ │ └── test-empty-batch.json
│ └── support/
│ ├── e2e.ts
│ ├── iframe.ts # Safe App iframe helpers
│ └── commands.ts
.github/workflows/
├── tx-builder-deploy.yml # New workflow for tx-builder deployment
└── tx-builder-checks.yml # PR checks (type-check, lint, test)
Structure Decision: tx-builder is a standalone Vite SPA in apps/tx-builder, following the monorepo pattern for apps. It does NOT integrate into Next.js web app since it runs as a Safe App in an iframe.
Complexity Tracking
No constitution violations requiring justification.
Testing Migration
Unit Tests (6 files to migrate)
| File | Description | Migration Notes |
|---|---|---|
src/utils.test.ts |
Utility function tests | Direct copy, update imports |
src/lib/checksum.test.js |
Checksum validation | Convert to TypeScript |
src/components/forms/validations/validations.test.ts |
Form validation tests | Update MUI component mocks |
src/components/forms/fields/fields.test.ts |
Form field tests | Update MUI component mocks |
src/components/forms/SolidityForm.test.tsx |
SolidityForm component | Update test-utils, MUI mocks |
src/components/Header.test.tsx |
Header component | Update test-utils, MUI mocks |
Test Infrastructure to Migrate:
src/test-utils.tsx- Custom render with providers (needs React 19 + MUI v6 updates)
Current Test Patterns:
// Uses custom render with all providers
import { render } from '../test-utils'
// Mocks axios (should migrate to MSW per constitution)
jest.mock('axios', () => ({
get: jest.fn(),
post: jest.fn(),
}))
Migration Changes Required:
- Update
test-utils.tsxfor React 19 (createRoot) and MUI v6 theme - Replace
jest.mock('axios')with MSW handlers (per constitution IV) - Update MUI component selectors (v4 → v6 class names may differ)
- Add
@faker-js/fakerfor test data generation
Cypress E2E Tests
Main Test File: cypress/e2e/tx-builder/tx-builder.spec.cy.js (413 lines, 17 test cases)
| Test Case | Coverage |
|---|---|
| Create and send simple batch | Core flow |
| Create and send complex batch | Multi-transaction |
| ENS name resolution | Address lookup |
| ABI-based transaction | Manual ABI entry |
| Custom data transaction | Raw data mode |
| Cancel batch flow | User cancellation |
| Revert cancel and continue | Flow recovery |
| Navigation with data preservation | State management |
| Invalid address validation | Error handling |
| Missing asset amount validation | Form validation |
| Missing method data validation | Form validation |
| Upload, save, download, remove batch | Library CRUD |
| Chain mismatch warning | Cross-chain detection |
| Modified batch error | Integrity check |
| Invalid batch rejection | Validation |
| Empty batch rejection | Validation |
| Simulate valid batch | Tenderly success |
| Simulate invalid batch | Tenderly failure |
Cypress Support Files to Migrate:
cypress/
├── e2e/tx-builder/
│ └── tx-builder.spec.cy.js # Main test file
├── fixtures/
│ ├── test-working-batch.json # Valid batch for testing
│ ├── test-mainnet-batch.json # Wrong chain batch
│ ├── test-modified-batch.json # Tampered batch
│ ├── test-invalid-batch.json # Malformed batch
│ └── test-empty-batch.json # Empty batch
├── support/
│ ├── e2e.js # Custom commands setup
│ ├── iframe.js # Safe App iframe helpers
│ └── commands.js # cypress-file-upload
└── lib/
└── slack.js # Slack notifications (optional)
Target Structure in Monorepo:
apps/tx-builder/
├── cypress/
│ ├── e2e/
│ │ └── tx-builder.spec.cy.js
│ ├── fixtures/
│ │ └── [batch JSON files]
│ └── support/
│ ├── e2e.js
│ ├── iframe.js
│ └── commands.js
├── cypress.config.ts # Vite-compatible config
└── package.json # cypress devDependency
Cypress Configuration Updates:
- Convert
cypress.config.js→cypress.config.ts - Update environment variables:
CYPRESS_TX_BUILDER_URLfor local dev - Ensure iframe commands work with Safe{Wallet} test environment
- Add to monorepo CI workflow
Migration Scope Analysis
Current State (safe-react-apps)
| Aspect | Current | Issue |
|---|---|---|
| React | 17.0.2 | Outdated, missing React 18/19 features |
| MUI | Mixed v4/v6 | package.json has v4, theme imports v6, components import v4 |
| ethers | v5 | Breaking changes to v6 |
| TypeScript | 4.9 | Missing newer features |
| Bundler | CRA (react-scripts) | Deprecated, slow builds |
| Env vars | REACT_APP_* |
CRA pattern |
Target State (safe-wallet-monorepo)
| Aspect | Target | Benefit |
|---|---|---|
| React | 19.1.0 | Concurrent features, better performance |
| MUI | v6 | Modern API, better theming |
| ethers | 6.14.3 | Consistent with monorepo |
| TypeScript | 5.9 | Const type parameters, decorators |
| Bundler | Vite 6.x | Fast HMR, modern ESM |
| Env vars | VITE_* |
Vite pattern |
Files Requiring Migration Changes
- 46 files importing from
@material-ui/*→@mui/* - Entry point
index.tsx→main.tsx(Vite convention) - Environment variables
REACT_APP_*→VITE_* - Build configuration CRA → Vite
- ethers usage v5 API → v6 API (breaking changes in providers, utils)