Skip to content

[Refactor] unify create pool validation with zod#90

Open
kushbosamiya wants to merge 6 commits intoStabilityNexus:mainfrom
kushbosamiya:refactor/79-unify-create-pool-validation-zod
Open

[Refactor] unify create pool validation with zod#90
kushbosamiya wants to merge 6 commits intoStabilityNexus:mainfrom
kushbosamiya:refactor/79-unify-create-pool-validation-zod

Conversation

@kushbosamiya
Copy link
Copy Markdown

@kushbosamiya kushbosamiya commented Mar 22, 2026

1. Context

Closes #79.

This PR consolidates Create Pool form validation into one runtime schema using Zod, replacing duplicated manual validation logic spread across the form component and src/lib/validation.ts.

2. Problem

Before this change, validation was fragmented:

  • src/components/Forms/CreateFatePool.tsx: manual regex + parseFloat/isNaN checks inside validateCurrentStep()
  • src/lib/validation.ts: overlapping pool-related validators (validatePoolCreation, fee/symbol/name checks)
  • src/components/Forms/FormData.ts: compile-time interface only, no runtime schema enforcement

Impact:

  • no single source of truth for addresses/fees/step rules
  • drift risk as form steps evolve
  • inconsistent error behavior across files

3. Current Behavior (before)

Reproduction:

  1. Open create pool flow
  2. Trigger step validation for addresses/fees
  3. Observe manual checks in CreateFatePool.tsx while similar rules also existed in src/lib/validation.ts

Evidence:

  • Manual step checks in validateCurrentStep() (address regex + fee parsing)
  • Overlapping pool creation validators in src/lib/validation.ts

4. Proposed Solution

Implemented:

  • Add explicit dependency: zod in package.json
  • Define FormDataSchema + step schemas in src/components/Forms/FormData.ts
    • StepOneFormDataSchema
    • StepTwoFormDataSchema
    • StepThreeFormDataSchema
  • Infer FormData type from Zod schema (z.infer)
  • Refactor validateCurrentStep() in CreateFatePool.tsx to safeParse by step
  • Remove overlapping form-level pool validators from src/lib/validation.ts

5. Problems Encountered After Zod with its Step-by-step Approach

  1. Build instability from stale generated TS references (.next/types/...)
  • Resolution: set incremental: false in tsconfig.json and rebuild cleanly.
  1. next/font/google Geist fetch failure in build environments with restricted network
  • Resolution: remove Google font import from src/app/layout.tsx and use existing global font stack.
  1. Reown fallback noise (DEFAULT_PROJECT_ID causing remote 403 behavior)
  • Resolution: in src/utils/wagmiConfig.ts, disable WalletConnect fallback when project id is missing and only warn outside production.
  1. Lint regressions after moving from next lint to eslint .
  • Resolution:
    • ignore generated/build files in eslint.config.mjs (.next, out, next-env.d.ts)
    • replace require("tailwindcss-animate") with ESM import in tailwind.config.ts
    • update chart tooltip formatter typing in src/app/portfolio/page.tsx to accept Recharts value unions safely
  1. UI lint warning for <img> in token avatar
  • Resolution: migrate to next/image in src/components/ui/TokenImage.tsx.

6. Implementation Details (files)

  • src/components/Forms/FormData.ts: Zod schema + step schemas + inferred form type
  • src/components/Forms/CreateFatePool.tsx: step validation via safeParse
  • src/lib/validation.ts: removed overlapping pool validation utilities
  • package.json, package-lock.json: explicit zod dependency + dependency updates
  • tsconfig.json: incremental: false
  • src/app/layout.tsx: remove next/font/google Geist dependency
  • src/utils/wagmiConfig.ts: safe missing-project-id fallback
  • eslint.config.mjs, tailwind.config.ts, src/components/ui/TokenImage.tsx, src/components/Forms/Steps/PoolConfigurationStep.tsx, src/app/portfolio/page.tsx: lint/type hardening

7. Verification

Executed locally during this branch work:

  • npm run lint
  • npm run build

Result:

  • lint passes
  • build passes with static export output
  • validation now goes through Zod step schemas in Create Pool flow

8. Acceptance Criteria

  • Zod added explicitly as direct dependency
  • Form runtime schema added with inferred TypeScript type
  • Step validation refactored to safeParse
  • Duplicate overlapping pool validation removed
  • No lint/build blockers in the final branch state

Summary by CodeRabbit

  • New Features

    • Centralized Zod-based form validation with i18n validation messages; image component now uses optimized image handling and next.config permits additional remote image hosts.
  • Bug Fixes

    • Chart tooltips consistently format numbers; token selection syncing refined across updates.
  • Style

    • Simplified default typography (removed embedded font imports).
  • Chores

    • Lint script/config updated and dependencies bumped; added review doc.
  • Breaking Changes

    • Legacy validation helper exports removed (callers must use new schemas).

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 22, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Introduce Zod-based per-step form schemas and centralized validation messages; remove duplicate validators; adjust linting and deps; switch Google fonts out; replace native img with Next.js Image; tweak wagmi fallback/config; add Next image remote patterns; coerce chart tooltip values to numbers.

