Skip to content
Merged
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
21 changes: 21 additions & 0 deletions docs/stash-exports/2026-07-30-backend-migration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Backend migration stash exports

These patches preserve the three non-empty RipGuard stashes that existed before the 2026-07-30 backend migration. Each file is the verbatim output of `git stash show -p --binary --include-untracked` and retains blob IDs for `git apply --3way` recovery.

None applied cleanly forward or in reverse to `origin/main` at `1dd284b`, so none was treated as empty or already landed.

| Patch | Stash commit | First parent | Original message |
| --- | --- | --- | --- |
| `testnet-landing-polish-fd51ae9.patch` | `fd51ae94223aa486184b588a890a249d278f32ca` | `4c553dd1445820b2842eaad61853fe1d3791d651` | `WIP on testnet: 4c553dd visual: polish landing page fold` |
| `testnet-client-providers-2623bb8.patch` | `2623bb8d39a76a80c5b9214251c5118365f3d2ac` | `1ea67c8eccc7d5603e8a3115348eb71ee06e960b` | `On testnet: testnet: ClientProviders ssr:false fix + untracked files` |
| `exit-strategy-readme-14906e5.patch` | `14906e5646c3b45029333650e15111c2b1eb7b9f` | `83aa38e657a88356a456548c64d391ea17a2ac1e` | `WIP on feat/exit-strategy-lab: 97f1f5b Fix testnet vault visibility: use 2k block chunks on Base Sepolia` |

## Recovery

Start from the patch's first-parent commit when possible, then run:

```sh
git apply --3way path/to/export.patch
```

Review before committing. These patches predate the current multi-chain and Solana code and may need manual conflict resolution.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
diff --git a/README.md b/README.md
index ab57bd6..baa6b5b 100644
--- a/README.md
+++ b/README.md
@@ -34,21 +34,14 @@ Or set a custom schedule.

## Architecture

-The `RipGuardRouter` is a stateless, non-upgradeable wrapper around Sablier Lockup. It:
-
-- Pulls USDC from the user
-- Creates a non-cancelable Sablier Lockup Linear stream
-- Routes a 0.5% broker fee to the treasury via Sablier's native mechanism
-- Never holds funds beyond a single transaction
-- Has no admin functions, no owner, no pause
+RipGuard has no custom on-chain contract. The frontend calls the Sablier Lockup v2.0 contract directly — your USDC goes from your wallet into Sablier's immutable protocol. The 0.5% broker fee is collected via Sablier's native broker mechanism at stream creation time; RipGuard never holds your funds.

**On-chain contracts (Base Mainnet):**

