Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions apps/web/public/celo-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/web/public/tether-usdt-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions apps/web/public/usd-coin-usdc-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
28 changes: 14 additions & 14 deletions apps/web/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ const stagger = {

const STATS = [
{ value: "< 5s", label: "Settlement Time" },
{ value: "0.3%", label: "Platform Fee" },
// { value: "0.3%", label: "Platform Fee" },
{ value: "Oracle", label: "Pricing Source" },
{ value: "ERC-8004", label: "AI Standard" },
];

const FEATURES = [
{
icon: <RefreshCw className="w-5 h-5 text-brand-blue" />,
title: "Mento Oracle Pricing",
desc: "Swap USDC and USDT at oracle-sourced rates — no AMM slippage, no price manipulation.",
title: "Mento & Uniswap Pricing",
desc: "Swap USDC and USDT at oracle-sourced rates from Mento and Uniswap — no AMM slippage, no price manipulation.",
gradient: "from-brand-blue/20 to-blue-600/5",
border: "border-brand-blue/15",
},
Expand Down Expand Up @@ -175,6 +175,14 @@ export default function Home() {
backgroundSize: "32px 32px",
}}
/>
<div
className="absolute inset-0 opacity-20"
style={{
backgroundImage: "url('/grid.svg')",
backgroundSize: "100px 100px",
backgroundRepeat: "repeat",
}}
/>
<motion.div
animate={{ opacity: [0.2, 0.4, 0.2] }}
transition={{ duration: 12, repeat: Infinity, ease: "easeInOut" }}
Expand Down Expand Up @@ -302,18 +310,10 @@ export default function Home() {

<div className="grid sm:grid-cols-2 gap-3 mt-6">
{[
{
title: "Oracle-backed rates",
value: "Mento + Uniswap",
},
{
title: "Execution guidance",
value: "ERC-8004 AI Agent",
},
{
title: "Platform fee",
value: "0.3% displayed upfront",
},
{
title: "Settlement",
value: "Celo Mainnet",
Expand All @@ -339,13 +339,13 @@ export default function Home() {
</p>
<div className="flex items-end justify-between mt-2">
<div>
<p className="text-white text-lg font-bold">0.50 CELO</p>
<p className="text-white text-lg font-bold">100 USDC</p>
<p className="text-white/45 text-xs">You send</p>
</div>
<ArrowRight className="w-4 h-4 text-white/35 mb-1" />
<div className="text-right">
<p className="text-brand-green text-lg font-bold">
~0.04 USDC
~99.70 USDT
</p>
<p className="text-white/45 text-xs">You receive</p>
</div>
Expand Down Expand Up @@ -652,7 +652,7 @@ export default function Home() {
</p>
<div className="flex justify-center gap-4 flex-wrap">
<a
href="#swap"
href="app"
className="inline-flex items-center gap-2 px-8 py-4 rounded-2xl bg-gradient-to-r from-brand-blue to-brand-green text-white font-bold hover:opacity-90 hover:-translate-y-0.5 transition-all shadow-lg shadow-brand-blue/20"
>
Launch Swap <ArrowRight className="w-4 h-4" />
Expand Down
11 changes: 7 additions & 4 deletions apps/web/src/components/main-app/panels/swap-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
import {
getSwapTokenInfo,
formatTokenAmount,
isValidSwapPair,

Check warning on line 17 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (22.x)

'isValidSwapPair' is defined but never used

Check warning on line 17 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (20.x)

'isValidSwapPair' is defined but never used
isCeloPair,

Check warning on line 18 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (22.x)

'isCeloPair' is defined but never used

Check warning on line 18 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (20.x)

'isCeloPair' is defined but never used
} from "@/lib/swap/usdc-usdt-swap";
import type { AgentRecommendation } from "@/lib/agent/erc8004-agent";
import { PLATFORM_FEE_PERCENT, SWAP_TOKENS } from "@/lib/minipay/constants";
Expand Down Expand Up @@ -100,7 +100,7 @@
symbol,
onSelect,
disabled = false,
balance,

Check warning on line 103 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (22.x)

'balance' is defined but never used

Check warning on line 103 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (20.x)

'balance' is defined but never used
}: {
symbol: SwapTokenSymbol;
onSelect: (token: SwapTokenSymbol) => void;
Expand Down Expand Up @@ -281,8 +281,8 @@
}
}, [externalSwapParams]);

const fromInfo = getSwapTokenInfo(swap.fromToken);

Check warning on line 284 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (22.x)

'fromInfo' is assigned a value but never used

Check warning on line 284 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (20.x)

'fromInfo' is assigned a value but never used
const toInfo = getSwapTokenInfo(swap.toToken);

Check warning on line 285 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (22.x)

'toInfo' is assigned a value but never used

Check warning on line 285 in apps/web/src/components/main-app/panels/swap-panel.tsx

View workflow job for this annotation

GitHub Actions / lint (20.x)

'toInfo' is assigned a value but never used

const handleSwap = async () => {
onTransactionStart?.();
Expand All @@ -296,8 +296,9 @@

return (
<>
<div className="space-y-2">
<div className="rounded-2xl p-4 bg-white/[0.04] border border-white/[0.07] focus-within:border-brand-blue/40 transition-all duration-200">
<div className="space-y-0 relative">
{/* Top Input Container */}
<div className="rounded-t-3xl pt-4 px-4 pb-6 bg-white/[0.04] border border-white/[0.07] border-b-0 focus-within:border-brand-blue/40 transition-all duration-200">
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-medium text-white/40 uppercase tracking-wider">
You Send
Expand Down Expand Up @@ -328,7 +329,8 @@
</div>
</div>

<div className="flex items-center justify-center -my-1 relative z-10">
{/* Swap Button - Positioned to overlap both containers */}
<div className="flex items-center justify-center relative z-20 -my-5">
<motion.button
onClick={swap.handleSwitch}
disabled={swap.isSwapping}
Expand All @@ -341,7 +343,8 @@
</motion.button>
</div>

<div className="rounded-2xl p-4 bg-white/[0.03] border border-white/[0.05]">
{/* Bottom Input Container */}
<div className="rounded-b-3xl pt-6 px-4 pb-4 bg-white/[0.03] border border-white/[0.05] border-t-0">
<div className="flex items-center justify-between mb-3">
<span className="text-xs font-medium text-white/40 uppercase tracking-wider">
You Receive
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/swap/ai-agent-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ function AIAgentPanelInner({
<div className="flex items-center gap-1.5 mt-0.5">
<span className={cn("w-1.5 h-1.5 rounded-full", cfg.dot)} />
<span className={cn("text-[11px] font-medium", cfg.color)}>
{cfg.label} · Mento oracle
{cfg.label} ·
</span>
{reputation?.averageScore != null && (
<span className="ml-1 text-[10px] text-white/30 flex items-center gap-0.5">
Expand Down
4 changes: 2 additions & 2 deletions apps/web/src/components/swap/swap-interface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ export function SwapInterface() {
externalSwapParams={chatSwapParams}
/>

<div className="grid grid-cols-2 gap-2 mt-5 pt-5 border-t border-white/[0.05]">
{/* <div className="grid grid-cols-2 gap-2 mt-5 pt-5 border-t border-white/[0.05]">
{[
{
icon: <Shield className="w-3.5 h-3.5 text-brand-green" />,
Expand All @@ -210,7 +210,7 @@ export function SwapInterface() {
<span>{text}</span>
</div>
))}
</div>
</div> */}
</GlassCard>
</motion.div>

Expand Down
89 changes: 43 additions & 46 deletions apps/web/src/contexts/auth-context.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
'use client';
"use client";

import { createContext, useContext, useEffect, useState } from 'react';
import { useAccount, useDisconnect } from 'wagmi';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useRouter } from 'next/navigation';
import { useToast } from '@/components/ui/use-toast';
import { getNonce, verifySignature } from '@/lib/auth/helpers';
import { useUser } from '@/lib/hooks/useUser';
import { createContext, useContext, useEffect, useState } from "react";
import { useAccount, useDisconnect } from "wagmi";
import { useQueryClient } from "@tanstack/react-query";
import { useRouter } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";
import { getNonce, verifySignature } from "@/lib/auth/helpers";

type User = {
id: string;
Expand All @@ -18,7 +17,9 @@ type AuthContextType = {
user: User | null;
isAuthenticated: boolean;
isLoading: boolean;
login: (signMessageAsync: (message: string) => Promise<string>) => Promise<void>;
login: (
signMessageAsync: (message: string) => Promise<string>,
) => Promise<void>;
logout: () => void;
};

Expand All @@ -33,55 +34,51 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const [isLoading, setIsLoading] = useState(false);
const [user, setUser] = useState<User | null>(null);

// Fetch user profile when wallet is connected
const { data: userProfile, isLoading: isProfileLoading } = useUser(address);

useEffect(() => {
if (userProfile) {
setUser(userProfile as unknown as User);
} else if (isDisconnected) {
setUser(null);
}
}, [userProfile, isDisconnected]);

const login = async (signMessageAsync: (message: string) => Promise<string>) => {
const login = async (
signMessageAsync: (message: string) => Promise<string>,
) => {
if (!address) {
throw new Error('No wallet connected');
throw new Error("No wallet connected");
}

try {
setIsLoading(true);

// Get nonce from server
const { nonce, message } = await getNonce(address);

// Sign the message
const signature = await signMessageAsync(message);

// Verify the signature
const { token, user: userData } = await verifySignature(address, signature, message);

const { token, user: userData } = await verifySignature(
address,
signature,
message,
);

// Store the token
if (typeof window !== 'undefined') {
localStorage.setItem('authToken', token);
localStorage.setItem('walletAddress', address);
if (typeof window !== "undefined") {
localStorage.setItem("authToken", token);
localStorage.setItem("walletAddress", address);
}

setUser(userData);
queryClient.invalidateQueries({ queryKey: ['user-profile', address] });
queryClient.invalidateQueries({ queryKey: ["user-profile", address] });

toast({
title: 'Successfully connected',
description: 'Your wallet has been connected successfully.',
title: "Successfully connected",
description: "Your wallet has been connected successfully.",
});

router.refresh();
} catch (error) {
console.error('Login error:', error);
console.error("Login error:", error);
toast({
title: 'Error',
description: error instanceof Error ? error.message : 'Failed to connect wallet',
type: 'error',
title: "Error",
description:
error instanceof Error ? error.message : "Failed to connect wallet",
type: "error",
});
// Disconnect wallet on error
disconnect();
Expand All @@ -93,19 +90,19 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {

const logout = () => {
disconnect();
if (typeof window !== 'undefined') {
localStorage.removeItem('authToken');
localStorage.removeItem('walletAddress');
if (typeof window !== "undefined") {
localStorage.removeItem("authToken");
localStorage.removeItem("walletAddress");
}
setUser(null);
queryClient.clear();
router.push('/');
router.push("/");
};

const value = {
user,
isAuthenticated: !!user,
isLoading: isLoading || isProfileLoading,
isLoading,
login,
logout,
};
Expand All @@ -116,7 +113,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};
14 changes: 7 additions & 7 deletions apps/web/src/lib/minipay/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const SWAP_TOKENS = [
address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C', // Celo Mainnet
addressSepolia: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', // Celo Sepolia
color: '#2775CA',
logo: '/tokens/usdc.png',
logo: '/usd-coin-usdc-logo.svg',
issuer: 'Circle',
},
{
Expand All @@ -40,7 +40,7 @@ export const SWAP_TOKENS = [
address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e', // Celo Mainnet
addressSepolia: '0x617f3112bF5ad0E84E882D5142D0aE6C606cc89', // Celo Sepolia
color: '#26A17B',
logo: '/tokens/usdt.png',
logo: '/tether-usdt-logo.svg',
issuer: 'Tether',
},
{
Expand All @@ -50,7 +50,7 @@ export const SWAP_TOKENS = [
address: '0x471EcE3750Da237f93B8E339c536989b8978a438', // Celo Mainnet
addressSepolia: '0xF194AFDF50bAE0a21EF85469d1521810657a1b53', // Celo Sepolia
color: '#FCFF52',
logo: '/tokens/celo.png',
logo: '/celo-logo.svg',
issuer: 'Celo',
},
] as const;
Expand All @@ -66,17 +66,17 @@ export const USDM_TOKEN = {

// Keep SUPPORTED_TOKENS for Mento SDK compatibility (includes USDm + CELO for rate API)
export const SUPPORTED_TOKENS = [
{ ...USDM_TOKEN, logo: '/tokens/usdm.png' },
{ ...USDM_TOKEN, logo: '/usd-coin-usdc-logo.svg' },
{
symbol: 'CELO',
name: 'Celo',
decimals: 18,
address: '0x471EcE3750Da237f93B8E339c536989b8978a438',
addressSepolia: '0xF194AFDF50bAE0a21EF85469d1521810657a1b53',
logo: '/tokens/celo.png',
logo: '/celo-logo.svg',
},
{ symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C', addressSepolia: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', logo: '/tokens/usdc.png' },
{ symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e', addressSepolia: '0x617f3112bF5ad0E84E882D5142D0aE6C606cc89', logo: '/tokens/usdt.png' },
{ symbol: 'USDC', name: 'USD Coin', decimals: 6, address: '0xcebA9300f2b948710d2653dD7B07f33A8B32118C', addressSepolia: '0x2A3684e9Dc20B857375EA04235F2F7edBe818FA7', logo: '/usd-coin-usdc-logo.svg' },
{ symbol: 'USDT', name: 'Tether USD', decimals: 6, address: '0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e', addressSepolia: '0x617f3112bF5ad0E84E882D5142D0aE6C606cc89', logo: '/tether-usdt-logo.svg' },
];

// ─── Platform Fee ────────────────────────────────────────────────────────────
Expand Down
Loading