From 73ae0cb9e50620a5118261722a1d21017fad23dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:14:40 +0000 Subject: [PATCH 1/3] Initial plan From c03fa54eb3aa6ffe8e9f25a157cf76d06bee07cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 01:33:50 +0000 Subject: [PATCH 2/3] fix: transaction errors, real price feeds, multi-token trading, marketplace fork, dashboard PnL charts Agent-Logs-Url: https://github.com/mesayanroy/0x402-pubsub/sessions/a5017868-1cf9-406e-87a5-d8afd60c61af Co-authored-by: mesayanroy <169074736+mesayanroy@users.noreply.github.com> --- app/dashboard/page.tsx | 242 +++++++---- app/marketplace/page.tsx | 271 +++++++++---- app/trading/page.tsx | 788 +++++++++++++++--------------------- components/PaymentModal.tsx | 102 ++++- lib/stellar.ts | 20 +- 5 files changed, 789 insertions(+), 634 deletions(-) diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 574d604..f27bdec 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -9,6 +9,9 @@ import { Bar, BarChart, CartesianGrid, + Cell, + Pie, + PieChart, ResponsiveContainer, Tooltip, XAxis, @@ -45,12 +48,7 @@ const EMPTY_ANALYTICS: AnalyticsResponse = { requestRate: [], earnings: [], invoices: [], - totals: { - requests: 0, - paidRequests: 0, - totalEarnedXlm: 0, - avgLatencyMs: 0, - }, + totals: { requests: 0, paidRequests: 0, totalEarnedXlm: 0, avgLatencyMs: 0 }, generatedAt: new Date().toISOString(), }; @@ -70,11 +68,14 @@ function modelName(model: string): string { return model; } +const PIE_COLORS = ['#00FFE5', '#FFB800', '#4ade80', '#f87171', '#a78bfa']; + export default function DashboardPage() { const [walletAddress, setWalletAddress] = useState(null); const [myAgents, setMyAgents] = useState([]); const [analytics, setAnalytics] = useState(null); const [loading, setLoading] = useState(true); + const [xlmPrice, setXlmPrice] = useState(null); useEffect(() => { const addr = localStorage.getItem('wallet_address'); @@ -88,18 +89,13 @@ export default function DashboardPage() { fetch(`/api/agents/list?owner=${encodeURIComponent(addr)}`), fetch(`/api/dashboard/analytics?owner=${encodeURIComponent(addr)}&hours=24`), ]); - const agentsData = agentsRes.ok ? await agentsRes.json() : { agents: [] }; const analyticsData = analyticsRes.ok ? await analyticsRes.json() : EMPTY_ANALYTICS; - - setMyAgents(agentsData.agents || []); + setMyAgents((agentsData as { agents: Agent[] }).agents || []); setAnalytics({ ...EMPTY_ANALYTICS, ...(analyticsData || {}), - totals: { - ...EMPTY_ANALYTICS.totals, - ...(analyticsData?.totals || {}), - }, + totals: { ...EMPTY_ANALYTICS.totals, ...((analyticsData as AnalyticsResponse)?.totals || {}) }, }); } catch { setMyAgents([]); @@ -109,8 +105,20 @@ export default function DashboardPage() { } }; + // Fetch XLM price for USD conversion + const fetchXlmPrice = async () => { + try { + const r = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=stellar&vs_currencies=usd'); + if (r.ok) { + const d = await r.json() as { stellar: { usd: number } }; + setXlmPrice(d.stellar?.usd ?? null); + } + } catch { /* ignore */ } + }; + void fetchAll(); - const interval = setInterval(fetchAll, 5000); + void fetchXlmPrice(); + const interval = setInterval(fetchAll, 10_000); return () => clearInterval(interval); }, []); @@ -128,50 +136,72 @@ export default function DashboardPage() { if (loading && !analytics) { return (
-

Loading real-time dashboard...

+

Loading real-time dashboard...

); } + const totalEarned = analytics?.totals?.totalEarnedXlm ?? 0; + const totalEarnedUsd = xlmPrice ? totalEarned * xlmPrice : null; + + const freeRequests = (analytics?.totals?.requests ?? 0) - (analytics?.totals?.paidRequests ?? 0); + const paidRequests = analytics?.totals?.paidRequests ?? 0; + + const tradeTypeData = [ + { name: 'Paid Requests', value: paidRequests }, + { name: 'Free Requests', value: freeRequests }, + ].filter((d) => d.value > 0); + + // Compute cumulative PnL from earnings data + let cumulative = 0; + const pnlData = (analytics?.earnings ?? []).map((e) => { + cumulative += e.amount; + return { date: e.date, daily: e.amount, cumulative }; + }); + const statCards = [ - { label: 'My Agents', value: myAgents.length, unit: '' }, - { label: 'Total Earned', value: (analytics?.totals?.totalEarnedXlm ?? 0).toFixed(2), unit: 'XLM' }, - { label: 'Total Requests', value: (analytics?.totals?.requests ?? 0).toLocaleString(), unit: '' }, - { label: 'Active Agents', value: myAgents.filter((a) => a.is_active).length, unit: '' }, + { label: 'My Agents', value: String(myAgents.length), unit: '', color: 'text-[#00FFE5]' }, + { + label: 'Total Earned', + value: totalEarned.toFixed(2), + unit: 'XLM', + sub: totalEarnedUsd ? `≈ $${totalEarnedUsd.toFixed(2)}` : undefined, + color: 'text-[#FFB800]', + }, + { label: 'Total Requests', value: (analytics?.totals?.requests ?? 0).toLocaleString(), unit: '', color: 'text-[#4ade80]' }, + { label: 'Avg Latency', value: String(analytics?.totals?.avgLatencyMs ?? 0), unit: 'ms', color: 'text-purple-400' }, ]; return (
- + +

Dashboard

{walletAddress}

+

Auto-refresh every 10s · Last: {analytics ? new Date(analytics.generatedAt).toLocaleTimeString([], { hour12: false }) : '—'}

+ {/* Stat Cards */}
{statCards.map((stat) => ( -
-
+
+
{stat.value}{stat.unit ? ` ${stat.unit}` : ''}
+ {stat.sub &&
{stat.sub}
}
{stat.label}
))}
+ {/* Charts Row 1: Request Rate + Billing by Model */}

Request Rate by Minute

- auto-refresh 5s + auto-refresh 10s
@@ -183,20 +213,10 @@ export default function DashboardPage() { - new Date(value).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })} - /> + new Date(value).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false })} /> - + @@ -212,25 +232,14 @@ export default function DashboardPage() { - modelName(value)} - /> + modelName(value)} /> { - const raw = Array.isArray(value) ? value[0] : value; - const n = typeof raw === 'number' ? raw : Number(raw || 0); + const n = typeof value === 'number' ? value : Number(value || 0); return `${n.toFixed(2)} XLM`; }} - contentStyle={{ - background: '#0a0a10', - border: '1px solid rgba(255,255,255,0.12)', - borderRadius: 10, - color: '#fff', - }} - /> + contentStyle={{ background: '#0a0a10', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 10, color: '#fff' }} /> @@ -238,24 +247,98 @@ export default function DashboardPage() {
+ {/* Charts Row 2: PnL + Request Type Breakdown */} +
+ {/* Cumulative PnL */} +
+
+

Cumulative PnL (XLM)

+ daily earnings +
+
+ {pnlData.length === 0 ? ( +
+ No paid activity yet — run a paid agent to see PnL +
+ ) : ( + + + + + + + + + + + + { + const n = typeof value === 'number' ? value : Number(value || 0); + return [`${n.toFixed(4)} XLM`, name === 'cumulative' ? 'Total PnL' : 'Daily Earned']; + }} + contentStyle={{ background: '#0a0a10', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 10, color: '#fff' }} /> + + + + + )} +
+
+ + {/* Request Type Breakdown */} +
+
+

Request Type Breakdown

+ paid vs free +
+
+ {tradeTypeData.length === 0 ? ( +
No requests yet
+ ) : ( +
+
+ + + + {tradeTypeData.map((_, index) => ( + + ))} + + [`${value} requests`, '']} + contentStyle={{ background: '#0a0a10', border: '1px solid rgba(255,255,255,0.12)', borderRadius: 10, color: '#fff' }} /> + + +
+
+ {tradeTypeData.map((d, i) => ( +
+ + {d.name} + {d.value} +
+ ))} +
+
+ )} +
+
+
+ + {/* Invoice Stream */}

Invoice Stream (0x402)

