Skip to content

Consolidate validation and lifecycle abstractions across action providers #477

Description

@its-everdred

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<TConfig, TSettings> 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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:todoFollow-up task suitable for agent implementation

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions