Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
104 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
f21ad27
refactor: layout upgrades
passandscore Jan 30, 2026
e674fa8
testing: console.logs
passandscore Jan 30, 2026
125663b
feat: network check before swap
passandscore Feb 2, 2026
fad6860
refactor: update dashboard tab
passandscore Feb 2, 2026
7e1e4e0
refactor: hide auto slippage badge on confirmation when not set
passandscore Feb 2, 2026
f9aa08a
feat: centralize error handling
passandscore Feb 2, 2026
4a2ecb2
refactor: fast/eth request format
passandscore Feb 2, 2026
2e07e6f
refactor: fix tx execution errors
passandscore Feb 3, 2026
981b7ed
refactor: update error message handling
passandscore Feb 3, 2026
7be815b
refactor: update gas values
passandscore Feb 4, 2026
37b1a81
refactor: cleanup
passandscore Feb 4, 2026
6e8884d
refactor: improve deadline errors
passandscore Feb 4, 2026
9da84a2
refactor: handle slippage display in settings
passandscore Feb 4, 2026
9e8aa85
feat: add Barter api
passandscore Feb 4, 2026
e408101
fix: build errors
passandscore Feb 4, 2026
2e421e8
refactor: make auto slippage default onload
passandscore Feb 4, 2026
8b7852a
refactor: optimise for Vercel speed insights
passandscore Feb 4, 2026
ce6aa9c
refactor: format full errors
passandscore Feb 4, 2026
f782b8d
fix: remove weth as output token when eth is needed
passandscore Feb 5, 2026
15d977d
refactor: add additional details to barter error log
passandscore Feb 5, 2026
f34fdc8
feat: permit2 approval
passandscore Feb 5, 2026
7b14f28
feat: redesign tx processing toast
passandscore Feb 5, 2026
211df11
feat: swap toast feature flag
passandscore Feb 5, 2026
a4abfeb
refactor: swap toast
passandscore Feb 6, 2026
57ba1f6
feat: use additional Barter response data
passandscore Feb 6, 2026
c2d7ac3
refactor: change cta button text for intent
passandscore Feb 7, 2026
95fe8a4
refactor: add slippage to rpc request payload
passandscore Feb 9, 2026
9409823
refactor: switch all quotes back to Uniswap
passandscore Feb 9, 2026
9124dc2
refactor: clamp slippage to max 2%
passandscore Feb 9, 2026
b645097
refactor: show preconf on swapToast
passandscore Feb 9, 2026
c36fdf2
feat: handle failed DB reciepts
passandscore Feb 9, 2026
ef04240
feat: add swap whilelist
passandscore Feb 10, 2026
bb53f96
feat: whitelist feature flag
passandscore Feb 10, 2026
69b806e
refactor: handle db errors
passandscore Feb 11, 2026
6d43ea0
refactor: primary cta button
passandscore Feb 11, 2026
373ec1c
refactor: hide try again on errors after preconf
passandscore Feb 11, 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
25 changes: 25 additions & 0 deletions docs/tx-confirmation-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Transaction confirmation flow (TL;DR)

Two sources race to confirm a swap tx: **Fast RPC** (polled via `eth_getTransactionReceipt`) and **Wagmi** (on-chain receipt). The flow is robust to status flipping from success to failed before finality.

## Flow

- **RPC polling** (every 500ms): Single fetch per iteration. If receipt is **success** (0x1) → call `onPreConfirmed` once, show “pre-confirmed” UI, **keep polling**. If receipt is **reverted** (0x0) at any time → treat as final failure: stop polling, set `hasConfirmedRef` so Wagmi won’t override, call `onError` → UI shows error modal.
- **Wagmi**: When on-chain receipt arrives, if we already “confirmed” (including by RPC failure) we do nothing. Else: if reverted → `onError`; if success → `onConfirmed`, abort RPC polling.

So: pre-confirm is optimistic (0x1 from RPC); we keep re-checking until either Wagmi confirms or we see 0x0 and show error.

## Key pieces

