# AAStar SDK Refactor Audit & Plan (Trae) — 2026-01-13 22:12:42

**Scope audited (code, not only docs)**
- `packages/sdk/src` (clients, errors, utils, public exports)
- `packages/core/src` (abis, actions, validation, configs)
- `packages/paymaster/src` (Paymaster V4 middleware + UserOp helpers)
- Root tooling (`package.json`, `vitest.config.ts`, `run_sdk_regression.sh`)

**Repository state snapshot**
- Root package version: `0.16.3` (workspaces) (`/package.json`)
- `@aastar/sdk` package version: `0.16.3` (`/packages/sdk/package.json`)
- `@aastar/core` package version: `0.16.2` (`/packages/core/package.json`)

---

## 0) Executive Summary (Reality vs Docs)

The previous refactor docs under `docs/refactor/2026-01-13-*.md` describe a “v3 plan completed” state (validation, ABI loader fix, namespacing, vitest). The codebase largely matches the direction, but **the “production-grade” bar is still blocked by a few structural issues**:

1. **Type safety is still broken at the SDK surface**: `as any` is pervasive in clients and core actions, so consumers lose reliable TS guarantees and refactors remain risky.
2. **Error handling is fragmented**: you currently have at least 4 competing error strategies (`AAStarError`, `SDKResult`, `decodeContractError`, `handleContractError`) plus plain `Error` + `console.*`.
3. **Tooling quality gates are miswired**: root scripts call `pnpm -r lint/test` but packages don’t define `lint`/`test`, so CI-style enforcement is not actually guaranteed.
4. **Node-only utilities are exported from browser-facing SDK**: `KeyManager` uses `fs/path`, but `packages/sdk/src/index.ts` exports it, which breaks browser bundling and is a security footgun.
5. **Dependency/version drift**: root `viem` differs from workspace packages’ `viem`, increasing duplicated installs and type mismatch probability.

The good news: **ABI loading and basic input validation are already implemented and integrated in critical paths**, and the “Thick Client” direction is clearly present.

---

## 1) What’s Already Solid (Keep / Build On)

### 1.1 ABI loading is fixed and unified
- `packages/core/src/abis/index.ts` uses `extractAbi()` to support both `[{...}]` and `{ abi: [...] }` JSON formats and exports an `Abis` namespace.
- This resolves the historic “abi.filter is not a function” runtime failure class.

### 1.2 Validation layer exists and is used in some critical flows
- `packages/core/src/utils/validation.ts` provides `validateAddress`, `validateAmount`, `validateUint128`, `validateHex`.
- It’s already used inside:
  - Operator flow (`createOperatorClient().onboardFully` validates amounts/roleId/gasTokens)
  - Admin namespaced modules validate parameters before calls
  - Paymaster middleware validates `uint128` bounds for gas limits

### 1.3 Namespacing is implemented (Admin)
- `packages/sdk/src/clients/admin.ts` exposes `client.system`, `client.finance`, `client.operators`.
- This is the right move to keep top-level API from exploding.

### 1.4 Paymaster V4 middleware is moving towards defensible encoding
- `packages/paymaster/src/V4/PaymasterUtils.ts` has a clear packing layout and `validateUint128` checks for gas limits.

---

## 2) Critical Gaps (P0 / “Production Blockers”)

### 2.1 Tooling “gates” are currently not real
- Root scripts:
  - `lint`: `pnpm -r lint`
  - `test`: `pnpm -r test`
- Workspace packages (checked `packages/*/package.json`) generally do **not** define `lint`/`test`, so these scripts will fail or do nothing meaningful depending on pnpm config.

**Impact**
- You can’t rely on “lint/typecheck/test must pass” as an invariant.
- Refactor risk is high because regressions can slip through until runtime.

**Concrete evidence**
- `pnpm run build` currently fails at `@aastar/core` because `src/**/*.test.ts` imports `vitest`, but `@aastar/core` does not depend on `vitest` and its `tsc` compilation includes those test files.

### 2.2 Browser safety + security footgun: node fs exported from SDK
- `packages/sdk/src/utils/keys.ts` imports `fs/path` and provides `KeyManager.saveToEnvFile/saveToJsonFile`.
- This file is exported via `packages/sdk/src/index.ts`.

**Impact**
- Bundlers for web apps may error (Node builtins).
- SDK consumers may accidentally ship key material logic into browser bundles.
- Even if used only in Node, putting “key writing” on the main SDK surface is risky.

### 2.3 Type-safety degradation: pervasive `as any`
- SDK: 83 occurrences across 9 files in `packages/sdk/src`.
- Typical pattern:
  - `superPaymasterActions(...)(client as any)`
  - `(client as any).writeContract(...)`, `.waitForTransactionReceipt(...)`

**Impact**
- TS cannot protect contract call parameter shapes or return types.
- Autocomplete and refactor safety collapse exactly in the critical “SDK glue” code.

### 2.4 Error handling fragmentation
You currently have:
- `AAStarError` + `AAStarErrorCode` (`packages/sdk/src/errors/AAStarError.ts`)
- `SDKResult<T>` + `safeSDKCall()` (`packages/sdk/src/types/result.ts`)
- `decodeContractError()` (`packages/sdk/src/errors/decoder.ts`)
- `handleContractError()` + known error mapping (`packages/sdk/src/utils/errorHandler.ts`)
- plain `throw new Error(...)` in multiple clients

**Impact**
- Callers can’t predict: do I catch exceptions, or check `result.success`?
- Logging noise (71 console occurrences in sdk) makes CLI output messy and leaks internal state.

---

