ethereum-optimism / ethereum-optimism/actions

Consolidate validation and lifecycle abstractions across action providers

Open
#477 1 comment 0 reactions 0 assignees View on GitHub
agent:todo
Dominant language
TypeScript
Stars
32
Forks
25
Avg merge
10h 20m
Merged PRs (30d)
16

Description

## Context

After the borrow PR #3 review consolidation, the three action provider domains (`LendProvider`, `SwapProvider`, `BorrowProvider`) have drifted in how they shape per-action validation, allowlist resolution, and config matching. Borrow is now the most refined (post-`hoist allowlist resolution` commit); lend and swap haven't been brought up to parity.

This issue tracks the larger pass: pull shared logic up into `BaseActionProvider` where it belongs, extract per-domain `validations.ts` modules, and make naming/shape parallel across all three domains.

## Current state

| Concern | Lend | Swap | Borrow (post-cleanup) |
|---|---|---|---|
| Validations module | inline + scattered helpers in `lend/utils/markets.ts` | fully inline in `SwapProvider.ts` | `borrow/core/validations.ts` (6 functions) |
| Protected method name | `validateMarketAllowed` (renamed for parity) | `validateMarketAllowed` | `validateMarketAllowed` ✅ |
| Method scope | bundles chain check + allowlist | allowlist + blocklist only | allowlist + blocklist only |
| Strict lookup-and-return helper | none (returns `undefined`) | `resolveMarketConfig` (returns `undefined`) | `requireAllowlistedMarketConfig` (throws) |
| Allowlist matcher helper | `lendMarketIdMatches` (extracted) | inline closure in `findMatchingConfig` (`SwapProvider.ts:493`) | `marketIdMatches` (extracted) |
| Tampered-config protection on write path | n/a | n/a | resolved in `resolveTrustedBaseParams` (write hooks receive allowlist-trusted config) |
| Walletaddress validation | inline `AddressRequiredError` | `validateNotZeroAddress` inline | `validateBorrowWalletAddress` (deduped 2-step check) |
| Quote validation helpers | n/a (no quotes) | inline (`validateQuoteExpiration`) | `validateQuoteAction` + `validateQuoteNotExpired` |

## Proposed consolidation

### 1. Per-domain `validations.ts` modules
- `lend/core/validations.ts`
- `swap/core/validations.ts`

Move existing scattered helpers in. Mirror borrow's pattern — pure functions taking config rather than `this`.

### 2. Lift to `BaseActionProvider`

`BaseActionProvider` is the right place for cross-domain shared protected methods. Candidates:

- `validateMarketAllowed(...)` — every domain has this with the same intent (allowlist + blocklist). The args differ (`marketId` vs `assetIn/assetOut/chainId` vs `BorrowMarketConfig`), so we'd need either a generic shape or domain-specific overrides. Likely a generic \`validateMarketAllowed(market, matcher)\` accepting a per-domain matcher.
- `requireAllowlistedMarketConfig(...)` — same pattern; strict lookup that throws.
- `validateWalletAddress(walletAddress)` — agnostic; move to `utils/validation.ts` if not already, drop the per-domain wrappers (borrow has \`validateBorrowWalletAddress\` — could become \`validateNonZeroWalletAddress\` shared).

### 3. Per-domain matcher helpers
- `lendMarketIdMatches` (already extracted)
- `marketIdMatches` (borrow, already extracted)
- **New:** `swapMarketPairMatches(assetIn, assetOut, chainId, pair)` — extract from inline closure in \`SwapProvider.ts:493\`.

### 4. Naming / scope alignment
- Lend's \`validateMarketAllowed\` currently bundles chain check + allowlist. Borrow and swap keep them separate. Decide a single convention and apply.
- Swap's \`resolveMarketConfig\` returns \`undefined\`. Borrow's \`requireAllowlistedMarketConfig\` throws. Add a consistent pair (\`findX\` returns, \`requireX\` throws) per domain.

### 5. Tampered-config protection
- Lend write paths (deposit/withdraw) currently trust caller-provided \`asset\` metadata. Audit whether a parallel \`resolveTrustedBaseParams\` is warranted.
- Swap is less exposed because the router doesn't take a marketParams hash — the asset addresses go directly into Permit2 / router calldata. Still worth a one-pass audit.

## Out of scope

- Behavior changes beyond what naming/structure consolidation surfaces.
- Cross-domain abstraction over market-id shape (lend uses `address`, swap uses asset pair, borrow uses `marketId` hash) — these are genuinely different and shouldn't be forced together.

## Acceptance

- All three domains have a \`/core/validations.ts\` module.
- Naming for shared concepts is identical across domains where the shape allows.
- Any logic identical across all three domains lives on \`BaseActionProvider\`.
- No behavioral regressions; existing tests still pass.

## Related history

The borrow consolidation that motivated this issue landed in branch \`kevin/borrow-pr3\` between commits \`0f1e980c\` (validations module) and \`d0b71326\` (lend rename for parity).

Contributor guide

Open the contributing guide

Research direction

Compare BaseActionProvider with lend/core/validations.ts, swap/core/validations.ts, borrow/core/validations.ts, and the provider implementations, starting at SwapProvider.ts:493 and the borrow consolidation commits 0f1e980c and d0b71326. Run the existing test suite while aligning validation modules and shared naming; done means all three domains have validations.ts modules, shared identical logic is centralized, and tests pass without behavioral regressions.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend-api-design
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.