Skip to content

test: e2e network test harness and provider/namespace suites (Phase 1 of #335)#348

Open
latifkasuli wants to merge 2 commits intoethereum-optimism:mainfrom
latifkasuli:feat/e2e-system-tests
Open

test: e2e network test harness and provider/namespace suites (Phase 1 of #335)#348
latifkasuli wants to merge 2 commits intoethereum-optimism:mainfrom
latifkasuli:feat/e2e-system-tests

Conversation

@latifkasuli
Copy link
Copy Markdown

Summary

Phase 1 implementation of #335 — adds a shared Anvil-fork test harness and the first layer of e2e network tests covering provider read-paths, transaction construction, and namespace-level execution with state verification.

Shared test harness (src/test/network/)

Fixtures:

  • Chain configs (Mainnet, Optimism, Base) with CI-enforced RPC env vars — fails fast when MAINNET_RPC, OP_MAINNET_RPC, or BASE_MAINNET_RPC are missing in CI, falls back to public RPCs for local development
  • Test assets with whale addresses for ERC20 funding via impersonation
  • Pinned Morpho vault and Velodrome market fixtures for deterministic tests

Harness:

  • Fork lifecycle management (start/stop/snapshot/revert/time-travel)
  • ERC20 funding via whale impersonation, ETH funding via Anvil accounts
  • Balance change and receipt success assertions (explicitly fails on unknown receipt shapes)
  • TestEOAWallet — real EOAWallet subclass for testing the SDK's wallet execution path
  • ForkClient type — inferred from createPublicClient through the same chain resolution path to sidestep TS2719 from pnpm's dual viem resolution (no any casts)

Provider-level network tests

Provider Coverage
UniswapSwapProvider getQuote + execute tx construction on Mainnet, Optimism, Base
VelodromeSwapProvider Refactored to shared harness; multi-pool coverage on Optimism, Base
AaveLendProvider getMarkets + getMarket + openPosition tx construction on Optimism, Base
MorphoLendProvider Pinned to stable vault fixtures; getMarket + getMarkets on Mainnet, Optimism, Base

Namespace-level system tests

Namespace Type Coverage
WalletSwapNamespace Execution Full quote → approve → swap → balance verification (USDC→OP on Optimism)
WalletLendNamespace Execution Open → get → close position lifecycle (Aave USDC on Optimism)
ActionsSwapNamespace Read-only Multi-provider quoting with strength assertion (both Uniswap and Velodrome must contribute)
ActionsLendNamespace Read-only Market discovery with explicit Morpho vault presence + Aave market assertions

Design decisions

  • Provider vs namespace boundary: Provider tests validate protocol-specific reads and tx construction. Namespace tests validate wallet orchestration and on-chain state transitions. Minimal intentional overlap.
  • No any in harness: The ForkClient type is inferred via ReturnType<typeof createForkPublicClient> where createForkPublicClient takes ForkChainConfig['chain'], ensuring type inference flows through the same viem copy that resolves the chain's transaction types.
  • Pinned fixtures over discovery: Morpho tests use canonical vault addresses rather than relying on API discovery order, avoiding flaky tests from upstream market list changes.
  • CI fail-fast: getRpcUrl() throws in CI when env vars are missing rather than silently falling back to rate-limited public RPCs.

What this does NOT cover (remaining #335 scope)

  • Hosted-wallet provider conformance (Privy, Turnkey, Dynamic) — needs credential/infrastructure guidance
  • Smart-wallet (ERC-4337) smoke tests — needs bundler setup
  • Edge cases: existing allowance skip, native ETH path, quote expiration, recipient re-encoding, blocklist enforcement
  • Namespace execution on Mainnet and Base (currently OP only)
  • CI pipeline wiring (Add network fork tests to CI #332 / Parallelize tests #333)

These are planned as follow-up PRs once this foundation is reviewed and merged.

Test plan

  • pnpm typecheck passes (zero errors)
  • pnpm test passes (439 unit tests, no regressions)
  • npx eslint on all changed files: zero errors (3 pre-existing warnings on relative import style)
  • pnpm test:network — requires Anvil + RPC env vars; tested locally against fork RPCs

Closes: Phase 1 of #335

Introduce a shared Anvil-fork test harness and the first layer of e2e
network tests toward ethereum-optimism#335.

Shared harness (src/test/network/):
- fixtures: chain configs with CI-enforced RPC env vars, test assets
  with whale addresses, pinned Morpho vault and Velodrome market fixtures
- harness: fork lifecycle (start/stop/snapshot/revert), ERC20 funding
  via whale impersonation, balance/receipt assertions, TestEOAWallet
  (real EOAWallet subclass), ForkClient type (sidesteps TS2719 from
  pnpm's dual viem resolution without any `any` casts)

Provider-level network tests:
- UniswapSwapProvider: getQuote + execute tx construction on 3 chains
- VelodromeSwapProvider: refactored to shared harness, multi-pool coverage
- AaveLendProvider: getMarkets + getMarket + openPosition tx on OP/Base
- MorphoLendProvider: pinned to stable vault fixtures, 3 chains

Namespace-level system tests:
- WalletSwapNamespace: full quote -> approve -> swap -> balance verification
- WalletLendNamespace: open -> get -> close position lifecycle
- ActionsSwapNamespace: read-only quoting with multi-provider strength
  assertion (both uniswap and velodrome must contribute)
- ActionsLendNamespace: read-only market discovery with explicit Morpho
  vault presence assertion

This is Phase 1 of ethereum-optimism#335. Remaining scope (hosted-wallet conformance,
smart-wallet smoke tests, edge cases, CI wiring) is outlined in the
issue comment thread.
@latifkasuli latifkasuli requested a review from a team as a code owner March 30, 2026 10:11
@latifkasuli latifkasuli requested a review from lucasmoore March 30, 2026 10:11
@netlify
Copy link
Copy Markdown

netlify Bot commented Mar 30, 2026

👷 Deploy request for actions-ui pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit b6d5468

Copy link
Copy Markdown

@its-applekid its-applekid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent E2E Test Infrastructure! ✅

This PR implements comprehensive network fork testing for the Actions SDK - exactly what #335 calls for. Strong work!

Strengths

1. Solid Test Architecture

  • Clean separation: fixtures, harness, test files
  • Fork management with proper lifecycle (beforeAll/afterAll, snapshot/revert)
  • Reusable createProvider helper pattern across all provider tests

2. Coverage

  • Tests both read-only (ActionsLendNamespace) and wallet-based (WalletLendNamespace) namespaces
  • Covers all major providers: Aave, Morpho, Uniswap, Velodrome
  • Tests across multiple chains (Optimism, Base)

3. Real-World Validation

  • Actual contract addresses and market configs
  • Verifies APY, TVL, transaction construction
  • Tests approval flows for ERC20 positions

4. Developer Experience

  • Clear JSDoc headers explain purpose and how to run
  • Generous 60s timeout for fork startup
  • Silent anvil processes (no noise in test output)

Minor Suggestions

1. Error Handling in Fork Startup
Current fork startup retries 30 times silently. Consider:

  • Logging retry attempts after N failures for debugging
  • More descriptive error when anvil binary not found

2. Fork Port Conflicts
The activeForks map reuses existing forks on port collision - good! But consider documenting this behavior in a comment since it's subtle.

3. Test Isolation
Great use of snapshot/revert! Consider adding a test helper that auto-snapshots before each test and reverts after to prevent state leakage between tests.

Questions

  1. Anvil dependency - Should this be documented in package.json scripts or CI setup? (Or is it expected to be globally installed?)
  2. RPC URLs - getRpcUrl reads from env vars - is there a .env.example for local dev setup?

Overall

This is Phase 1 of #335 and it delivers exactly what it promises. Clean, well-structured, and comprehensive. The foundation is solid for future expansion.

Recommendation: Approve and merge. This unlocks network-level testing for the entire SDK.

Great work @latifkasuli! 🚀

- Detect missing anvil binary upfront with a clear install link
- Warn after 10 failed startup retries instead of staying silent
- Document the port-reuse behavior of the activeForks map
- Improve timeout error message with duration and troubleshooting hint
- Add RPC env var examples to .env.test.local.example
@latifkasuli
Copy link
Copy Markdown
Author

Thanks for the thorough review @its-applekid — really appreciate the detailed feedback.

Pushed a follow-up commit (b6d5468) addressing your suggestions and questions:

Answers

1. Anvil dependency:
Anvil is expected to be globally installed via Foundry (curl -L https://foundry.paradigm.xyz | bash && foundryup). The harness now detects a missing anvil binary upfront and throws a clear error with the install link instead of timing out silently. CI setup (installing Foundry in the pipeline) is planned under #332/#333.

2. RPC URLs / .env.example:
Added the three RPC env vars to the existing .env.test.local.example with placeholder Alchemy URLs and a note that dedicated providers are recommended for reliable runs.

Suggestions addressed

Error handling in fork startup:

  • assertAnvilInstalled() now runs before spawning, with a descriptive error pointing to the Foundry install page.
  • After 10 failed readiness retries, a console.warn alerts the developer. The final timeout error now includes the duration and a troubleshooting hint.

Fork port reuse:
Added a comment on the activeForks map explaining why existing forks are reused on port match.

Auto-snapshot test helper:
Good idea — the current tests do manual snapshot/revert in beforeEach/afterEach which is adequate for Phase 1, but a withSnapshot(fork, fn) wrapper would be a natural ergonomic improvement for Phase 2. Noted for follow-up.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants