Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ const compat = new FlatCompat({
});

const eslintConfig = [
{
ignores: [".next/**", "out/**", "node_modules/**", "next-env.d.ts"],
},
...compat.extends("next/core-web-vitals", "next/typescript"),
];

Expand Down
18 changes: 18 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@ const nextConfig = {
distDir: 'out',
images: {
unoptimized: true,
remotePatterns: [
{
protocol: "https",
hostname: "coin-images.coingecko.com",
},
{
protocol: "https",
hostname: "static.coingecko.com",
},
{
protocol: "http",
hostname: "coin-images.coingecko.com",
},
{
protocol: "http",
hostname: "static.coingecko.com",
},
],
},
webpack: (config) => {
config.resolve.fallback = { fs: false, net: false, tls: false };
Expand Down
2,427 changes: 1,439 additions & 988 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "eslint ."
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.15",
Expand All @@ -25,7 +25,7 @@
"ethers": "^6.15.0",
"framer-motion": "^12.23.25",
"lucide-react": "^0.475.0",
"next": "^15.5.6",
"next": "^15.5.14",
"next-themes": "^0.4.6",
"react": "^19.2.0",
"react-dom": "^19.2.0",
Expand All @@ -34,15 +34,16 @@
"tailwind-merge": "^3.4.0",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.40.3",
"wagmi": "^2.19.5"
"wagmi": "^2.19.5",
"zod": "^4.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "^3.3.3",
"@types/node": "^20.19.25",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
"eslint": "^9.39.1",
"eslint-config-next": "^15.1.7",
"eslint-config-next": "^15.5.14",
"pino-pretty": "^13.1.3",
"postcss": "^8.5.6",
"tailwindcss": "^3.4.18",
Expand Down
16 changes: 3 additions & 13 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { ClientProviders } from "@/components/layout/Layout";
import Navbar from "@/components/layout/Navbar";
import FooterWrapper from "@/components/layout/FooterWrapper";
import { ErrorBoundary } from "@/components/ErrorBoundary";


const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});

const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});

