From 7bb41f77a84791c1a105396c1692f5c9413644e3 Mon Sep 17 00:00:00 2001 From: ashalafan Date: Tue, 30 Jun 2026 22:06:22 +0300 Subject: [PATCH] Add trust and info pages --- app/src/app/audit/page.tsx | 116 ++++++ app/src/app/contracts/ContractsClient.tsx | 103 ++++++ app/src/app/contracts/page.tsx | 21 ++ app/src/app/faq/page.tsx | 68 ++++ app/src/app/info-pages.module.css | 419 ++++++++++++++++++++++ app/src/app/infoPages.tsx | 75 ++++ app/src/app/learn/page.tsx | 72 ++++ app/src/app/page.module.css | 3 +- app/src/app/page.tsx | 7 +- app/src/app/roadmap/page.tsx | 81 +++++ 10 files changed, 963 insertions(+), 2 deletions(-) create mode 100644 app/src/app/audit/page.tsx create mode 100644 app/src/app/contracts/ContractsClient.tsx create mode 100644 app/src/app/contracts/page.tsx create mode 100644 app/src/app/faq/page.tsx create mode 100644 app/src/app/info-pages.module.css create mode 100644 app/src/app/infoPages.tsx create mode 100644 app/src/app/learn/page.tsx create mode 100644 app/src/app/roadmap/page.tsx diff --git a/app/src/app/audit/page.tsx b/app/src/app/audit/page.tsx new file mode 100644 index 0000000..e599bdb --- /dev/null +++ b/app/src/app/audit/page.tsx @@ -0,0 +1,116 @@ +import type { Metadata } from 'next'; + +import { ExternalLink, InfoShell } from '../infoPages'; +import styles from '../info-pages.module.css'; + +export const metadata: Metadata = { + title: 'Audit - YieldLadder', + description: 'YieldLadder internal audit status, findings, resolutions, and security resources.', +}; + +const findings = [ + { + id: 'M-01', + severity: 'Medium', + title: 'Share checkpoint gap on compounding deposits', + resolution: 'Resolved in a3f1c2d', + }, + { + id: 'M-02', + severity: 'Medium', + title: 'Early-exit fee precision loss on small deposits', + resolution: 'Resolved in b7e4a91', + }, + { + id: 'M-03', + severity: 'Medium', + title: 'Governance timelock could be reset during pending parameter change', + resolution: 'Resolved in c2d8f05', + }, + { + id: 'L-01', + severity: 'Low', + title: 'Strategy allocation cap re-check missing after rebalance', + resolution: 'Resolved in d9a3b12', + }, + { + id: 'L-02', + severity: 'Low', + title: 'Harvester cooldown used ledger timestamp instead of ledger sequence', + resolution: 'Resolved in e5c7f44', + }, +]; + +export default function AuditPage() { + return ( + +
+
+
+ 0 +

Critical or high findings

+

Internal audit summary reports no critical or high severity findings.

+
+
+ 5 +

Resolved findings

+

Three medium and two low findings are listed with resolution commits.

+
+
+ Q2 +

External audit window

+

The roadmap targets an external audit before mainnet launch.

+
+
+
+ +
+
+

Internal audit findings

+ All resolved +
+
+ + + + + + + + + + + {findings.map((finding) => ( + + + + + + + ))} + +
IDSeverityFindingResolution
{finding.id}{finding.severity}{finding.title}{finding.resolution}
+
+
+ +
+

Security resources

+

+ YieldLadder keeps review notes and bounty scope in the public repository so users can inspect the protocol status before interacting with contracts. +

+
+ + Internal audit report + + + Bug bounty policy + +
+
+
+ ); +} diff --git a/app/src/app/contracts/ContractsClient.tsx b/app/src/app/contracts/ContractsClient.tsx new file mode 100644 index 0000000..064a610 --- /dev/null +++ b/app/src/app/contracts/ContractsClient.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { useState } from 'react'; + +import { ExternalLink } from '../infoPages'; +import styles from '../info-pages.module.css'; + +const contracts = { + testnet: [ + { name: 'Vault Factory', address: 'CC_PENDING_TESTNET', role: 'Creates and configures vault tiers' }, + { name: 'Strategy', address: 'CC_PENDING_TESTNET', role: 'Routes liquidity into the Stellar AMM strategy' }, + { name: 'Share Token', address: 'CC_PENDING_TESTNET', role: 'Tracks weighted vault share accounting' }, + { name: 'Bounty Harvester', address: 'CC_PENDING_TESTNET', role: 'Executes harvests and applies keeper bounty rules' }, + ], + mainnet: [ + { name: 'Vault Factory', address: 'Pending mainnet deployment', role: 'Awaiting audit and launch approval' }, + { name: 'Strategy', address: 'Pending mainnet deployment', role: 'Awaiting audit and launch approval' }, + { name: 'Share Token', address: 'Pending mainnet deployment', role: 'Awaiting audit and launch approval' }, + { name: 'Bounty Harvester', address: 'Pending mainnet deployment', role: 'Awaiting audit and launch approval' }, + ], +}; + +type Network = keyof typeof contracts; + +function stellarExpertUrl(network: Network, address: string) { + return `https://stellar.expert/explorer/${network}/contract/${address}`; +} + +export default function ContractsClient() { + const [network, setNetwork] = useState('testnet'); + + return ( + <> +
+
+
+

Deployment registry

+

Default view shows testnet contract identifiers from the deployment manifest.

+
+
+ {(['testnet', 'mainnet'] as Network[]).map((item) => ( + + ))} +
+
+ +
+ + + + + + + + + + + {contracts[network].map((contract) => ( + + + + + + + ))} + +
ContractAddressRoleExplorer
{contract.name} + {contract.address} + {contract.role} + {network === 'testnet' ? ( + + Open in Stellar Expert + + ) : ( + Explorer pending + )} +
+
+
+ +
+
+
+

Testnet status

+

Testnet deployment slots are reserved in the manifest and should be verified on Stellar Expert when concrete contract IDs are published.

+
+
+

Mainnet status

+

Mainnet deployment remains pending until audit, formal verification, and final launch controls are complete.

+
+
+
+ + ); +} diff --git a/app/src/app/contracts/page.tsx b/app/src/app/contracts/page.tsx new file mode 100644 index 0000000..f0a3836 --- /dev/null +++ b/app/src/app/contracts/page.tsx @@ -0,0 +1,21 @@ +import type { Metadata } from 'next'; + +import { InfoShell } from '../infoPages'; +import ContractsClient from './ContractsClient'; + +export const metadata: Metadata = { + title: 'Contracts - YieldLadder', + description: 'YieldLadder contract deployment registry with testnet explorer links and mainnet status.', +}; + +export default function ContractsPage() { + return ( + + + + ); +} diff --git a/app/src/app/faq/page.tsx b/app/src/app/faq/page.tsx new file mode 100644 index 0000000..f461bf4 --- /dev/null +++ b/app/src/app/faq/page.tsx @@ -0,0 +1,68 @@ +import type { Metadata } from 'next'; + +import { InfoShell } from '../infoPages'; +import styles from '../info-pages.module.css'; + +export const metadata: Metadata = { + title: 'FAQ - YieldLadder', + description: 'Common questions about YieldLadder vaults, wallets, deposits, risk, and taxes.', +}; + +const faqs = [ + { + question: 'What is YieldLadder?', + answer: + 'YieldLadder is a non-custodial Soroban protocol for time-locked USDC vaults on Stellar. Deposits are routed into curated AMM liquidity strategies, and realized trading-fee yield is distributed by share units.', + }, + { + question: 'Which vault tiers are planned?', + answer: + 'The landing page currently presents Flex, 3-month, 6-month, and 12-month vault tiers. Longer locks receive larger share multipliers and may have higher minimum deposits.', + }, + { + question: 'What are the current minimum deposits?', + answer: + 'The current interface shows 1 USDC for Flex, 50 USDC for 3-month, 100 USDC for 6-month, and 250 USDC for 12-month vaults.', + }, + { + question: 'Which wallets are supported?', + answer: + 'The app is built around Stellar-compatible wallets, with Freighter support present in the current interface. Additional wallet support should be verified against the latest release notes before mainnet deposits.', + }, + { + question: 'Is yield guaranteed?', + answer: + 'No. Yield depends on AMM trading fees and market activity. Smart contract risk, impermanent loss, stablecoin depeg risk, network risk, and regulatory risk can all affect outcomes.', + }, + { + question: 'Can I exit a locked vault early?', + answer: + 'Early exit support is part of the vault model, but locked tiers can charge a fee. Users should check the final contract parameters before depositing.', + }, + { + question: 'Is this tax advice?', + answer: + 'No. YieldLadder pages are technical product information only. Users should consult a qualified tax professional for their jurisdiction.', + }, +]; + +export default function FaqPage() { + return ( + +
+
+ {faqs.map((faq) => ( +
+ {faq.question} +
{faq.answer}
+
+ ))} +
+
+
+ ); +} diff --git a/app/src/app/info-pages.module.css b/app/src/app/info-pages.module.css new file mode 100644 index 0000000..ac38f98 --- /dev/null +++ b/app/src/app/info-pages.module.css @@ -0,0 +1,419 @@ +.page { + min-height: 100vh; + background: + radial-gradient(circle at top left, rgba(56, 189, 248, 0.08), transparent 34rem), + linear-gradient(180deg, #060810 0%, #0b1220 62%, #060810 100%); + color: #f1f5f9; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; +} + +.nav { + display: grid; + grid-template-columns: auto 1fr auto; + align-items: center; + gap: 1.25rem; + padding: 1.1rem 2rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + background: rgba(6, 8, 16, 0.78); + backdrop-filter: blur(16px); +} + +.logo { + color: #f8fafc; + font-size: 1.125rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.navLinks, +.footerLinks { + display: flex; + align-items: center; + justify-content: center; + flex-wrap: wrap; + gap: 0.75rem 1.1rem; +} + +.navLink, +.footerLinks a, +.footerBack { + color: #94a3b8; + font-size: 0.875rem; + text-decoration: none; + transition: color 0.15s ease; +} + +.navLink:hover, +.footerLinks a:hover, +.footerBack:hover { + color: #e2e8f0; +} + +.navCta { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 38px; + padding: 0 0.95rem; + border: 1px solid rgba(14, 165, 233, 0.35); + border-radius: 8px; + color: #7dd3fc; + font-size: 0.875rem; + font-weight: 600; + text-decoration: none; + background: rgba(14, 165, 233, 0.08); +} + +.main { + width: min(1120px, calc(100% - 2rem)); + margin: 0 auto; + padding: 4.5rem 0 3.5rem; +} + +.hero { + max-width: 760px; + padding-bottom: 2.5rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); +} + +.eyebrow { + margin: 0 0 0.9rem; + color: #38bdf8; + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.title { + margin: 0; + color: #f8fafc; + font-size: clamp(2.2rem, 5vw, 4.4rem); + font-weight: 800; + line-height: 0.98; +} + +.lead { + margin: 1.25rem 0 0; + color: #cbd5e1; + font-size: 1.0625rem; + line-height: 1.7; +} + +.section { + margin-top: 3rem; +} + +.sectionHeader { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: 1rem; + margin-bottom: 1rem; +} + +.section h2, +.sectionHeader h2 { + margin: 0; + color: #f1f5f9; + font-size: 1.35rem; + letter-spacing: -0.02em; +} + +.section p { + color: #94a3b8; + line-height: 1.7; +} + +.grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1rem; +} + +.twoColumn { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1rem; +} + +.card, +.metric, +.finding, +.accordionItem { + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 8px; + background: rgba(15, 23, 42, 0.58); +} + +.card, +.metric, +.finding { + padding: 1.25rem; +} + +.card h3, +.metric h3, +.finding h3 { + margin: 0 0 0.65rem; + color: #e2e8f0; + font-size: 1rem; +} + +.card p, +.metric p, +.finding p { + margin: 0; + color: #94a3b8; + font-size: 0.9375rem; + line-height: 1.65; +} + +.metricValue { + display: block; + margin-bottom: 0.45rem; + color: #f8fafc; + font-size: 1.8rem; + font-weight: 800; + letter-spacing: -0.03em; +} + +.muted { + color: #64748b; +} + +.example { + display: grid; + grid-template-columns: 1fr auto 1fr; + align-items: stretch; + gap: 1rem; +} + +.operator { + display: flex; + align-items: center; + justify-content: center; + color: #38bdf8; + font-weight: 800; + min-width: 2rem; +} + +.tableWrap { + overflow-x: auto; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 8px; + background: rgba(15, 23, 42, 0.52); +} + +.table { + width: 100%; + min-width: 680px; + border-collapse: collapse; +} + +.table th, +.table td { + padding: 0.9rem 1rem; + border-bottom: 1px solid rgba(255, 255, 255, 0.07); + text-align: left; + vertical-align: top; +} + +.table th { + color: #cbd5e1; + font-size: 0.75rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.table td { + color: #94a3b8; + font-size: 0.9rem; + line-height: 1.5; +} + +.table tr:last-child td { + border-bottom: 0; +} + +.code { + display: inline-flex; + max-width: 100%; + padding: 0.18rem 0.35rem; + border-radius: 6px; + color: #bae6fd; + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', monospace; + font-size: 0.8125rem; + background: rgba(14, 165, 233, 0.12); + overflow-wrap: anywhere; +} + +.pill, +.toggleButton, +.activeToggle { + display: inline-flex; + align-items: center; + justify-content: center; + min-height: 34px; + padding: 0 0.75rem; + border-radius: 999px; + font-size: 0.8125rem; + font-weight: 700; +} + +.pill { + color: #a7f3d0; + border: 1px solid rgba(16, 185, 129, 0.28); + background: rgba(16, 185, 129, 0.1); +} + +.toggle { + display: inline-flex; + gap: 0.4rem; + padding: 0.3rem; + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 999px; + background: rgba(15, 23, 42, 0.62); +} + +.toggleButton, +.activeToggle { + border: 0; + cursor: pointer; +} + +.toggleButton { + color: #94a3b8; + background: transparent; +} + +.activeToggle { + color: #082f49; + background: #7dd3fc; +} + +.accordion { + display: grid; + gap: 0.75rem; +} + +.accordionItem { + padding: 0; + overflow: hidden; +} + +.accordionItem summary { + cursor: pointer; + padding: 1rem 1.15rem; + color: #e2e8f0; + font-weight: 700; + list-style: none; +} + +.accordionItem summary::-webkit-details-marker { + display: none; +} + +.accordionItem summary::after { + content: '+'; + float: right; + color: #38bdf8; +} + +.accordionItem[open] summary::after { + content: '-'; +} + +.accordionBody { + padding: 0 1.15rem 1.15rem; + color: #94a3b8; + font-size: 0.9375rem; + line-height: 1.7; +} + +.timeline { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1rem; +} + +.timelineList { + display: grid; + gap: 0.75rem; + margin: 1rem 0 0; + padding: 0; + list-style: none; +} + +.timelineList li { + padding-left: 1rem; + border-left: 2px solid rgba(56, 189, 248, 0.35); + color: #94a3b8; + line-height: 1.6; +} + +.linkList { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + margin-top: 1rem; +} + +.linkList a, +.table a, +.section a { + color: #7dd3fc; + text-decoration: none; +} + +.linkList a:hover, +.table a:hover, +.section a:hover { + color: #bae6fd; +} + +.footer { + display: flex; + align-items: center; + justify-content: space-between; + flex-wrap: wrap; + gap: 1rem; + width: min(1120px, calc(100% - 2rem)); + margin: 0 auto; + padding: 1.5rem 0 3rem; + border-top: 1px solid rgba(255, 255, 255, 0.07); +} + +@media (max-width: 780px) { + .nav { + grid-template-columns: 1fr; + justify-items: start; + padding: 1rem; + } + + .navLinks { + justify-content: flex-start; + } + + .main { + padding-top: 3rem; + } + + .sectionHeader, + .footer { + align-items: flex-start; + flex-direction: column; + } + + .grid, + .twoColumn, + .example, + .timeline { + grid-template-columns: 1fr; + } + + .operator { + min-height: 1rem; + } +} diff --git a/app/src/app/infoPages.tsx b/app/src/app/infoPages.tsx new file mode 100644 index 0000000..2fcebd3 --- /dev/null +++ b/app/src/app/infoPages.tsx @@ -0,0 +1,75 @@ +import Link from 'next/link'; + +import styles from './info-pages.module.css'; + +type InfoShellProps = { + eyebrow: string; + title: string; + lead: string; + children: React.ReactNode; +}; + +const trustLinks = [ + { href: '/learn', label: 'Learn' }, + { href: '/faq', label: 'FAQ' }, + { href: '/audit', label: 'Audit' }, + { href: '/contracts', label: 'Contracts' }, + { href: '/roadmap', label: 'Roadmap' }, +]; + +export function InfoShell({ eyebrow, title, lead, children }: InfoShellProps) { + return ( +
+ + +
+
+

{eyebrow}

+

{title}

+

{lead}

+
+ {children} +
+ +
+ + Back to YieldLadder + +
+ {trustLinks.map((item) => ( + + {item.label} + + ))} +
+
+
+ ); +} + +type ExternalLinkProps = { + href: string; + children: React.ReactNode; +}; + +export function ExternalLink({ href, children }: ExternalLinkProps) { + return ( + + {children} + + ); +} diff --git a/app/src/app/learn/page.tsx b/app/src/app/learn/page.tsx new file mode 100644 index 0000000..9e14728 --- /dev/null +++ b/app/src/app/learn/page.tsx @@ -0,0 +1,72 @@ +import type { Metadata } from 'next'; + +import { InfoShell } from '../infoPages'; +import styles from '../info-pages.module.css'; + +export const metadata: Metadata = { + title: 'Learn - YieldLadder', + description: 'How YieldLadder time-locked USDC vaults, share units, harvesting, and early exits work.', +}; + +export default function LearnPage() { + return ( + +
+
+
+

Deposit USDC

+

Choose a vault tier, connect a Stellar wallet, and deposit USDC into the non-custodial Soroban contracts.

+
+
+

Receive share units

+

Each deposit mints internal accounting shares. Longer lock tiers receive a higher multiplier and therefore a larger claim on harvested yield.

+
+
+

Harvest AMM fees

+

Harvests realize trading-fee yield from the underlying Stellar AMM strategy and compound it back into vault accounting.

+
+
+
+ +
+
+

Worked share-unit example

+ Example numbers +
+
+
+ 100 +

Flex shares

+

A 100 USDC Flex deposit uses a 1.00x multiplier, so it receives 100 share units.

+
+
vs
+
+ 140 +

12-month shares

+

A 100 USDC 12-month deposit uses a 1.40x multiplier, so it receives 140 share units.

+
+
+

+ If a harvest distributes 24 USDC across 240 total share units, the Flex position receives 10 USDC and the 12-month position receives 14 USDC. Both deposited the same principal, but the longer lock receives the larger yield allocation because it contributed more weighted shares. +

+
+ +
+
+
+

Why the harvest bounty exists

+

Harvesting costs time and network fees. A small 10 bps bounty gives any keeper a reason to execute harvests promptly instead of relying on a single operator.

+
+
+

Early exits

+

Locked tiers can include an early-exit fee. The fee is redistributed by the protocol rules instead of becoming a discretionary protocol revenue stream.

+
+
+
+
+ ); +} diff --git a/app/src/app/page.module.css b/app/src/app/page.module.css index 4b66fef..0acd1a8 100644 --- a/app/src/app/page.module.css +++ b/app/src/app/page.module.css @@ -448,6 +448,7 @@ display: flex; gap: 2rem; justify-content: center; + flex-wrap: wrap; margin-bottom: 2rem; } @@ -588,4 +589,4 @@ display: inline-flex; align-items: center; gap: 0.3rem; -} \ No newline at end of file +} diff --git a/app/src/app/page.tsx b/app/src/app/page.tsx index dc7ec55..113f6e5 100644 --- a/app/src/app/page.tsx +++ b/app/src/app/page.tsx @@ -248,6 +248,11 @@ export default function Home() { Updates Security + Learn + FAQ + Audit + Contracts + Roadmap Terms Privacy @@ -263,4 +268,4 @@ export default function Home() { ); -} \ No newline at end of file +} diff --git a/app/src/app/roadmap/page.tsx b/app/src/app/roadmap/page.tsx new file mode 100644 index 0000000..e79d6a7 --- /dev/null +++ b/app/src/app/roadmap/page.tsx @@ -0,0 +1,81 @@ +import type { Metadata } from 'next'; + +import { InfoShell } from '../infoPages'; +import styles from '../info-pages.module.css'; + +export const metadata: Metadata = { + title: 'Roadmap - YieldLadder', + description: 'Current YieldLadder roadmap across shipped work, in-progress work, and planned launch milestones.', +}; + +const columns = [ + { + title: 'Shipped', + status: 'Current', + items: [ + 'Landing page, vault tier presentation, legal pages, and dashboard scaffold.', + 'Internal audit report with all listed medium and low findings resolved.', + 'Security and bounty policy published in the repository.', + 'Deployment manifests prepared for testnet and mainnet tracking.', + ], + }, + { + title: 'In progress', + status: 'Buildout', + items: [ + 'Soroban contract and SDK work for deposits, withdrawals, shares, harvesting, and strategy flows.', + 'Trust pages for learn, FAQ, audit, contracts, and roadmap visibility.', + 'Testnet deployment verification and user-facing contract address publication.', + 'Formal verification and external audit preparation.', + ], + }, + { + title: 'Planned', + status: 'Next', + items: [ + 'External audit completion before mainnet launch.', + 'Mainnet deployment after audit, verification, and launch controls.', + 'Expanded wallet support and user onboarding improvements.', + 'Post-launch analytics, keeper monitoring, and risk reporting.', + ], + }, +]; + +export default function RoadmapPage() { + return ( + +
+
+ {columns.map((column) => ( +
+ {column.status} +

{column.title}

+
    + {column.items.map((item) => ( +
  • {item}
  • + ))} +
+
+ ))} +
+
+ +
+
+
+

Current state

+

Contracts are not marked as mainnet deployed in the repository manifest. Users should treat YieldLadder as pre-launch until final deployment addresses and audit outcomes are published.

+
+
+

Launch gate

+

Mainnet readiness depends on verified contract deployment, external review, and clear risk communication for depositors.

+
+
+
+
+ ); +}