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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 116 additions & 0 deletions app/src/app/audit/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<InfoShell
eyebrow="Security"
title="Audit status"
lead="The current repository documents an internal review with no critical or high findings, plus a bug bounty program for responsible security disclosures."
>
<section className={styles.section}>
<div className={styles.grid}>
<article className={styles.metric}>
<span className={styles.metricValue}>0</span>
<h3>Critical or high findings</h3>
<p>Internal audit summary reports no critical or high severity findings.</p>
</article>
<article className={styles.metric}>
<span className={styles.metricValue}>5</span>
<h3>Resolved findings</h3>
<p>Three medium and two low findings are listed with resolution commits.</p>
</article>
<article className={styles.metric}>
<span className={styles.metricValue}>Q2</span>
<h3>External audit window</h3>
<p>The roadmap targets an external audit before mainnet launch.</p>
</article>
</div>
</section>

<section className={styles.section}>
<div className={styles.sectionHeader}>
<h2>Internal audit findings</h2>
<span className={styles.pill}>All resolved</span>
</div>
<div className={styles.tableWrap}>
<table className={styles.table}>
<thead>
<tr>
<th>ID</th>
<th>Severity</th>
<th>Finding</th>
<th>Resolution</th>
</tr>
</thead>
<tbody>
{findings.map((finding) => (
<tr key={finding.id}>
<td>{finding.id}</td>
<td>{finding.severity}</td>
<td>{finding.title}</td>
<td>{finding.resolution}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>

<section className={styles.section}>
<h2>Security resources</h2>
<p>
YieldLadder keeps review notes and bounty scope in the public repository so users can inspect the protocol status before interacting with contracts.
</p>
<div className={styles.linkList}>
<ExternalLink href="https://github.com/LadderMine/yieldladder/blob/main/audit/internal-2026-01.md">
Internal audit report
</ExternalLink>
<ExternalLink href="https://github.com/LadderMine/yieldladder/blob/main/security/bounty.md">
Bug bounty policy
</ExternalLink>
</div>
</section>
</InfoShell>
);
}
103 changes: 103 additions & 0 deletions app/src/app/contracts/ContractsClient.tsx
Original file line number Diff line number Diff line change
@@ -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<Network>('testnet');

return (
<>
<section className={styles.section}>
<div className={styles.sectionHeader}>
<div>
<h2>Deployment registry</h2>
<p className={styles.muted}>Default view shows testnet contract identifiers from the deployment manifest.</p>
</div>
<div className={styles.toggle} role="tablist" aria-label="Network">
{(['testnet', 'mainnet'] as Network[]).map((item) => (
<button
key={item}
type="button"
className={network === item ? styles.activeToggle : styles.toggleButton}
onClick={() => setNetwork(item)}
aria-pressed={network === item}
>
{item}
</button>
))}
</div>
</div>

<div className={styles.tableWrap}>
<table className={styles.table}>
<thead>
<tr>
<th>Contract</th>
<th>Address</th>
<th>Role</th>
<th>Explorer</th>
</tr>
</thead>
<tbody>
{contracts[network].map((contract) => (
<tr key={`${network}-${contract.name}`}>
<td>{contract.name}</td>
<td>
<span className={styles.code}>{contract.address}</span>
</td>
<td>{contract.role}</td>
<td>
{network === 'testnet' ? (
<ExternalLink href={stellarExpertUrl(network, contract.address)}>
Open in Stellar Expert
</ExternalLink>
) : (
<span className={styles.muted}>Explorer pending</span>
)}
</td>
</tr>
))}
</tbody>
</table>
</div>
</section>

<section className={styles.section}>
<div className={styles.twoColumn}>
<article className={styles.card}>
<h3>Testnet status</h3>
<p>Testnet deployment slots are reserved in the manifest and should be verified on Stellar Expert when concrete contract IDs are published.</p>
</article>
<article className={styles.card}>
<h3>Mainnet status</h3>
<p>Mainnet deployment remains pending until audit, formal verification, and final launch controls are complete.</p>
</article>
</div>
</section>
</>
);
}
21 changes: 21 additions & 0 deletions app/src/app/contracts/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<InfoShell
eyebrow="Registry"
title="Contracts"
lead="A transparent deployment registry for users who want to verify contract roles, addresses, and network status before interacting with YieldLadder."
>
<ContractsClient />
</InfoShell>
);
}
68 changes: 68 additions & 0 deletions app/src/app/faq/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<InfoShell
eyebrow="Questions"
title="FAQ"
lead="Short answers for users checking the protocol before connecting a wallet or evaluating a vault tier."
>
<section className={styles.section}>
<div className={styles.accordion}>
{faqs.map((faq) => (
<details key={faq.question} className={styles.accordionItem}>
<summary>{faq.question}</summary>
<div className={styles.accordionBody}>{faq.answer}</div>
</details>
))}
</div>
</section>
</InfoShell>
);
}
Loading