export const metadata: Metadata = {
title: {
default: "Fate Protocol",
Expand Down Expand Up @@ -157,7 +146,7 @@ export default function RootLayout({
<meta name="theme-color" content="#000000" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
<body className="antialiased">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<ErrorBoundary>
<ClientProviders>
{/* Absolute positioned navbar to avoid affecting hero positioning */}
Expand All @@ -175,4 +164,5 @@ export default function RootLayout({
</body>
</html>
);
}
}

28 changes: 21 additions & 7 deletions src/app/portfolio/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1397,13 +1397,20 @@ const PositionChart = ({
boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1)",
color: "#000",
}}
formatter={(value: number) => [
`${value.toLocaleString(undefined, {
formatter={(value) => {
const numericValue =
typeof value === "number"
? value
: Number(value ?? 0);

return [
`${numericValue.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 4,
})} ${data[0]?.baseTokenSymbol || 'UNKNOWN'}`,
type === "bull" ? "Bull Value" : "Bear Value",
]}
];
}}
/>
<Bar dataKey={dataKey} radius={[6, 6, 0, 0]}>
{data.map((entry, index) => (
Expand Down Expand Up @@ -1436,13 +1443,20 @@ const PositionChart = ({
))}
</Pie>
<Tooltip
formatter={(value: number) => [
`${value.toLocaleString(undefined, {
formatter={(value) => {
const numericValue =
typeof value === "number"
? value
: Number(value ?? 0);

return [
`${numericValue.toLocaleString(undefined, {
minimumFractionDigits: 0,
maximumFractionDigits: 4,
})} ${data[0]?.baseTokenSymbol || 'UNKNOWN'}`,
type === "bull" ? "Bull Value" : "Bear Value",
]}
];
}}
contentStyle={{
backgroundColor: "rgba(255, 255, 255, 0.95)",
border: "1px solid #e5e5e5",
Expand Down Expand Up @@ -2771,4 +2785,4 @@ export default function PortfolioPage() {
</div>
</div>
);
}
}
100 changes: 34 additions & 66 deletions src/components/Forms/CreateFatePool.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ import PoolConfigurationStep from "./Steps/PoolConfigurationStep";
import TokenConfigurationStep from "./Steps/TokenConfigurationStep";
import FeeConfigurationStep from "./Steps/FeeConfigurationStep";
import ReviewStep from "./Steps/ReviewStep";
import type { FormData } from "./FormData";
import {
type FormData,
StepOneFormDataSchema,
StepTwoFormDataSchema,
StepThreeFormDataSchema,
} from "./FormData";
import { toast } from "sonner";
import { logger } from "@/lib/logger";
import { PredictionPoolFactoryABI } from "@/utils/abi/PredictionPoolFactory";
Expand Down Expand Up @@ -151,74 +156,37 @@ export default function CreateFatePool() {


const validateCurrentStep = useCallback(() => {
const newErrors: Record<string, string> = {};
const schema =
currentStep === 1
? StepOneFormDataSchema
: currentStep === 2
? StepTwoFormDataSchema
: currentStep === 3
? StepThreeFormDataSchema
: null;

if (!schema) {
setErrors({});
return true;
}

if (currentStep === 1) {
logger.debug('Validating step 1 with formData:', { formData });

if (!formData.poolName.trim()) newErrors.poolName = "Pool name is required";
if (!formData.baseTokenAddress.trim()) {
newErrors.baseTokenAddress = "Base token address is required";
} else if (!/^0x[a-fA-F0-9]{40}$/.test(formData.baseTokenAddress.trim())) {
newErrors.baseTokenAddress = "Invalid Ethereum address format";
}

// Validate initial deposit
const initialDeposit = parseFloat(formData.initialDeposit);
if (isNaN(initialDeposit)) {
newErrors.initialDeposit = "Initial deposit must be a valid number";
} else if (initialDeposit < 0) {
newErrors.initialDeposit = "Initial deposit cannot be negative";
}

// Validate oracle configuration based on type
if (formData.oracleType === 'chainlink') {
if (!formData.priceFeedAddress) {
newErrors.priceFeedAddress = "Please select a Chainlink price feed";
}
} else if (formData.oracleType === 'hebeswap') {
logger.debug('Validating Hebeswap configuration:', {
hebeswapPairAddress: formData.hebeswapPairAddress,
hebeswapQuoteToken: formData.hebeswapQuoteToken
});

if (!formData.hebeswapPairAddress) {
newErrors.hebeswapPairAddress = "Hebeswap pair address is required";
} else if (!/^0x[a-fA-F0-9]{40}$/.test(formData.hebeswapPairAddress.trim())) {
newErrors.hebeswapPairAddress = "Invalid Hebeswap pair address format";
}
if (!formData.hebeswapQuoteToken) {
newErrors.hebeswapQuoteToken = "Quote token address is required";
} else if (!/^0x[a-fA-F0-9]{40}$/.test(formData.hebeswapQuoteToken.trim())) {
newErrors.hebeswapQuoteToken = "Invalid quote token address format";
}
const result = schema.safeParse(formData);
if (result.success) {
setErrors({});
return true;
}

const newErrors: Record<string, string> = {};
for (const issue of result.error.issues) {
const field = issue.path[0];
if (typeof field === "string" && !newErrors[field]) {
newErrors[field] = issue.message;
}

logger.debug('Step 1 validation errors:', { errors: newErrors });
} else if (currentStep === 2) {
if (!formData.bullCoinName.trim()) newErrors.bullCoinName = "Bull coin name is required";
if (!formData.bullCoinSymbol.trim()) newErrors.bullCoinSymbol = "Bull coin symbol is required";
if (!formData.bearCoinName.trim()) newErrors.bearCoinName = "Bear coin name is required";
if (!formData.bearCoinSymbol.trim()) newErrors.bearCoinSymbol = "Bear coin symbol is required";
} else if (currentStep === 3) {
const mintFee = parseFloat(formData.mintFee);
const burnFee = parseFloat(formData.burnFee);
const creatorFee = parseFloat(formData.creatorFee);
const treasuryFee = parseFloat(formData.treasuryFee);

if (isNaN(mintFee) || mintFee < 0) newErrors.mintFee = "Invalid fee value";
if (isNaN(burnFee) || burnFee < 0) newErrors.burnFee = "Invalid fee value";
if (isNaN(creatorFee) || creatorFee < 0) newErrors.creatorFee = "Invalid fee value";
if (isNaN(treasuryFee) || treasuryFee < 0) newErrors.treasuryFee = "Invalid fee value";

const totalFee = mintFee + burnFee + creatorFee + treasuryFee;
if (totalFee >= 100) {
newErrors.mintFee = "Total fees must be less than 100%";
}
}
}

logger.debug(`Step ${currentStep} validation errors:`, { errors: newErrors });
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
return false;
}, [currentStep, formData]);

const nextStep = useCallback(() => {
Expand Down Expand Up @@ -712,4 +680,4 @@ export default function CreateFatePool() {
</div>
</div>
);
}
}
Loading