- - avg latency: {analytics?.totals?.avgLatencyMs ?? 0} ms - + avg latency: {analytics?.totals?.avgLatencyMs ?? 0} ms
- - - - - - - + {['Invoice', 'Agent', 'Model', 'Amount', 'Signature', 'Caller', 'Time'].map((h) => ( + + ))} @@ -266,12 +349,7 @@ export default function DashboardPage() { @@ -293,31 +371,29 @@ export default function DashboardPage() { + {/* My Agents */}

My Agents

- + + Deploy New
+ {myAgents.length === 0 && ( +

No agents deployed yet.

+ )} {myAgents.map((agent) => ( -
+
{agent.name}
- {agent.model === 'openai-gpt4o-mini' ? 'GPT-4o Mini' : 'Claude Haiku'} ·{' '} - {agent.price_xlm} XLM/req · {agent.visibility} + {agent.model === 'openai-gpt4o-mini' ? 'GPT-4o Mini' : 'Claude Haiku'} · {agent.price_xlm} XLM/req · {agent.visibility}
{agent.total_earned_xlm} XLM
+ {xlmPrice &&
≈ ${(agent.total_earned_xlm * xlmPrice).toFixed(2)}
}
{agent.total_requests.toLocaleString()} requests
diff --git a/app/marketplace/page.tsx b/app/marketplace/page.tsx index 982da9f..8f1894f 100644 --- a/app/marketplace/page.tsx +++ b/app/marketplace/page.tsx @@ -1,97 +1,214 @@ 'use client'; +import { useState, useEffect } from 'react'; import { motion } from 'framer-motion'; import AgentCard from '@/components/AgentCard'; import { Agent } from '@/types'; -const FEATURED: Agent[] = [ - { - id: '1', - owner_wallet: 'GABC...XYZ1', - name: 'DeFi Analyst', - description: 'Top-ranked DeFi analysis agent with real-time protocol insights.', - tags: ['web3', 'finance', 'defi'], - model: 'openai-gpt4o-mini', - system_prompt: '', - tools: ['on_chain_data', 'web_search'], - price_xlm: 0.05, - visibility: 'public', - total_requests: 14200, - total_earned_xlm: 710.0, - is_active: true, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - }, - { - id: '2', - owner_wallet: 'GDEF...XYZ2', - name: 'Code Review Bot', - description: 'Elite code review agent used by 200+ developers daily.', - tags: ['dev', 'automation'], - model: 'anthropic-claude-haiku', - system_prompt: '', - tools: ['code_execution'], - price_xlm: 0.1, - visibility: 'public', - total_requests: 8920, - total_earned_xlm: 892.0, - is_active: true, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - }, -]; - -const TRENDING: Agent[] = [ - { - id: '3', - owner_wallet: 'GHIJ...XYZ3', - name: 'Smart Contract Auditor', - description: 'Trending: Soroban contract vulnerability scanner.', - tags: ['web3', 'security'], - model: 'anthropic-claude-haiku', - system_prompt: '', - tools: ['code_execution', 'on_chain_data'], - price_xlm: 0.25, - visibility: 'public', - total_requests: 2340, - total_earned_xlm: 585.0, - is_active: true, - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - }, -]; +interface ForkModalProps { + agent: Agent; + onClose: () => void; + onSuccess: (txHash: string) => void; +} + +function ForkModal({ agent, onClose, onSuccess }: ForkModalProps) { + const [step, setStep] = useState<'idle' | 'paying' | 'done' | 'error'>('idle'); + const [error, setError] = useState(null); + const [customName, setCustomName] = useState(`Fork of ${agent.name}`); + const [customPrompt, setCustomPrompt] = useState(agent.system_prompt || ''); + const FORK_FEE_XLM = agent.price_xlm > 0 ? agent.price_xlm * 10 : 1; + + const handleFork = async () => { + setStep('paying'); + setError(null); + try { + const StellarSdk = await import('stellar-sdk'); + const freighter = await import('@stellar/freighter-api'); + + const conn = await freighter.isConnected(); + if (!conn.isConnected) throw new Error('Freighter wallet not installed. Visit https://www.freighter.app'); + + await freighter.requestAccess(); + const { address: senderKey, error: addrError } = await freighter.getAddress(); + if (addrError || !senderKey) throw new Error('Could not get wallet address from Freighter.'); + + const isMainnet = process.env.NEXT_PUBLIC_STELLAR_NETWORK === 'mainnet'; + const horizonUrl = process.env.NEXT_PUBLIC_HORIZON_URL || + (isMainnet ? 'https://horizon.stellar.org' : 'https://horizon-testnet.stellar.org'); + const networkPassphrase = isMainnet ? StellarSdk.Networks.PUBLIC : StellarSdk.Networks.TESTNET; + + const horizonServer = new StellarSdk.Horizon.Server(horizonUrl); + const senderAccount = await horizonServer.loadAccount(senderKey); + + const memo = `fork:${agent.id}`.slice(0, 28); + const tx = new StellarSdk.TransactionBuilder(senderAccount, { fee: StellarSdk.BASE_FEE, networkPassphrase }) + .addOperation(StellarSdk.Operation.payment({ + destination: agent.owner_wallet, + asset: StellarSdk.Asset.native(), + amount: FORK_FEE_XLM.toFixed(7), + })) + .addMemo(StellarSdk.Memo.text(memo)) + .setTimeout(60) + .build(); + + const signedResult = await freighter.signTransaction(tx.toXDR(), { networkPassphrase }); + if (signedResult.error) throw new Error(String(signedResult.error)); + + const signedTx = StellarSdk.TransactionBuilder.fromXDR(signedResult.signedTxXdr, networkPassphrase); + const result = await horizonServer.submitTransaction(signedTx); + + setStep('done'); + onSuccess(result.hash); + } catch (err) { + const msg = String(err); + setError(msg.startsWith('Error:') ? msg.slice(7).trim() : msg); + setStep('error'); + } + }; + + return ( +
+
e.stopPropagation()}> +

Fork Agent

+

+ Pay {FORK_FEE_XLM} XLM to fork "{agent.name}" and customise it. +

+ +
+
+ + setCustomName(e.target.value)} + className="w-full px-3 py-2 bg-white/[0.03] border border-white/[0.08] rounded-lg text-white text-sm font-mono focus:outline-none focus:border-[rgba(255,184,0,0.4)]" /> +
+
+ +
InvoiceAgentModelAmountSignatureCallerTime{h}
{modelName(row.model)} {row.amountXlm.toFixed(4)} XLM - + {shortHash(row.txHash)}