import { Fragment, useEffect, useState } from 'react'; import EmptyState from '../components/EmptyState.jsx'; import MetricCard from '../components/MetricCard.jsx'; import Pill from '../components/Pill.jsx'; import TableFrame from '../components/TableFrame.jsx'; import { formatEur, formatTimestamp, stringifyJson, truncateMiddle } from '../lib/format.js'; function buildInitialEstimateForm(balances, withdrawalDefaults) { const firstAssetId = balances?.[0]?.asset_id || ''; return { asset_id: firstAssetId, amount: '', destination_address: withdrawalDefaults?.[firstAssetId] || Object.values(withdrawalDefaults || {})[0] || '', chain: '', }; } function BalancesTable({ items }) { if (!items?.length) return No inventory snapshot is available yet.; return ( {items.map((item) => ( ))}
Asset Spendable Pending inbound Pending outbound EUR value
{item.symbol}
{item.asset_id}
{item.spendable} {item.pending_inbound} {item.pending_outbound} {formatEur(item.eur_value_eure)}
); } function HandlesList({ handles }) { if (!handles?.length) return No funding handles are available.; return (
{handles.map((handle) => (
{handle.symbol}
{handle.address || 'Unavailable'}
{handle.memo ?
{`Memo ${handle.memo}`}
: null}
{`Refreshed ${formatTimestamp(handle.refreshed_at)}`}
))}
); } function FundingTable({ items, creditedOnly = false }) { if (!items?.length) { return ( {creditedOnly ? 'No credited deposits have been recorded yet.' : 'No funding observations are available.'} ); } return ( {items.map((item) => ( ))}
Status Asset Amount Handle Observed
{item.asset_symbol || item.asset_id} {item.amount_display || item.amount || 'Unavailable'} {truncateMiddle(item.funding_handle || '', 36)} {formatTimestamp(item.credited_at || item.last_seen_at || item.first_seen_at)}
); } function PreCreditAssetTable({ items }) { if (!items?.length) return No pre-credit funding is currently tracked.; return ( {items.map((item) => ( ))}
Asset Pending amount Observation count Last seen
{item.asset_symbol || item.asset_id} {item.amount_display || item.amount || 'Unavailable'} {item.observation_count || 0} {formatTimestamp(item.last_seen_at)}
); } function WithdrawalsTable({ items }) { if (!items?.length) return No withdrawals are tracked yet.; return ( {items.map((item) => ( ))}
Status Asset Amount Destination Observed
{item.asset_symbol || item.asset_id} {item.amount_display || item.amount || 'Unavailable'} {truncateMiddle(item.destination_address || '', 36)} {formatTimestamp(item.completed_at || item.requested_at)}
); } function WithdrawalEstimateForm({ balances, withdrawalDefaults, onControl }) { const [form, setForm] = useState(() => buildInitialEstimateForm(balances, withdrawalDefaults)); useEffect(() => { setForm(buildInitialEstimateForm(balances, withdrawalDefaults)); }, [balances, withdrawalDefaults]); async function handleSubmit(event) { event.preventDefault(); const body = { ...form }; if (!body.chain) delete body.chain; await onControl('liquidity-manager', 'withdrawal-estimate', body, { reload: false }); } return (
setForm((current) => ({ ...current, amount: event.target.value }))} placeholder="1000000" required value={form.amount} />
setForm((current) => ({ ...current, destination_address: event.target.value }))} value={form.destination_address} />
setForm((current) => ({ ...current, chain: event.target.value }))} placeholder="Optional" value={form.chain} />
); } async function copyIdentifier(value) { if (!value || typeof navigator === 'undefined' || !navigator.clipboard?.writeText) return; try { await navigator.clipboard.writeText(value); } catch { // Full ids remain visible when clipboard access is unavailable. } } function IdentifierLine({ label, value }) { if (!value) return
{`${label}: unavailable`}
; return (
{`${label}:`} {value}
); } function IntentRequestForm({ summary, onControl }) { const defaults = summary?.defaults || {}; const [form, setForm] = useState({ amount_eure: defaults.amount_eure || '5', slippage_bps: String(defaults.slippage_bps ?? 200), }); useEffect(() => { setForm({ amount_eure: defaults.amount_eure || '5', slippage_bps: String(defaults.slippage_bps ?? 200), }); }, [defaults.amount_eure, defaults.slippage_bps]); async function handlePreflight(event) { event.preventDefault(); await onControl('trade-executor', 'intent-request-preflight', { amount_eure: form.amount_eure, slippage_bps: Number(form.slippage_bps), }); } return (
setForm((current) => ({ ...current, amount_eure: event.target.value }))} step="0.01" type="number" value={form.amount_eure} />
{`Max ${defaults.max_amount_eure || '5'} EURe per live test request`}
setForm((current) => ({ ...current, slippage_bps: event.target.value }))} step="1" type="number" value={form.slippage_bps} />
{`Max ${defaults.max_slippage_bps ?? 200} bps / 2%`}
Preflight asks solvers for a quote. It does not submit live funds.
); } function IntentRequestLifecycle({ item }) { return (
1. Request preflight
{formatTimestamp(item.created_at)}
{item.lifecycle?.preflight?.state || item.state}
{item.reason_text}
{item.reason_code}
2. Solver quote
{formatTimestamp(item.deadline_at)}
{`${item.solver_quote_count || 0} quote(s)`}
{item.quoted_destination_amount ? `Quoted ${item.quoted_destination_amount} BTC` : 'No usable quote stored'}
3. Relay submission
{formatTimestamp(item.submitted_at)}
{item.submission_status || 'Not submitted'}
Relay acceptance is not a completed trade.
4. Settlement truth
{formatTimestamp(item.resolved_at)}
{item.outcome_status || item.state}
{item.settlement_summary?.text || 'No settled inventory delta is linked to this request.'}
{item.settlement_summary?.caveat ?
{item.settlement_summary.caveat}
: null}
Raw lifecycle evidence
{stringifyJson(item.lifecycle)}
); } function IntentRequestsTable({ items, executorArmed, onControl }) { const [expanded, setExpanded] = useState(() => new Set()); if (!items?.length) return No repo-created EURe-to-BTC requests are stored yet.; function toggle(rowKey) { setExpanded((current) => { const next = new Set(current); if (next.has(rowKey)) next.delete(rowKey); else next.add(rowKey); return next; }); } return ( {items.map((item, index) => { const rowKey = item.request_id || item.idempotency_key || String(index); const isExpanded = expanded.has(rowKey); const canSubmit = item.live_submit_capable && executorArmed === true; return ( {isExpanded ? ( ) : null} ); })}
Time Request id Spend / min receive Quote / intent State Reason Settlement Action
{formatTimestamp(item.resolved_at || item.submitted_at || item.created_at)}
{`${item.source_amount} ${item.source_symbol || 'EURe'}`}
{`Min ${item.min_destination_amount} ${item.destination_symbol || 'BTC'}`}
{`${item.slippage_bps ?? 'n/a'} bps slippage`}
{item.reason_text}
{item.reason_code}
{item.settlement_summary?.text || 'No settled inventory delta linked'}
{item.has_settlement_evidence ? 'Settlement evidence present' : 'Not completed'}
{item.live_submit_capable ? ( ) : null}
{item.live_submit_capable && executorArmed !== true ? (
Executor must be armed before live submit.
) : null}
); } function LastControlResult({ result }) { if (!result) return null; return (
Last control result
{stringifyJson(result)}
); } export default function FundsPage({ funds, onControl, lastControlResult, }) { const profitability = funds.profitability; const controlState = funds.funding.control_state || {}; const externalFlowAdjusted = profitability.external_flow_adjusted; const externalFlowCount = profitability.external_flow_count || 0; const baselineLabel = externalFlowAdjusted ? 'PnL vs net funded capital' : 'PnL vs deposit baseline'; const baselineMeta = externalFlowAdjusted ? `Adjusted for ${externalFlowCount} later deposit or withdrawal flows` : 'Baseline anchored before first live trade'; const simpleHoldMeta = externalFlowAdjusted ? 'Simple hold includes later credited deposits and completed withdrawals' : 'Comparison against a no-trade simple-hold baseline'; const marketMoveMeta = externalFlowAdjusted ? 'Simple-hold market move on baseline plus later external flows' : 'Baseline mark move only'; const portfolioVsHoldMeta = externalFlowAdjusted ? 'Current minus cash-flow-adjusted simple hold; not realized trade PnL' : 'Current minus simple hold; not realized trade PnL'; return ( <>
Default view

Funds

Profitability comes first. Spendable inventory remains verifier or bridge credit only, while pre-credit funding stays separate.
{profitability.caveats.map((item) => (
{item}
))}
Current balances

Spendable and pending

{`Inventory synced ${formatTimestamp(funds.balances.synced_at)}`}
Operator controls

Safe actions

Risky treasury submit paths remain absent. Strategy and executor arm or disarm remain absent.
Own requests

EURe to BTC request creation

Create a solver quote request first, then submit only a drafted request. Completed requires inventory movement, not relay acceptance.
{funds.intent_requests?.caveat}
Funding handles

Credited and pre-credit funding

Credited deposits

Pre-credit by asset

Transfer history

Withdrawals and observations

Recent withdrawals

Recent funding observations

); }