Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion frontend/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import { useState, useEffect } from 'react';
import type { Slice } from '@/components/AllocationChart';
import { VaultOverviewCard } from "@/components/vault-overview-card";
import { Shield, ArrowUpFromLine, ArrowDownToLine } from "lucide-react";
import Link from "next/link";
Expand Down
79 changes: 47 additions & 32 deletions frontend/app/vault/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@ import { ArrowUpFromLine, ArrowDownToLine, Loader2 } from "lucide-react";
import { useWallet } from "@/hooks/use-wallet";
import { useNetwork, NetworkType } from "@/app/context/NetworkContext";
import { buildDepositXdr, buildWithdrawXdr, simulateAndAssembleTransaction, submitTransaction, fetchVaultData, VaultMetrics, getNetworkPassphrase } from "@/lib/stellar";
import VaultAPYChart from "@/components/VaultAPYChart";

const CONTRACT_ID = process.env.NEXT_PUBLIC_CONTRACT_ID || "CDLZFC3SYJYDZT7K67VZ75HPJVIEUVNIXF47ZG2FB2RMQQVU2HHGCYSC";

type TabType = "deposit" | "withdraw";

// Placeholder historical APY data
const mockApyData = Array.from({ length: 30 }).map((_, i) => {
const date = new Date();
date.setDate(date.getDate() - (29 - i));
return {
date: date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),
apy: 5 + Math.random() * 3 + (i * 0.1), // Creates an upward trend between 5-11%
};
});

