Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
6219ff4
feat: inital swap setup
passandscore Jan 19, 2026
6792206
refactor: swap redesign
passandscore Jan 20, 2026
6ed8477
feat: local tokenlist and pricing api
passandscore Jan 20, 2026
f606de6
feat: enhance swap interface with mock quote integration and dual tok…
passandscore Jan 20, 2026
6fbedef
feat: create reusable TokenSelectButton component and improve swap in…
passandscore Jan 20, 2026
d7f6afb
chore: formatting
passandscore Jan 20, 2026
029811e
feat: uniswap quotes
passandscore Jan 20, 2026
8ca0ffe
refactor: swap quote system with pulsing animation
passandscore Jan 20, 2026
7ac9f64
feat: multil-side quotes and switcher logoc
passandscore Jan 20, 2026
86eb762
feat: input odometer effect
passandscore Jan 20, 2026
328e3c8
Add swap interface focus mode with hidden hero sections and still bac…
passandscore Jan 21, 2026
a60d8ec
feat: slippage and review
passandscore Jan 21, 2026
91dce04
refactor: update review swap modal
passandscore Jan 21, 2026
1c9852a
chore: clean up swap files
passandscore Jan 21, 2026
6c7f337
refactor SwapInterface: Extract components and reduce file size
passandscore Jan 21, 2026
cafa070
refactor: fix quotes
passandscore Jan 21, 2026
d2dc4b8
refactor: fix input cursor
passandscore Jan 21, 2026
1708633
refactor: switch logic
passandscore Jan 22, 2026
f7f2219
refactor: fix quote bugs
passandscore Jan 22, 2026
303ca77
refactor: adjust nav header
passandscore Jan 22, 2026
33acdb0
refactor: support 13in macbooks
passandscore Jan 22, 2026
6053506
refactor: action button bug when token switched
passandscore Jan 22, 2026
e6fe948
refactor: remove rounding from wallet balance
passandscore Jan 22, 2026
aa77351
feat: add ETH to token list
passandscore Jan 22, 2026
06b5496
fix: token selection scrolling
passandscore Jan 22, 2026
9ca8cec
refactor: fee teir checks use promise.race & no liquidity errot handling
passandscore Jan 22, 2026
bc7b478
refactor: quote fee tiers
passandscore Jan 22, 2026
8652148
feat: dynamic text input
passandscore Jan 22, 2026
937ed3f
refactor: precentage max bug
passandscore Jan 22, 2026
46cd6e8
refactor: hide visible scroll bar
passandscore Jan 22, 2026
8523238
refactor: redesign swap confirmation
passandscore Jan 22, 2026
42ed47c
feat: weth handling
passandscore Jan 22, 2026
734c595
feat: migrate fast swaps
passandscore Jan 22, 2026
7b5f488
chore: clean up landing
passandscore Jan 22, 2026
e2e9813
refactor: use token list
passandscore Jan 22, 2026
f19b61f
refactor: quote logic
passandscore Jan 23, 2026
6b40bc2
refactor: component restructuring
passandscore Jan 23, 2026
3ef815a
refactor: quote adjustments
passandscore Jan 23, 2026
2cd25c1
refactor: overlay switch button
passandscore Jan 23, 2026
c358a7d
fix: rebase conflict
passandscore Jan 28, 2026
b62e9a8
feat: gas estimate for wrapped operationd
passandscore Jan 26, 2026
e49ee69
fix: remove FastRPC for readOnly calls
passandscore Jan 26, 2026
1476566
refactor: bypass quotes for weth
passandscore Jan 26, 2026
27a6fa8
refactor: adjust contrast on modals
passandscore Jan 28, 2026
998528e
chore: remove unused files
passandscore Jan 28, 2026
6a4d2b9
feat: add fastRPC api endpoints
passandscore Jan 28, 2026
0463104
feat: dummy mode
passandscore Jan 28, 2026
f9c2cfa
feat: review details accordian
passandscore Jan 29, 2026
2841c87
feat: support minOut values
passandscore Jan 29, 2026
173a4ef
feat: mock wagmi wallet connector
passandscore Jan 29, 2026
3d3495b
fix: buikd error
passandscore Jan 29, 2026
2d8bf49
refactor: adjust slippage logic
passandscore Jan 29, 2026
f0b1ffb
feat: useWaitForTxConfirmation hook
passandscore Jan 29, 2026
6fb7d20
refactor: update SwapConfirmationModal label
passandscore Jan 29, 2026
b64b7e7
refactor: add useWaitForTxConfirmation to swap logic
passandscore Jan 29, 2026
5f42c44
refactor: remove dummy logic and cleanup useSwapConfirmation
passandscore Jan 29, 2026
4887d4a
refactor: handle deadline
passandscore Jan 29, 2026
8230522
feat: isStablecoin logic
passandscore Jan 29, 2026
cca2cb9
refactor: SwapConfirmation
passandscore Jan 30, 2026
f5f1403
refactor: cleanup use-swap-confirmation and related files
passandscore Jan 30, 2026
2934e65
refactor: handle price impact confirmation
passandscore Jan 30, 2026
320c18b
feat: reset swap state on wallet disconnect
passandscore Jan 30, 2026
4a46c4d
refactor: add odometer effect to secondary values
passandscore Jan 30, 2026
445f7b6
refactor: adjust swap interface padding
passandscore Jan 30, 2026
68595f9
feat: fetch user points for Fuul
passandscore Jan 30, 2026
28c88a2
refactor: update AppHeader
passandscore Jan 30, 2026
e642913
refactor: padding adjustments
passandscore Jan 30, 2026
e4ec04a
testing: logs and gas
passandscore Jan 30, 2026
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
57 changes: 57 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
"test:run": "vitest run",
"test:coverage": "vitest run --coverage",
"format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\""
"format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
"stablecoins": "tsx scripts/getStablecoins.ts"
},
"dependencies": {
"@fuul/sdk": "^7.7.1",
"@hookform/resolvers": "^3.10.0",
"@next/eslint-plugin-next": "^15.4.2",
"@number-flow/react": "^0.5.10",
"@radix-ui/react-accordion": "^1.2.11",
"@radix-ui/react-alert-dialog": "^1.1.14",
"@radix-ui/react-aspect-ratio": "^1.1.7",
Expand Down Expand Up @@ -60,6 +62,7 @@
"ethers": "^6.16.0",
"googleapis": "^169.0.0",
"input-otp": "^1.4.2",
"lenis": "^1.3.17",
"lucide-react": "^0.462.0",
"motion": "^12.23.26",
"next": "^15.5.7",
Expand Down
35 changes: 35 additions & 0 deletions scripts/getStablecoins.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Fetches stablecoin symbols from CoinGecko and writes a deduplicated list to src/lib/stablecoin-list.json.
* Run: npx tsx scripts/getStablecoins.ts
*/

const COINGECKO_URL =
"https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&category=stablecoins"
const OUT_PATH = new URL("../src/lib/stablecoin-list.json", import.meta.url)

interface CoinGeckoItem {
symbol: string
[key: string]: unknown
}

async function main() {
const res = await fetch(COINGECKO_URL)
if (!res.ok) {
throw new Error(`CoinGecko API error: ${res.status} ${res.statusText}`)
}
const data = (await res.json()) as CoinGeckoItem[]
const symbols = [...new Set(data.map((item) => item.symbol.toUpperCase()))].sort()
const json = JSON.stringify(symbols, null, 2)
const { writeFileSync, mkdirSync } = await import("fs")
const { dirname } = await import("path")
const { fileURLToPath } = await import("url")
const outPath = fileURLToPath(OUT_PATH)
mkdirSync(dirname(outPath), { recursive: true })
writeFileSync(outPath, json + "\n", "utf8")
console.log(`Wrote ${symbols.length} stablecoin symbols to ${outPath}`)
}

main().catch((err) => {
console.error(err)
process.exit(1)
})
4 changes: 3 additions & 1 deletion src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ function AppLayoutContent({ children }: { children: React.ReactNode }) {
!hasCheckedStatus &&
status !== "connecting" &&
status !== "reconnecting" &&
(pathname?.startsWith("/dashboard") || pathname?.startsWith("/leaderboard"))
(pathname?.startsWith("/dashboard") ||
pathname?.startsWith("/leaderboard") ||
pathname?.startsWith("/swap"))
) {
// Small tick to ensure RainbowKit's internal state is also ready
const timer = setTimeout(() => {
Expand Down
9 changes: 9 additions & 0 deletions src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use client"

import { SwapForm } from "@/components/swap/SwapForm"

const IndexPage = () => {
return <SwapForm />
}

export default IndexPage
78 changes: 78 additions & 0 deletions src/app/api/fuul/payouts/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { NextRequest, NextResponse } from "next/server"
import { env } from "@/env/server"
import { isAddress } from "viem"

const FUUL_TOTALS_URL = "https://api.fuul.xyz/api/v1/payouts/totals"

export async function GET(request: NextRequest) {
try {
const fuulApiKey = env.FUUL_API_KEY
if (!fuulApiKey) {
console.error("FUUL_API_KEY not configured")
return NextResponse.json({ error: "Server configuration error" }, { status: 500 })
}

const { searchParams } = new URL(request.url)
const address = searchParams.get("address")

if (!address || !isAddress(address)) {
return NextResponse.json(
{ error: "Valid address query parameter is required" },
{ status: 400 }
)
}

const url = `${FUUL_TOTALS_URL}/${encodeURIComponent(address)}`

const response = await fetch(url, {
method: "GET",
headers: {
accept: "application/json",
Authorization: `Bearer ${fuulApiKey}`,
},
})

if (!response.ok) {
const errorText = await response.text()
console.error("Fuul API error:", response.status, errorText)
return NextResponse.json(
{ error: "Failed to fetch total user points from Fuul", details: errorText },
{ status: response.status }
)
}

try {
const data = await response.json()

// Fuul returns { "total_points": "0" } (string); coerce to number
let totalPoints = 0
if (data?.total_points != null) {
totalPoints = Number(data.total_points)
} else if (typeof data === "number") {
totalPoints = data
} else if (data?.total_payouts != null) {
totalPoints = Number(data.total_payouts)
} else if (data?.total != null) {
totalPoints = Number(data.total)
} else if (data?.points != null) {
totalPoints = Number(data.points)
}

return NextResponse.json({ success: true, data, totalPoints }, { status: 200 })
} catch (parseError) {
console.error("Failed to parse Fuul API response:", parseError)
return NextResponse.json({ error: "Failed to parse response from Fuul API" }, { status: 500 })
}
} catch (error) {
console.error("Error fetching total user points from Fuul:", error)

if (error instanceof Error) {
return NextResponse.json(
{ error: "Failed to fetch total user points", details: error.message },
{ status: 500 }
)
}

return NextResponse.json({ error: "Failed to fetch total user points" }, { status: 500 })
}
}
28 changes: 28 additions & 0 deletions src/app/api/token-price/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { NextRequest, NextResponse } from "next/server"
import { getTokenPrice } from "@/lib/analytics-server"

export async function GET(request: NextRequest) {
try {
const searchParams = request.nextUrl.searchParams
const symbol = searchParams.get("symbol")

if (!symbol) {
return NextResponse.json({ error: "Symbol parameter is required" }, { status: 400 })
}

const price = await getTokenPrice(symbol)

if (price === null) {
return NextResponse.json({ error: `Failed to fetch ${symbol} price` }, { status: 500 })
}

return NextResponse.json({
success: true,
symbol: symbol.toUpperCase(),
price: price,
})
} catch (error) {
console.error("Error fetching token price:", error)
return NextResponse.json({ error: "Failed to fetch token price" }, { status: 500 })
}
}
16 changes: 16 additions & 0 deletions src/app/api/tokens/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { getUniswapTokenList } from "@/lib/swap-server"
import { NextResponse } from "next/server"

/**
* API route to fetch Uniswap token list
* Used by client-side hooks for token list fetching
*/
export async function GET() {
try {
const tokens = await getUniswapTokenList()
return NextResponse.json(tokens)
} catch (error) {
console.error("Error fetching token list:", error)
return NextResponse.json([], { status: 500 })
}
}
Loading
Loading