Changes

Cohort / File(s) Summary
Form schemas & i18n
src/components/Forms/FormData.ts, src/lib/i18n/validationMessages.ts
Add Zod schemas (per-step) and derived FormData type; introduce centralized validation message constants.
Form integration
src/components/Forms/CreateFatePool.tsx, src/components/Forms/Steps/PoolConfigurationStep.tsx
Refactor step validation to use schema.safeParse() and map Zod issues to errors; remove token auto-selection from token-load effect.
Removed validators
src/lib/validation.ts
Remove exported helpers that duplicated form validation (symbol/name/fee/pool creation validators).
Linting & deps
eslint.config.mjs, package.json
Add flat ESLint ignore patterns; change lint script to eslint .; add zod dependency; bump Next.js and eslint-config-next.
Next image config
next.config.mjs
Add images.remotePatterns for Coingecko hosts to allow remote image optimization.
Layout & fonts
src/app/layout.tsx
Remove next/font/google Geist imports and variables; simplify body className to "antialiased".
UI image & charts
src/components/ui/TokenImage.tsx, src/app/portfolio/page.tsx
Replace native <img> with Next.js <Image> and size mapping; coerce Recharts Tooltip value to numeric before formatting.
Wagmi config
src/utils/wagmiConfig.ts
Change fallback when NEXT_PUBLIC_PROJECT_ID missing to warn only outside production; return createConfig using injected() connectors and explicit http() transports; adjust memo typing and imports.
Tailwind plugin import
tailwind.config.ts
Switch plugin loading to ES module import (tailwindcss-animate).
ESLint flat config
eslint.config.mjs
Add top-level ignores for .next/**, out/**, node_modules/**, next-env.d.ts.
Docs
01-REVIEWS.md
Add review document summarizing form validation consolidation, strengths, concerns, and action items.

Sequence Diagram(s)

sequenceDiagram
  participant User as "User (browser)"
  participant Form as "CreateFatePool component"
  participant Schema as "Zod Step Schema"
  participant Messages as "validationMessages"
  participant UI as "Form UI (errors)"

  User->>Form: submit current step
  Form->>Schema: schema.safeParse(formData)
  Schema-->>Form: success / error issues
  alt success
    Form->>UI: advance step / clear errors
  else error
    Form->>Messages: map issues -> localized keys
    Messages-->>Form: message strings
    Form->>UI: display field errors
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Typescript Lang, Documentation, Linter

Poem

🐰
I hop through schemas, tidy and keen,
Zod trims the fields, keeps values clean.
Images sharpen, configs align,
Errors map to messages, clear and fine.
Small hops, big leaps — the code feels serene.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title '[Refactor] unify create pool validation with zod' directly and accurately summarizes the main change: consolidating scattered validation logic using Zod schemas.
Linked Issues check ✅ Passed All four coding objectives from issue #79 are fulfilled: zod added to dependencies, FormDataSchema with z.infer in FormData.ts, validateCurrentStep refactored to use safeParse, and overlapping validators removed from validation.ts.
Out of Scope Changes check ✅ Passed Several changes address post-migration build/lint issues (tsconfig.json incremental, layout.tsx font removal, wagmiConfig.ts fallback, eslint/tailwind updates, TokenImage/portfolio updates) that are necessary to resolve blockers from the refactor, not out of scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Forms/Steps/PoolConfigurationStep.tsx (1)

43-63: ⚠️ Potential issue | 🟡 Minor

Unnecessary token reload on baseTokenAddress change.

Adding formData.baseTokenAddress to this dependency array causes loadTokensForChain(chainId) to re-run whenever the base token address changes, even though the token list depends only on chainId. The second useEffect (lines 66-73) already handles updating selectedToken when baseTokenAddress changes.

This creates redundant async calls and could cause unnecessary re-renders. Consider removing formData.baseTokenAddress from this dependency array.

🐛 Proposed fix
     loadTokens();
-  }, [chainId, formData.baseTokenAddress]);
+  }, [chainId]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Forms/Steps/PoolConfigurationStep.tsx` around lines 43 - 63,
The useEffect that defines loadTokens currently lists formData.baseTokenAddress
in its dependency array causing unnecessary reloads; remove
formData.baseTokenAddress from that dependency array so the effect only depends
on chainId, since loadTokensForChain(chainId) and setAvailableTokens only need
chainId, and the separate effect that watches formData.baseTokenAddress (which
calls setSelectedToken) will handle updating the selection; update the
dependency array on the useEffect that contains loadTokens (and referenced
functions loadTokensForChain, loadTokens, setAvailableTokens,
setIsLoadingTokens) to [chainId] only.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@package.json`:
- Line 39: The package.json dependency for "zod" is pinned to v3 ("zod":
"^3.25.76"); update that version constraint to a v4 range (for example "zod":
"^4.3.6" or "^4.0.0") in package.json to allow zod v4, then run npm/yarn install
and your test suite; if any breaking changes surface, update usages of Zod APIs
(imports/usages of Zod functions/classes) to match v4 semantics.

In `@src/app/layout.tsx`:
- Line 149: package.json currently lists the unused dependency "geist"
(v^1.7.0); remove the "geist" entry from dependencies in package.json, reinstall
or run your package manager's install (npm/yarn/pnpm) to update node_modules and
the lockfile, and run a quick grep/IDE search for any remaining "geist" imports
or references (e.g., in layout.tsx, globals.css, tailwind.config.ts) to confirm
nothing else needs deleting.

In `@src/components/Forms/FormData.ts`:
- Around line 37-118: Extract all user-facing validation strings used in the Zod
validation block (the messages passed to ctx.addIssue for fields poolName,
baseTokenAddress, initialDeposit, priceFeedAddress, hebeswapPairAddress,
hebeswapQuoteToken, and any duplicates later in the file) into your i18n
resource files/constants and replace the literal strings with lookups; keep the
validation logic (ETH_ADDRESS_REGEX checks, Number parsing for initialDeposit,
and the conditional oracleType checks) intact and reference the i18n keys when
calling ctx.addIssue so copy is centralized and localizable.
- Around line 66-79: The validation currently uses Number.isNaN(deposit) which
lets non-finite numeric strings slip through; update the guard in the
initialDeposit validation to use Number.isFinite by checking
!Number.isFinite(deposit) so that NaN and non-finite values (e.g., Infinity) are
rejected before the negative check; keep the existing ctx.addIssue calls (path:
["initialDeposit"], code: z.ZodIssueCode.custom) and only replace the first
condition that references deposit.
- Around line 157-162: FeeStepSchema currently allows trimmed empty strings
because Number("") -> 0 and the numeric check passes; update the schema for
mintFee, burnFee, creatorFee, and treasuryFee to reject empty strings and ensure
they are numeric and positive (or non-zero as required). Replace
z.string().trim() with either z.string().trim().nonempty().refine(s =>
Number.isFinite(Number(s)) && Number(s) > 0, { message: 'must be a positive
number' }) or use z.preprocess to coerce to number and validate with
z.number().finite().positive(), ensuring the schema (FeeStepSchema) enforces
non-empty, numeric, and >0 values for all four fee fields.

In `@src/components/ui/TokenImage.tsx`:
- Around line 39-47: Remove the redundant unoptimized prop from the Image JSX in
TokenImage (the <Image ... /> element that uses src, alt,
width={sizePixels[size]}, height={sizePixels[size]}, className and onError={()
=> setHasError(true)}). Edit the TokenImage component to delete the unoptimized
attribute so the Image relies on the global images.unoptimized setting in
next.config.mjs; leave the other props (src, alt, width/height, className,
onError and setHasError) unchanged.

In `@tsconfig.json`:
- Line 19: The tsconfig setting "incremental": false was added to avoid stale
type references but harms build performance; revert "incremental" back to true
in tsconfig and instead diagnose and fix the root cause by deleting any stale
.tsbuildinfo files, running a full clean build (tsc --build or rm .tsbuildinfo
then tsc), and verifying compilerOptions and path alias consistency (baseUrl,
paths, composite/project references) across tsconfig(s) so incremental
compilation can run reliably without producing stale caches.

---

Outside diff comments:
In `@src/components/Forms/Steps/PoolConfigurationStep.tsx`:
- Around line 43-63: The useEffect that defines loadTokens currently lists
formData.baseTokenAddress in its dependency array causing unnecessary reloads;
remove formData.baseTokenAddress from that dependency array so the effect only
depends on chainId, since loadTokensForChain(chainId) and setAvailableTokens
only need chainId, and the separate effect that watches
formData.baseTokenAddress (which calls setSelectedToken) will handle updating
the selection; update the dependency array on the useEffect that contains
loadTokens (and referenced functions loadTokensForChain, loadTokens,
setAvailableTokens, setIsLoadingTokens) to [chainId] only.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3a7681a2-0ef3-4877-b222-e52b85005c64

📥 Commits

Reviewing files that changed from the base of the PR and between fbd27aa and ac1d7e3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • eslint.config.mjs
  • package.json
  • src/app/layout.tsx
  • src/app/portfolio/page.tsx
  • src/components/Forms/CreateFatePool.tsx
  • src/components/Forms/FormData.ts
  • src/components/Forms/Steps/PoolConfigurationStep.tsx
  • src/components/ui/TokenImage.tsx
  • src/lib/validation.ts
  • src/utils/wagmiConfig.ts
  • tailwind.config.ts
  • tsconfig.json
💤 Files with no reviewable changes (1)
  • src/lib/validation.ts

Comment thread package.json Outdated
Comment thread src/app/layout.tsx
Comment thread src/components/Forms/FormData.ts
Comment thread src/components/Forms/FormData.ts
Comment thread src/components/ui/TokenImage.tsx
Comment thread tsconfig.json Outdated
### 1. Context
Closes StabilityNexus#79. Replaced manual string-based error messages with a centralized i18n object (`validationMessages`) for the pool configuration validation step to prevent hardcoded duplication. Also updated Zod to version 4 to resolve validation bugs and enabled incremental compilation for faster TS builds.

### 2. Implementation Details
- `src/components/Forms/FormData.ts`: Centralized all string validations using `msg` from `validationMessages`. Changed `Number.isNaN` to `Number.isFinite` for accurate validation.
- `src/lib/i18n/validationMessages.ts`: Extracted and grouped all UI text constants.
- `src/components/Forms/Steps/PoolConfigurationStep.tsx`: Fixed redundant dependencies in `useEffect` list.
- `src/components/ui/TokenImage.tsx`: Removed redundant `unoptimized` flag on NextJS Image component.
- `package.json` & `tsconfig.json`: Bumped `zod` dependency to `^4.0.0` to support schema rules and removed `geist` font, enabled `incremental` compiler switch.

### 3. Verification
- Verified local syntax logic with Typescript linting passes.
- Confirmed UI logic for new pool components remains regression-free.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Forms/FormData.ts`:
- Around line 159-188: The per-field finitude/negativity checks should be
removed from StepThreeFormDataSchema.superRefine so it only enforces the
aggregate totalFee >= 100 rule; consolidate all per-field validations into
FeeStepSchema (ensure mintFee, burnFee, creatorFee, treasuryFee each use
.string().trim().nonempty(...).refine(...) for finite and >0 with the intended
messages) and update error messages there as needed; after this change, leave
StepThreeFormDataSchema.superRefine to only compute the numeric sum of those
four fields and add an issue on "totalFee" when sum < 100; ensure this aligns
with the CreateFatePool.tsx error handler that preserves the first error per
field.

In `@src/components/ui/TokenImage.tsx`:
- Around line 39-43: TokenImage uses Next.js Image with external URLs (see the
Image usage in TokenImage component and src prop) but those hosts aren’t
allowlisted, and hostname validation still runs even with unoptimized:true;
update Next.js image config to add remotePatterns that allow
coin-images.coingecko.com and static.coingecko.com (include both http and https
patterns or a protocol wildcard and the hostnames) so the Image component can
load external token images without failing at config time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9db8866f-55cd-49f7-a82f-812dcaaa9a62

📥 Commits

Reviewing files that changed from the base of the PR and between ac1d7e3 and 8dc5cf2.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • package.json
  • src/components/Forms/FormData.ts
  • src/components/Forms/Steps/PoolConfigurationStep.tsx
  • src/components/ui/TokenImage.tsx
  • src/lib/i18n/validationMessages.ts

Comment on lines +7 to +26
export const FormDataSchema = z.object({
poolName: z.string().trim(),
baseTokenAddress: z.string().trim(),
oracleType: z.enum(["chainlink", "hebeswap"]),
priceFeedAddress: z.string().trim(),
hebeswapPairAddress: z.string().trim(),
hebeswapQuoteToken: z.string().trim(),
bullCoinName: z.string().trim(),
bullCoinSymbol: z.string().trim(),
bearCoinName: z.string().trim(),
bearCoinSymbol: z.string().trim(),
creatorAddress: z.string().trim(),
mintFee: z.string().trim(),
burnFee: z.string().trim(),
creatorFee: z.string().trim(),
treasuryFee: z.string().trim(),
initialDeposit: z.string().trim(),
quoteTokenAddress: z.string().trim().optional(),
oracleDescription: z.string().trim().optional(),
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C3 'safeParse\(formData\)|result\.success|result\.error\.issues' src/components/Forms/CreateFatePool.tsx
printf '\n[result.data usage]\n'
rg -n 'result\.data' src/components/Forms/CreateFatePool.tsx || true
printf '\n[downstream raw formData consumers]\n'
rg -n -C2 'formData\.' src/components/Forms/Steps/ReviewStep.tsx

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 5229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if FormData type is derived from schema
printf '[FormData type definition]\n'
rg -n 'type FormData|export type FormData' src/components/Forms/FormData.ts -A2

# Check form input handling for trim
printf '\n[Form input onChange handling]\n'
rg -n 'onChange|setFormData' src/components/Forms/CreateFatePool.tsx -B1 -A2 | head -50

# Check submission/creation function
printf '\n[Submission and pool creation]\n'
rg -n 'createPool|mutation|submit' src/components/Forms/CreateFatePool.tsx -B2 -A3 | head -60

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 1213


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the full createPool arguments to see which fields are trimmed
printf '[Full createPool call with all arguments]\n'
sed -n '470,510p' src/components/Forms/CreateFatePool.tsx

# Also check if there are any other submission paths
printf '\n[Other uses of formData beyond createPool]\n'
rg -n 'formData\.' src/components/Forms/CreateFatePool.tsx | grep -E '(submit|deploy|send|create)' || true

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 1688


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how fee fields are converted to BigInt
printf '[Fee field conversion logic]\n'
rg -n 'mintFee|burnFee|creatorFee|treasuryFee' src/components/Forms/CreateFatePool.tsx -B2 -A2 | grep -E '(Units|BigInt|parseFloat|parseUnits)' -A2 -B2

# Check if baseTokenAddress and other addresses are trimmed anywhere
printf '\n[Address field handling before submission]\n'
rg -n 'baseTokenAddress|priceFeedAddress|hebeswapPairAddress' src/components/Forms/CreateFatePool.tsx -B1 -A1 | head -40

# Check the full fee conversion section
printf '\n[Full fee conversion logic around submission]\n'
sed -n '450,480p' src/components/Forms/CreateFatePool.tsx

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 3842


🏁 Script executed:

#!/bin/bash
set -euoripgrep

# Check hebeswapPairAddress and hebeswapQuoteToken handling
printf '[Hebeswap address field handling]\n'
rg -n 'hebeswapPairAddress|hebeswapQuoteToken' src/components/Forms/CreateFatePool.tsx -B1 -A1 | head -40

# Check if priceFeedAddress is trimmed
printf '\n[priceFeedAddress trimming check]\n'
sed -n '249,260p' src/components/Forms/CreateFatePool.tsx

# Verify all args passed to createPool - check if any address/fee fields are missing trim
printf '\n[Complete createPool args]\n'
sed -n '473,486p' src/components/Forms/CreateFatePool.tsx

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 750


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check hebeswapPairAddress and hebeswapQuoteToken handling
printf '[Hebeswap address field handling]\n'
rg -n 'hebeswapPairAddress|hebeswapQuoteToken' src/components/Forms/CreateFatePool.tsx -B1 -A1 | head -40

# Check if priceFeedAddress is trimmed
printf '\n[priceFeedAddress trimming check]\n'
sed -n '249,260p' src/components/Forms/CreateFatePool.tsx

# Verify all args passed to createPool - check if any address/fee fields are missing trim
printf '\n[Complete createPool args]\n'
sed -n '473,486p' src/components/Forms/CreateFatePool.tsx

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 2250


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the full flow of fee field handling at submission
printf '[Fee field submission without trim]\n'
sed -n '230,240p' src/components/Forms/CreateFatePool.tsx

# Check quoteTokenAddress (optional) handling
printf '\n[quoteTokenAddress/initialDeposit handling]\n'
rg -n 'quoteTokenAddress|initialDeposit|initialDepositAmount' src/components/Forms/CreateFatePool.tsx -B1 -A1 | head -40

# Check oracleDescription field
printf '\n[oracleDescription usage]\n'
rg -n 'oracleDescription' src/components/Forms/CreateFatePool.tsx -B1 -A1 || echo "Not found in CreateFatePool"

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 2476


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if fees fields have any trimming elsewhere in submission flow
printf '[All fee field uses at submission]\n'
sed -n '233,237p' src/components/Forms/CreateFatePool.tsx

# Check if quoteTokenAddress field is optional or required
printf '\n[quoteTokenAddress optional field handling]\n'
rg -n 'quoteTokenAddress' src/components/Forms/FormData.ts -B2 -A2

# Summary: list all fields with their trim status at submission
printf '\n[Field trim status at createPool call]\n'
sed -n '473,486p' src/components/Forms/CreateFatePool.tsx

Repository: StabilityNexus/Fate-EVM-Frontend

Length of output: 1618


Trim operations in FormDataSchema are ignored; only some fields are trimmed at submission, creating inconsistency.

The schema defines .trim() on all string fields, but safeParse() result is never merged back into state. More critically, at submission time only certain fields are manually trimmed (baseTokenAddress, poolName, coin names), while many are used untrimmed: fee fields (parsed via parseFloat(formData.mintFee)), address fields like priceFeedAddress and hebeswapPairAddress, and initialDeposit.

This inconsistency causes two problems:

  1. Review display reads untrimmed values — ReviewStep displays raw formData fields, showing any leading/trailing whitespace
  2. Address validation is vulnerable — lines 250 and 324 compare untrimmed user input directly against "0x0000...", so " 0x0000... " would pass the check

Either remove .trim() from the schema or apply it consistently at submission or on input change.

Comment thread src/components/ui/TokenImage.tsx
Update StepThreeFormDataSchema and FeeStepSchema to strictly check Number > 0 for fees, and validate aggregate totalFee >= 100 on 'totalFee' path. Add coin-images.coingecko.com and static.coingecko.com to Next.js images remotePatterns.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Forms/FormData.ts`:
- Around line 84-90: The Chainlink branch currently only checks presence of
priceFeedAddress; update the validation in the block that checks if
(data.oracleType === "chainlink" && !data.priceFeedAddress) to also validate the
address format using the existing ETH_ADDRESS_REGEX (same regex used for other
on-chain address fields) and call ctx.addIssue with path ["priceFeedAddress"]
and the same custom error behavior when the regex test fails; locate the
conditional around priceFeedAddress and replace the simple presence check with a
regex guard so malformed feed addresses are rejected before submission.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: b985042c-ea8a-4e55-aa4a-11b0c0d47daf

📥 Commits

Reviewing files that changed from the base of the PR and between 8dc5cf2 and 9b89559.

📒 Files selected for processing (2)
  • next.config.mjs
  • src/components/Forms/FormData.ts

Comment thread src/components/Forms/FormData.ts Outdated
Update StepOneFormDataSchema to enforce ETH_ADDRESS_REGEX on the Chainlink priceFeedAddress field, rejecting malformed addresses.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/components/Forms/FormData.ts (1)

7-26: ⚠️ Potential issue | 🟠 Major

Schema normalization is not guaranteed to be the canonical payload.

FormDataSchema now normalizes with .trim(), but downstream flow still uses raw formData in places instead of parsed output. That creates validation/display/submission drift (validated trimmed values vs submitted untrimmed values). Please make parsed data (safeParse(...).data) the single source of truth after step validation, or remove normalization transforms here to avoid false guarantees.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Forms/FormData.ts` around lines 7 - 26, FormDataSchema
currently trims inputs but callers still use the raw formData, causing mismatch
between validated/normalized values and what gets displayed/submitted; update
the flow that handles validation to use FormDataSchema.safeParse(...)'s parsed
output as the single source of truth (replace downstream uses of raw formData
with the safeParse result.data after successful validation) wherever form
submission/preview/display occurs, or alternatively remove the .trim()
transforms on FormDataSchema to avoid implying normalization—look for usages of
FormDataSchema, safeParse, formData, and places that read fields like
poolName/priceFeedAddress/quoteTokenAddress to apply the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/components/Forms/FormData.ts`:
- Around line 167-196: The four identical fee field definitions in FeeStepSchema
should be deduplicated by extracting a shared schema (e.g., feeStringSchema)
that encapsulates the chain .string().trim().nonempty({ message:
msg.invalidFeeValue }).refine((s) => Number.isFinite(Number(s)) && Number(s) >
0, { message: msg.positiveFeeRequired }), then reuse that schema for mintFee,
burnFee, creatorFee, and treasuryFee inside FeeStepSchema to ensure consistent
validation and messages across all fields.

---

Duplicate comments:
In `@src/components/Forms/FormData.ts`:
- Around line 7-26: FormDataSchema currently trims inputs but callers still use
the raw formData, causing mismatch between validated/normalized values and what
gets displayed/submitted; update the flow that handles validation to use
FormDataSchema.safeParse(...)'s parsed output as the single source of truth
(replace downstream uses of raw formData with the safeParse result.data after
successful validation) wherever form submission/preview/display occurs, or
alternatively remove the .trim() transforms on FormDataSchema to avoid implying
normalization—look for usages of FormDataSchema, safeParse, formData, and places
that read fields like poolName/priceFeedAddress/quoteTokenAddress to apply the
fix.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a9889c73-9420-4d34-9729-17552cba689d

📥 Commits

Reviewing files that changed from the base of the PR and between 9b89559 and fe03f38.

📒 Files selected for processing (1)
  • src/components/Forms/FormData.ts

Comment on lines +167 to +196
const FeeStepSchema = z.object({
mintFee: z
.string()
.trim()
.nonempty({ message: msg.invalidFeeValue })
.refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
message: msg.positiveFeeRequired,
}),
burnFee: z
.string()
.trim()
.nonempty({ message: msg.invalidFeeValue })
.refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
message: msg.positiveFeeRequired,
}),
creatorFee: z
.string()
.trim()
.nonempty({ message: msg.invalidFeeValue })
.refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
message: msg.positiveFeeRequired,
}),
treasuryFee: z
.string()
.trim()
.nonempty({ message: msg.invalidFeeValue })
.refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
message: msg.positiveFeeRequired,
}),
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Deduplicate fee field schema to reduce drift risk.

The four fee fields repeat identical chains. Extract a shared fee-string schema and reuse it to keep future message/rule changes consistent.

♻️ Proposed refactor
+const PositiveFeeStringSchema = z
+  .string()
+  .trim()
+  .nonempty({ message: msg.invalidFeeValue })
+  .refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
+    message: msg.positiveFeeRequired,
+  });
+
 const FeeStepSchema = z.object({
-  mintFee: z
-    .string()
-    .trim()
-    .nonempty({ message: msg.invalidFeeValue })
-    .refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
-      message: msg.positiveFeeRequired,
-    }),
-  burnFee: z
-    .string()
-    .trim()
-    .nonempty({ message: msg.invalidFeeValue })
-    .refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
-      message: msg.positiveFeeRequired,
-    }),
-  creatorFee: z
-    .string()
-    .trim()
-    .nonempty({ message: msg.invalidFeeValue })
-    .refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
-      message: msg.positiveFeeRequired,
-    }),
-  treasuryFee: z
-    .string()
-    .trim()
-    .nonempty({ message: msg.invalidFeeValue })
-    .refine((s) => Number.isFinite(Number(s)) && Number(s) > 0, {
-      message: msg.positiveFeeRequired,
-    }),
+  mintFee: PositiveFeeStringSchema,
+  burnFee: PositiveFeeStringSchema,
+  creatorFee: PositiveFeeStringSchema,
+  treasuryFee: PositiveFeeStringSchema,
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/Forms/FormData.ts` around lines 167 - 196, The four identical
fee field definitions in FeeStepSchema should be deduplicated by extracting a
shared schema (e.g., feeStringSchema) that encapsulates the chain
.string().trim().nonempty({ message: msg.invalidFeeValue }).refine((s) =>
Number.isFinite(Number(s)) && Number(s) > 0, { message: msg.positiveFeeRequired
}), then reuse that schema for mintFee, burnFee, creatorFee, and treasuryFee
inside FeeStepSchema to ensure consistent validation and messages across all
fields.

@kushbosamiya kushbosamiya changed the title [codex] unify create pool validation with zod [Refactor] unify create pool validation with zod Mar 23, 2026
@kushbosamiya
Copy link
Copy Markdown
Author

close #79 , @blizet @Zahnentferner I have implemented and tested the zod validation could you please review and merge it at your convenience .

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@01-REVIEWS.md`:
- Line 28: The review flagged ETH_ADDRESS_REGEX in FormData.ts but the project
intentionally prefers smart-contract-side validation (see CreateFatePool) so
update the review comment and FormData.ts note to reflect that regex is used
only for lightweight form-format validation and checksum validation is delegated
to the smart contract; mention ETH_ADDRESS_REGEX, isAddress() (viem) as an
available alternative, but explicitly state the chosen architecture (smart
contract authoritative validation) and rationale to avoid recommending adding
viem.isAddress() in the frontend.
- Line 17: Replace the phrase "Three step schemas" with the hyphenated compound
"three-step schemas" wherever it appears as a modifier (e.g., the review line
that starts with "Three step schemas properly isolate...") so the grammar uses
the correct compound adjective form.
- Around line 37-48: The markdown contains fenced code blocks without blank
lines before and after which triggers lint warnings; update the markdown so each
fenced code block (the example importing isAddress, the poolName zod snippet,
and the closing example around the FormData note) has a blank line immediately
before the opening ``` and a blank line immediately after the closing ```,
ensure language tags (e.g., ```typescript) are present where appropriate, and
verify each block is properly opened and closed around the symbols mentioned
(isAddress import example, poolName zod snippet, and the FormData totalFee note)
so the linter no longer flags formatting errors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: bc2e6805-6094-456a-855a-df813ed3d79f

📥 Commits

Reviewing files that changed from the base of the PR and between fe03f38 and 2eef288.

📒 Files selected for processing (1)
  • 01-REVIEWS.md

Comment thread 01-REVIEWS.md Outdated

### 2. Strengths

- **Good schema organization**: Three step schemas properly isolate validation concerns per form step (`FormData.ts:30-129`, `131-165`, `167-212`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Minor grammar: use hyphen for compound modifier.

"Three step schemas" should be "three-step schemas" when used as a compound modifier before a noun.

-- **Good schema organization**: Three step schemas properly isolate validation concerns per form step (`FormData.ts:30-129`, `131-165`, `167-212`)
+- **Good schema organization**: Three-step schemas properly isolate validation concerns per form step (`FormData.ts:30-129`, `131-165`, `167-212`)
🧰 Tools
🪛 LanguageTool

[grammar] ~17-~17: Use a hyphen to join words.
Context: ...s - Good schema organization: Three step schemas properly isolate validation...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@01-REVIEWS.md` at line 17, Replace the phrase "Three step schemas" with the
hyphenated compound "three-step schemas" wherever it appears as a modifier
(e.g., the review line that starts with "Three step schemas properly
isolate...") so the grammar uses the correct compound adjective form.

Comment thread 01-REVIEWS.md Outdated
| Severity | Issue | Location |
|----------|-------|----------|
| **HIGH** | Zod v4 specified but v3 likely installed (package-lock shows v3.22.4/3.25.x) — `safeParse` API differs significantly between versions | `package.json:38` |
| **HIGH** | ETH_ADDRESS_REGEX only checks format, not checksum — vulnerable to case-sensitive address bugs | `FormData.ts:4,53,91,107,121` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Reconsider the address validation recommendation.

The review flags ETH_ADDRESS_REGEX as a security gap and recommends using viem's isAddress() for checksum validation. However, this conflicts with the project's established preference for smart contract validation over duplicating validation logic in the frontend.

For basic format validation at the form level, a regex check is typically sufficient, especially when the smart contract will perform the authoritative validation. Adding checksum validation in the frontend may be redundant given this architecture. Based on learnings, in the CreateFatePool component, validation for assetId format is handled by the smart contract rather than frontend validation, and the user prefers smart contract validation over duplicating validation logic in the frontend.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@01-REVIEWS.md` at line 28, The review flagged ETH_ADDRESS_REGEX in
FormData.ts but the project intentionally prefers smart-contract-side validation
(see CreateFatePool) so update the review comment and FormData.ts note to
reflect that regex is used only for lightweight form-format validation and
checksum validation is delegated to the smart contract; mention
ETH_ADDRESS_REGEX, isAddress() (viem) as an available alternative, but
explicitly state the chosen architecture (smart contract authoritative
validation) and rationale to avoid recommending adding viem.isAddress() in the
frontend.

Comment thread 01-REVIEWS.md Outdated
Comment on lines +37 to +48
1. **Fix Zod version**: Verify actual installed version and test `safeParse` compatibility, or downgrade to `"zod": "^3.24.0"` for consistency with installed v3
2. **Replace regex with viem**: Use viem's `isAddress` from `validation.ts` for robust checksum validation:
```typescript
import { isAddress } from 'viem';
// Replace ETH_ADDRESS_REGEX.test() with isAddress()
```
3. **Add field length limits**:
```typescript
poolName: z.string().trim().min(1).max(100),
```
4. **Fix total fee logic**: Change `totalFee >= 100` to `totalFee > 100` at `FormData.ts:205`
5. **Add upper bound validation** for initialDeposit (e.g., max 10^18 for ETH denomination)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Fix markdown formatting around code blocks.

The code examples are missing blank lines before and after the fenced code blocks, which violates markdown best practices and triggers linting warnings.

📝 Proposed formatting fixes
 1. **Fix Zod version**: Verify actual installed version and test `safeParse` compatibility, or downgrade to `"zod": "^3.24.0"` for consistency with installed v3
 2. **Replace regex with viem**: Use viem's `isAddress` from `validation.ts` for robust checksum validation:
+
    ```typescript
    import { isAddress } from 'viem';
    // Replace ETH_ADDRESS_REGEX.test() with isAddress()
    ```
+
 3. **Add field length limits**:
+
    ```typescript
    poolName: z.string().trim().min(1).max(100),
    ```
+
 4. **Fix total fee logic**: Change `totalFee >= 100` to `totalFee > 100` at `FormData.ts:205`
 5. **Add upper bound validation** for initialDeposit (e.g., max 10^18 for ETH denomination)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
1. **Fix Zod version**: Verify actual installed version and test `safeParse` compatibility, or downgrade to `"zod": "^3.24.0"` for consistency with installed v3
2. **Replace regex with viem**: Use viem's `isAddress` from `validation.ts` for robust checksum validation:
```typescript
import { isAddress } from 'viem';
// Replace ETH_ADDRESS_REGEX.test() with isAddress()
```
3. **Add field length limits**:
```typescript
poolName: z.string().trim().min(1).max(100),
```
4. **Fix total fee logic**: Change `totalFee >= 100` to `totalFee > 100` at `FormData.ts:205`
5. **Add upper bound validation** for initialDeposit (e.g., max 10^18 for ETH denomination)
1. **Fix Zod version**: Verify actual installed version and test `safeParse` compatibility, or downgrade to `"zod": "^3.24.0"` for consistency with installed v3
2. **Replace regex with viem**: Use viem's `isAddress` from `validation.ts` for robust checksum validation:
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)

[warning] 39-39: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 42-42: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 44-44: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 46-46: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@01-REVIEWS.md` around lines 37 - 48, The markdown contains fenced code blocks
without blank lines before and after which triggers lint warnings; update the
markdown so each fenced code block (the example importing isAddress, the
poolName zod snippet, and the closing example around the FormData note) has a
blank line immediately before the opening ``` and a blank line immediately after
the closing ```, ensure language tags (e.g., ```typescript) are present where
appropriate, and verify each block is properly opened and closed around the
symbols mentioned (isAddress import example, poolName zod snippet, and the
FormData totalFee note) so the linter no longer flags formatting errors.

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.

feat: Add Zod for form validation in pool creation

1 participant