## 3) High-Value Improvements (P1 / “Make it Great”)

### 3.1 Unify “client composition” to reduce repetition and any-casts
Right now every role client repeats:
- `createClient({ chain, transport, account }).extend(publicActions).extend(walletActions)`
- merges actions via `...(xxxActions(addr)(client as any))`

This should become a single typed composition utility that:
- returns a consistent `WalletClient & PublicActions & WalletActions` type
- attaches typed action modules with minimal `as any` (ideally none)
- standardizes logging and error wrappers

### 3.2 Stop exporting “everything” from `@aastar/sdk`
`packages/sdk/src/index.ts` re-exports:
- `@aastar/core`, `@aastar/account`, `@aastar/paymaster`, `@aastar/identity`, `@aastar/tokens`, `@aastar/dapp`

This is convenient, but it:
- forces heavy dependencies (including React/dapp) onto server/CLI users
- makes tree-shaking harder
- makes compatibility (node vs browser) harder

Prefer:
- keep `@aastar/sdk` as “SDK runtime clients only”
- leave `@aastar/dapp` as separate package and stop re-exporting it
- provide explicit subpath exports for node-only helpers

### 3.3 Make gasless flows “first-class” and eliminate manual packing in clients
EndUser gasless flows build userOps with manual `pad/concat` and inline ABIs. This should be centralized:
- `UserOperationBuilder` (already exists in sdk utils) becomes the one place packing logic lives
- paymaster middleware becomes the single canonical `paymasterAndData` encoder
- clients call these helpers, not duplicate it

### 3.4 Align workspace dependency versions
Right now `viem` is defined at:
- root: `2.41.2`
- core: `2.43.3`
- sdk: `2.43.3`

Prefer one of:
- put `viem` only at root and use workspace protocol for all packages, or
- keep it per-package but ensure all packages use the exact same version.

---

## 4) Refactor Plan (v4) — staged, regression-safe

### Phase 0 (P0): Make quality gates real + lock down surface risks
**Deliverables**
- Fix root scripts so `lint` and `test` are deterministic and actually run.
- Remove node-only key utilities from the default SDK export surface.
- Add a “no accidental secrets” gate: ensure no default private keys are shipped in published artifacts.

**Key changes (conceptually)**
- Root `test` should run `vitest run` with root `vitest.config.ts`.
- Root `lint` should run root eslint config (or introduce one).
- SDK exports:
  - `@aastar/sdk` (browser-safe clients + types)
  - `@aastar/sdk/node` (KeyManager, filesystem helpers) or move to scripts-only.

### Phase 1 (P1): Typed client factory + action binding (reduce `as any`)
**Deliverables**
- One canonical function to construct clients and attach modules.
- Reduce `as any` usage sharply (goal: client layer → near-zero).
- Establish a typed wrapper to standardize `.readContract/.writeContract/.simulateContract`.

### Phase 2 (P1): Error strategy unification (choose one of 2 and commit)
Pick one:

**Option A (DX-first for app dev): Result-first**
- All public SDK methods return `Promise<SDKResult<T>>`.
- `throw` only for programmer errors (invariants).

**Option B (ecosystem-default): throw-first**
- All public SDK methods throw `AAStarError` (typed, with code + details).
- `SDKResult` stays internal (for optional wrapping).

### Phase 3 (P1): Gasless and AA orchestration consolidation
**Deliverables**
- Move all packing (accountGasLimits, gasFees, paymasterAndData) behind one builder API.
- Ensure EntryPoint version compatibility is explicit (v0.6/v0.7) and validated.
- Add “simulate first” optional mode for safety.

### Phase 4 (P2): Package boundaries + exports cleanup
**Deliverables**
- Stop re-exporting `@aastar/dapp` from `@aastar/sdk`.
- Split utils into runtime-safe vs node-only.
- Clarify which packages are “SDK runtime” vs “examples/scripts”.

### Phase 5 (P2): Testing matrix
**Deliverables**
- L1: unit tests for validation and packers
- L2: mocked viem client tests for orchestration ordering
- L3: optional anvil E2E (already exists via `run_full_regression.sh`)

---

## 5) Concrete Audit Notes (By File / Module)

### 5.1 `packages/core/src/abis/index.ts`
- ✅ `extractAbi()` and `Abis` namespace are correct direction.
- ⚠️ ABI typing is still `any`; long-term: generate types or use `as const` ABI typing.

### 5.2 `packages/core/src/utils/validation.ts`
- ✅ good baseline, covers address/hex/uint128 bounds.
- ⚠️ doesn’t enforce fixed-length bytes32 for roleId (validates hex syntax only).

### 5.3 `packages/sdk/src/clients/operator.ts`
- ✅ validates stake/deposit/roleId/gasTokens.
- ⚠️ mixes idempotent checks + orchestration + logging in one method.
- ⚠️ still heavy `client as any` usage; returns thrown `Error` instead of unified strategy.

### 5.4 `packages/sdk/src/clients/admin.ts`
- ✅ namespacing exists and validation is applied for most functions.
- ⚠️ still binds actions via `baseClient as any`.

### 5.5 `packages/sdk/src/utils/keys.ts`
- ⚠️ node-only, should not be exported from browser-safe SDK entry.

---

## 6) “Next 48 hours” move (lowest risk, highest ROI)
1. Fix root `lint/test` scripts so they run deterministically.
2. Stop exporting node-only key utilities from `@aastar/sdk` main entry.
3. Introduce one typed client factory helper and migrate one client (Operator) as a pilot.
4. Decide error strategy (Result vs throw) and enforce it for the pilot client.