- **Hook:** `useWaitForTxConfirmation` in `src/hooks/use-wait-for-tx-confirmation.ts` — runs the race, uses `fetchTransactionReceiptFromDb` (single request per loop), `preConfirmedFiredRef` so `onPreConfirmed` fires once per hash.
- **Swap UI:** `SwapToast` uses the hook only; `onPreConfirmed` → set status to `"pre-confirmed"`; `onError` → `setFailed` → `lastTxError` → SwapConfirmationModal shows the error.
- **Status:** RPC returns 0x1/0x0; `transaction-receipt-utils` normalizes to `"success"` / `"reverted"`. No raw 0x0/0x1 checks in UI.

## Outcome matrix

| RPC first | Then | Result |
|----------------|------------|---------------------------|
| 0x1 (success) | keep polling | Pre-confirm shown |
| 0x1 then 0x0 | — | Error; Wagmi ignored |
| 0x0 | — | Error |
| Wagmi receipt | — | Success → `onConfirmed`; reverted → `onError` |
15 changes: 15 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,26 @@ const nextConfig = {
hostname: 'assets.coingecko.com',
pathname: '/coins/images/**',
},
{
protocol: 'https',
hostname: 'coin-images.coingecko.com',
pathname: '/coins/images/**',
},
{
protocol: 'https',
hostname: 'cryptologos.cc',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'token-icons.s3.amazonaws.com',
pathname: '/**',
},
{
protocol: 'https',
hostname: 'raw.githubusercontent.com',
pathname: '/**',
},
],
},
};
Expand Down
60 changes: 59 additions & 1 deletion package-lock.json

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

6 changes: 5 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 All @@ -80,6 +83,7 @@
"vaul": "^0.9.9",
"viem": "^2.40.4",
"wagmi": "^2.19.5",
"zustand": "^5.0.3",
"zod": "^3.25.76"
},
"devDependencies": {
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
13 changes: 13 additions & 0 deletions src/app/(app)/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Hero } from "@/components/swap/HeroSection"
import { AnimatedBackgroundOrbs } from "@/components/swap/OrbAnimatedBackground"
import { SwapForm } from "@/components/swap/SwapForm"

export default function IndexPage() {
return (
<div className="relative flex flex-col items-center justify-start px-4 xs:pt-6 pb-4">
<AnimatedBackgroundOrbs />
<Hero />
<SwapForm />
</div>
)
}
73 changes: 73 additions & 0 deletions src/app/api/barter/route/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { NextRequest, NextResponse } from "next/server"

const BARTER_API_BASE = "https://api2.eth.barterswap.xyz"

export async function POST(request: NextRequest) {
try {
const body = await request.json()
const { source, target, sellAmount } = body

if (!source || !target || !sellAmount) {
return NextResponse.json(
{ error: "Missing required fields: source, target, sellAmount" },
{ status: 400 }
)
}

const apiKey = process.env.BARTER_API_KEY
if (!apiKey) {
return NextResponse.json({ error: "Barter API key invalid or missing." }, { status: 500 })
}

const requestId = crypto.randomUUID?.() ?? `route-${Date.now()}`

const resp = await fetch(`${BARTER_API_BASE}/route`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
"X-Request-Id": requestId,
},
body: JSON.stringify({
source: String(source).toLowerCase(),
target: String(target).toLowerCase(),
sellAmount: String(sellAmount),
}),
})

const data = await resp.json()
console.log("barter data", data)

if (!resp.ok) {
const msg = data?.error ?? data?.message ?? `Barter API error (${resp.status})`
return NextResponse.json({ error: msg }, { status: resp.status })
}

const outputAmount = data?.outputWithGasAmount
const gasEstimation = data?.gasEstimation
const transactionFee = data?.transactionFee
const gasPrice = data?.gasPrice

if (outputAmount == null || gasEstimation == null) {
return NextResponse.json(
{ error: "Barter API error. Invalid route response." },
{ status: 500 }
)
}

const response: Record<string, unknown> = {
outputAmount: String(outputAmount),
gasEstimation: Number(gasEstimation),
}
if (transactionFee != null) response.transactionFee = String(transactionFee)
if (gasPrice != null) response.gasPrice = String(gasPrice)

return NextResponse.json(response)
} catch (error) {
console.error("Barter route API error:", error)
return NextResponse.json(
{ error: "Barter API error. Please check your connection." },
{ status: 500 }
)
}
}
Loading