-- **RipGuardRouter**: [`packages/contracts/src/RipGuardRouter.sol`](packages/contracts/src/RipGuardRouter.sol)
- **Sablier Lockup**: [`0xb5D78DD3276325f5FAF3106Cc4Acc56E28e0Fe3B`](https://basescan.org/address/0xb5D78DD3276325f5FAF3106Cc4Acc56E28e0Fe3B) (Sablier Lockup v2.0)
- **USDC**: [`0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`](https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913)

-All streams are created as **non-cancelable, non-transferable** Lockup Linear streams with both sender and recipient set to the depositor.
+All streams are created as **non-cancelable, non-transferable** Lockup Linear streams with both sender and recipient set to the depositor. No RipGuard contract exists between you and Sablier.

## Stack

@@ -88,7 +81,7 @@ forge test

## Security

-The `RipGuardRouter` is intentionally minimal — it holds no state, has no admin functions, and cannot be upgraded. All funds flow through [Sablier's audited Lockup protocol](https://docs.sablier.com/contracts/v2/security). RipGuard never custodies funds; the router is stateless and all locks are enforced by Sablier's battle-tested on-chain contracts.
+RipGuard has no custom contract between you and your funds. The app calls the Sablier Lockup v2.0 contract directly — all locks are created in and enforced by [Sablier's audited, immutable protocol](https://docs.sablier.com/contracts/v2/security). The only RipGuard-authored code is the frontend UI and a reference `RipGuardRouter.sol` in `packages/contracts/` (undeployed, written as a fee-routing reference implementation).

If you find a vulnerability, please report it responsibly by emailing the team rather than opening a public issue.

Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
diff --git a/packages/app/src/app/layout.tsx b/packages/app/src/app/layout.tsx
index c7fe435..72191dd 100644
--- a/packages/app/src/app/layout.tsx
+++ b/packages/app/src/app/layout.tsx
@@ -1,7 +1,7 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
-import { Providers } from "@/providers";
import { TestnetBanner } from "@/components/TestnetBanner";
+import { ClientProviders } from "@/components/ClientProviders";
import "./globals.css";

const geistSans = Geist({
@@ -50,7 +50,7 @@ export default function RootLayout({
className={`${geistSans.variable} ${geistMono.variable} antialiased bg-black text-white`}
>
<TestnetBanner />
- <Providers>{children}</Providers>
+ <ClientProviders>{children}</ClientProviders>
</body>
</html>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
diff --git a/packages/app/src/app/page.tsx b/packages/app/src/app/page.tsx
index cd61e21..7773b31 100644
--- a/packages/app/src/app/page.tsx
+++ b/packages/app/src/app/page.tsx
@@ -94,6 +94,10 @@ function FAQItem({
);
}

+import Image from "next/image";
+
+// ... existing icons ...
+
export default function Home() {
const sablierExplorerUrl = `${EXPLORER_URL}/address/${SABLIER_LOCKUP}`;
const githubUrl = "https://github.com/fielding/ripguard";
@@ -113,19 +117,17 @@ export default function Home() {
<div className="absolute right-[6%] top-[22%] h-40 w-40 rounded-full bg-cyan/[0.08] blur-[90px]" />
</div>

- <div className="relative z-10 mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-[1.08fr_0.92fr]">
+ <div className="relative z-10 mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-[1fr_1fr]">
<div className="max-w-2xl">
- <div className="surface-pill mb-6 text-[11px] font-semibold uppercase tracking-[0.28em]">
+ <div className="surface-pill mb-10 text-[11px] font-semibold uppercase tracking-[0.28em]">
Built on Sablier&apos;s audited contracts
</div>

- <RipGuardLockup className="mb-7" />
-
- <h1 className="max-w-2xl text-4xl font-semibold tracking-[-0.04em] text-white sm:text-[4.35rem] sm:leading-[0.98]">
+ <h1 className="max-w-2xl text-5xl font-semibold tracking-[-0.04em] text-white sm:text-[4.8rem] sm:leading-[0.98]">
Lock your winnings before you give them back.
</h1>

- <p className="mt-6 max-w-xl text-base leading-8 text-white/62 sm:text-lg">
+ <p className="mt-8 max-w-xl text-base leading-8 text-white/62 sm:text-lg">
The &ldquo;I&apos;m up, get me out&rdquo; button for Base traders. Send USDC
straight into the{" "}
<a
@@ -139,21 +141,21 @@ export default function Home() {
with a schedule you choose, so future-you can&apos;t instantly ape back in.
</p>

- <div className="mt-8 flex flex-col gap-3 sm:flex-row">
- <Link href="/create" className="button-primary px-7 text-base sm:px-8 sm:text-lg">
+ <div className="mt-10 flex flex-col gap-3 sm:flex-row">
+ <Link href="/create" className="button-primary px-8 text-base sm:px-10 sm:text-lg">
Create a Lock
</Link>
<a
href={sablierExplorerUrl}
target="_blank"
rel="noopener noreferrer"
- className="button-secondary px-7 text-base sm:px-8 sm:text-lg"
+ className="button-secondary px-8 text-base sm:px-10 sm:text-lg"
>
View Contract
</a>
</div>

- <div className="mt-6 flex flex-wrap gap-2.5">
+ <div className="mt-8 flex flex-wrap gap-2.5">
{HERO_PROOF_POINTS.map((point) => (
<span key={point} className="surface-pill text-xs font-medium">
<span className="h-1.5 w-1.5 rounded-full bg-cyan shadow-[0_0_12px_rgba(0,229,255,0.45)]" />
@@ -161,62 +163,59 @@ export default function Home() {
</span>
))}
</div>
-
- {!IS_TESTNET && (
- <a
- href="https://testnet.ripguard.xyz"
- className="mt-5 inline-flex text-sm text-white/34 hover:text-cyan/75 transition-colors"
- >
- Want to test the flow first? Use the testnet version &rarr;
- </a>
- )}
</div>

<div className="relative">
- <div className="hero-stage p-5 sm:p-6">
- <div className="flex items-center justify-between rounded-2xl border border-white/6 bg-black/20 px-4 py-3 text-[11px] uppercase tracking-[0.22em] text-white/35">
- <span>RipGuard Vault Engine</span>
- <span>Base Mainnet</span>
- </div>
-
- <div className="relative mt-5 overflow-hidden rounded-[1.6rem] border border-cyan/12 bg-[linear-gradient(180deg,rgba(6,20,25,0.92)_0%,rgba(4,10,13,0.98)_100%)] px-6 py-8 sm:px-8">
- <div className="absolute left-1/2 top-4 h-44 w-44 -translate-x-1/2 rounded-full bg-cyan/[0.12] blur-[80px]" />
- <div className="relative flex flex-col items-center text-center">
- <div className="animate-float">
- <RipGuardMark className="h-32 w-32 sm:h-36 sm:w-36" />
- </div>
- <p className="mt-5 text-sm uppercase tracking-[0.3em] text-white/28">
- Discipline, enforced on-chain
- </p>
- <p className="mt-3 max-w-sm text-sm leading-7 text-white/52">
- Your USDC routes directly into Sablier Lockup. No RipGuard custody.
- No admin override. No cancel path.
- </p>
+ <div className="hero-stage group aspect-[4/5] overflow-hidden rounded-[2.5rem] border-white/10 sm:aspect-square lg:aspect-[4/5]">
+ <Image
+ src="/hero-full.png"
+ alt="RipGuard Background"
+ fill
+ className="object-cover opacity-40 mix-blend-luminosity grayscale transition-transform duration-1000 group-hover:scale-110"
+ />
+ <div className="absolute inset-0 bg-gradient-to-t from-[#0a0a0a] via-[#0a0a0a]/40 to-[#0a0a0a]/80" />
+
+ <div className="absolute inset-0 flex flex-col items-center justify-center p-8">
+ <div className="relative w-full max-w-[448px] sm:max-w-[512px] aspect-square animate-float mt-[-20px]">
+ <Image
+ src="/vault-logo.png"
+ alt="RipGuard Vault"
+ fill
+ className="object-contain glow-cyan drop-shadow-[0_0_60px_rgba(0,229,255,0.25)]"
+ priority
+ />
</div>

- <div className="relative mt-7 grid gap-3">
- {Object.entries(PRESETS).map(([key, preset]) => (
- <Link
- key={key}
- href={`/create?preset=${key}`}
- className="flex items-center justify-between rounded-2xl border border-white/8 bg-white/[0.03] px-4 py-3.5 text-left transition-colors hover:border-cyan/24 hover:bg-cyan/[0.05]"
- >
- <div className="flex items-center gap-3">
- <div className="flex h-11 w-11 items-center justify-center rounded-2xl border border-cyan/15 bg-cyan/[0.08]">
- {PRESET_ICONS[key]}
+ <div className="relative mt-8 w-full max-w-sm">
+ <div className="grid gap-2">
+ {Object.entries(PRESETS).map(([key, preset]) => (
+ <Link
+ key={key}
+ href={`/create?preset=${key}`}
+ className="flex items-center justify-between rounded-xl border border-white/10 bg-black/40 backdrop-blur-md px-5 py-4 text-left transition-all hover:border-cyan/40 hover:bg-cyan/[0.08] hover:translate-y-[-1px]"
+ >
+ <div className="flex items-center gap-4">
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg border border-cyan/20 bg-cyan/[0.1]">
+ {PRESET_ICONS[key]}
+ </div>
+ <p className="text-sm font-semibold text-white/90">{preset.label}</p>
</div>
- <div>
- <p className="text-sm font-semibold text-white/88">{preset.label}</p>
- <p className="text-xs text-white/38">{preset.description}</p>
- </div>
- </div>
- <span className="text-xs font-medium uppercase tracking-[0.18em] text-cyan/72">
- Launch
- </span>
- </Link>
- ))}
+ <span className="text-[10px] font-bold uppercase tracking-[0.2em] text-cyan">
+ Launch
+ </span>
+ </Link>
+ ))}
+ </div>
</div>
</div>
+
+ <div className="absolute top-8 left-8 right-8 flex items-center justify-between rounded-full border border-white/10 bg-black/40 backdrop-blur-md px-6 py-3 text-[10px] uppercase tracking-[0.3em] text-white/60">
+ <span className="flex items-center gap-2">
+ <span className="h-1.5 w-1.5 rounded-full bg-cyan shadow-[0_0_10px_rgba(0,229,255,0.6)]" />
+ RipGuard Engine
+ </span>
+ <span>Base Mainnet</span>
+ </div>
</div>
</div>
</div>
diff --git a/packages/app/src/components/Brand.tsx b/packages/app/src/components/Brand.tsx
index b1d4f45..00cbb72 100644
--- a/packages/app/src/components/Brand.tsx
+++ b/packages/app/src/components/Brand.tsx
@@ -1,8 +1,24 @@
+import Image from "next/image";
+
function cn(...parts: Array<string | undefined>) {
return parts.filter(Boolean).join(" ");
}

-export function RipGuardMark({ className }: { className?: string }) {
+export function RipGuardMark({ className, useImage = false }: { className?: string; useImage?: boolean }) {
+ if (useImage) {
+ return (
+ <div className={cn("relative overflow-hidden", className)}>
+ <Image
+ src="/vault-logo.png"
+ alt="RipGuard Vault"
+ fill
+ className="object-contain glow-cyan"
+ priority
+ />
+ </div>
+ );
+ }
+
return (
<svg
viewBox="0 0 160 160"
@@ -80,13 +96,15 @@ export function RipGuardWordmark({
export function RipGuardLockup({
className,
showTagline = true,
+ useImage = true,
}: {
className?: string;
showTagline?: boolean;
+ useImage?: boolean;
}) {
return (
<div className={cn("flex items-center gap-4", className)}>
- <RipGuardMark className="h-16 w-16 sm:h-20 sm:w-20 shrink-0" />
+ <RipGuardMark useImage={useImage} className="h-14 w-14 sm:h-16 sm:w-16 shrink-0" />
<RipGuardWordmark showTagline={showTagline} />
</div>
);
Loading
Loading