export default function VaultPage() {
const { connected, address, signTransaction } = useWallet();
const { network } = useNetwork();
Expand All @@ -24,7 +35,7 @@ export default function VaultPage() {

const loadMetrics = useCallback(async () => {
if (!connected || !address) return;

try {
const data = await fetchVaultData(CONTRACT_ID, address, network);
setMetrics(data);
Expand All @@ -51,35 +62,35 @@ export default function VaultPage() {

try {
const passphrase = getNetworkPassphrase(network);

const xdr = await buildDepositXdr(
CONTRACT_ID,
address,
amount,
network
);

const { result: assembledXdr, error: assembleError } = await simulateAndAssembleTransaction(
xdr,
network
);

if (assembleError || !assembledXdr) {
throw new Error(assembleError || "Failed to assemble transaction");
}

const { signedTxXdr, error: signError } = await signTransaction(assembledXdr, passphrase);

if (signError || !signedTxXdr) {
throw new Error(signError || "Failed to sign transaction");
}

const { hash, error: submitError } = await submitTransaction(signedTxXdr, network);

if (submitError || !hash) {
throw new Error(submitError || "Failed to submit transaction");
}

setStatus({ type: "success", message: `Deposit successful! Transaction: ${hash.slice(0, 8)}...` });
setAmount("");
await loadMetrics();
Expand Down Expand Up @@ -110,35 +121,35 @@ export default function VaultPage() {

try {
const passphrase = getNetworkPassphrase(network);

const xdr = await buildWithdrawXdr(
CONTRACT_ID,
address,
amount,
network
);

const { result: assembledXdr, error: assembleError } = await simulateAndAssembleTransaction(
xdr,
network
);

if (assembleError || !assembledXdr) {
throw new Error(assembleError || "Failed to assemble transaction");
}

const { signedTxXdr, error: signError } = await signTransaction(assembledXdr, passphrase);

if (signError || !signedTxXdr) {
throw new Error(signError || "Failed to sign transaction");
}

const { hash, error: submitError } = await submitTransaction(signedTxXdr, network);

if (submitError || !hash) {
throw new Error(submitError || "Failed to submit transaction");
}

setStatus({ type: "success", message: `Withdraw successful! Transaction: ${hash.slice(0, 8)}...` });
setAmount("");
await loadMetrics();
Expand All @@ -160,22 +171,20 @@ export default function VaultPage() {
<div className="flex border-b">
<button
onClick={() => setActiveTab("deposit")}
className={`flex-1 flex items-center justify-center gap-2 py-3 px-4 font-medium transition-colors ${
activeTab === "deposit"
? "bg-primary/10 text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
className={`flex-1 flex items-center justify-center gap-2 py-3 px-4 font-medium transition-colors ${activeTab === "deposit"
? "bg-primary/10 text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
<ArrowUpFromLine className="w-4 h-4" />
Deposit
</button>
<button
onClick={() => setActiveTab("withdraw")}
className={`flex-1 flex items-center justify-center gap-2 py-3 px-4 font-medium transition-colors ${
activeTab === "withdraw"
? "bg-primary/10 text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
className={`flex-1 flex items-center justify-center gap-2 py-3 px-4 font-medium transition-colors ${activeTab === "withdraw"
? "bg-primary/10 text-primary border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
<ArrowDownToLine className="w-4 h-4" />
Withdraw
Expand Down Expand Up @@ -234,11 +243,10 @@ export default function VaultPage() {

{status.type && (
<div
className={`p-4 rounded-lg ${
status.type === "success"
? "bg-green-500/10 text-green-500"
: "bg-red-500/10 text-red-500"
}`}
className={`p-4 rounded-lg ${status.type === "success"
? "bg-green-500/10 text-green-500"
: "bg-red-500/10 text-red-500"
}`}
>
{status.message}
</div>
Expand All @@ -247,6 +255,13 @@ export default function VaultPage() {
)}
</div>
</div>

{connected && (
<div className="mt-8 rounded-lg border bg-card p-6">
<h2 className="text-xl font-bold mb-6">APY History</h2>
<VaultAPYChart data={mockApyData} />
</div>
)}
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/components/AllocationChart.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";
import React from 'react';

type Slice = { name: string; value: number; color?: string };
export type Slice = { name: string; value: number; color?: string };

function polarToCartesian(cx: number, cy: number, r: number, angle: number) {
const rad = (angle - 90) * (Math.PI / 180.0);
Expand Down
81 changes: 81 additions & 0 deletions frontend/components/VaultAPYChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"use client";

import React from 'react';
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer
} from 'recharts';

type DataPoint = {
date: string;
apy: number;
};

interface VaultAPYChartProps {
data: DataPoint[];
}

export default function VaultAPYChart({ data }: VaultAPYChartProps) {
if (!data || data.length === 0) {
return (
<div className="flex h-64 items-center justify-center rounded-lg border border-dashed bg-card/50 text-muted-foreground">
No APY data available
</div>
);
}

return (
<div className="w-full h-72">
<ResponsiveContainer width="100%" height="100%">
<AreaChart
data={data}
margin={{ top: 10, right: 10, left: -20, bottom: 0 }}
>
<defs>
<linearGradient id="colorApy" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="hsl(142.1 76.2% 36.3%)" stopOpacity={0.8} />
<stop offset="95%" stopColor="hsl(142.1 76.2% 36.3%)" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="hsl(var(--border))" />
<XAxis
dataKey="date"
axisLine={false}
tickLine={false}
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
dy={10}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={{ fill: 'hsl(var(--muted-foreground))', fontSize: 12 }}
tickFormatter={(value) => `${value}%`}
/>
<Tooltip
contentStyle={{
backgroundColor: 'hsl(var(--card))',
borderColor: 'hsl(var(--border))',
borderRadius: '8px',
color: 'hsl(var(--card-foreground))'
}}
itemStyle={{ color: 'hsl(var(--foreground))' }}
formatter={(value: any) => [`${Number(value).toFixed(2)}%`, 'APY']}
/>
<Area
type="monotone"
dataKey="apy"
stroke="hsl(142.1 76.2% 36.3%)"
strokeWidth={2}
fillOpacity={1}
fill="url(#colorApy)"
/>
</AreaChart>
</ResponsiveContainer>
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
import "./.next/types/routes.d.ts";
import "./.next/dev/types/routes.d.ts";

// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
Loading