Skip to content

CodeWithShamim/Vestal

Repository files navigation

Vestal — the unruggable token launchpad on Ritual Chain

Launch terms are physics, not promises.

Vestal is a full-stack token launchpad where the rules of a launch — LP lock, team vesting, dev-wallet sell caps, monitoring cadence — are not published policies but on-chain physics. Every launch commits a covenant whose enforcement lives inside the token's own transfer path and whose custody belongs to a sovereign AI guardian on Ritual Chain: an agent that runs in a TEE, holds its keys via DKMS (no human ever has them), wakes itself through the chain's native Scheduler, and is revived from checkpoint by consensus if it ever crashes. After launch, no one — not the creator, not Vestal, not anyone — holds an admin key over any part of the system.

This repository contains the complete stack:

Layer Where What
Smart contracts contracts/ Foundry project: token, covenant, factories, registry, AMM, guardian providers. See contracts/README.md for contract-level detail.
Chain client layer src/chain/ viem reads/writes: registry enumeration, covenant state, enforcement log, market data, wallet connection, the createLaunch write.
Frontend src/ React 18 + Vite + Tailwind v4 app: landing, explore grid, token detail with Guardian Panel and live market, 4-step launch wizard, docs.

Live on Ritual Chain testnet (chain id 1979, ~5 blocks/s) — the app reads all launch, guardian, and market state directly from deployed contracts. There are no mocks in the data path.


Table of contents


How it works — the big picture

                                    FRONTEND (React + viem)
   Landing · Explore · Token detail (Guardian Panel, price chart, trade widget) · Launch wizard · Docs
        │  reads: registry, covenant, events, pool          │  writes: createLaunch, buy, sell, open market
        ▼                                                   ▼
┌─────────────────────────────── RITUAL CHAIN TESTNET (id 1979) ───────────────────────────────┐
│                                                                                              │
│  Creator ──► VestalLaunchFactory ──► VestalToken (fixed supply, covenant hook)               │
│                     │                      │  every transfer checked by                      │
│                     │                      ▼                                                 │
│                     ├────────────► GuardianCovenant (custody: vesting + LP, enforcement log) │
│                     │                      ▲  onlyGuardian enforcement                       │
│                     │                      │                                                 │
│                     ├── IGuardianProvider ─┴─► sovereign agent (TEE, DKMS keys)              │
│                     │        ├ RitualGuardianProvider → 0x080C provision, 0x0820 heartbeat   │
│                     │        └ MockGuardianProvider  → local anvil / pre-precompile testnet  │
│                     │                                                                        │
│                     └────────────► CovenantRegistry (append-only launch index)               │
│                                                                                              │
│  VestalPoolFactory ──► LaunchPool (native-paired constant-product AMM, ERC20 LP shares)      │
│                              └── LP shares locked in the GuardianCovenant via lockLp()       │
│                                                                                              │
│  Ritual primitives: Scheduler (self-scheduled wake-ups) · DKMS (keys only in TEE) ·          │
│                     heartbeat monitoring + consensus revival from checkpoint                 │
└──────────────────────────────────────────────────────────────────────────────────────────────┘

The design goal, stated once and pursued everywhere: no promise on a Vestal launch page should depend on any human staying honest, solvent, or alive. Ritual Chain is used because it provides the four primitives this requires at the protocol level — TEE-hosted agents invoked via precompiles, decentralized key management (DKMS), a native Scheduler for self-directed recurring execution, and consensus-level heartbeat monitoring with automatic revival from checkpoints.


Architecture, step by step

Step 1 — The creator commits a covenant

The Launch wizard walks the creator through four steps:

  1. Token basics — name, symbol, description.
  2. Tokenomics — total supply and the allocation split (Public & LP / Team-vested / Treasury / Community), which must sum to exactly 100% with at least 1% team allocation (the guardian needs something to vest).
  3. Guardian terms — LP lock duration (days), vesting schedule (cliff + number of tranches + interval), dev-wallet sell cap (% per rolling 30-day window), and monitoring cadence (every 150 / 300 / 1,200 blocks).
  4. Review & deploy — the creator types the token symbol as a signature, acknowledges immutability, and signs one transaction.

src/chain/factory.js translates the wizard's human units into contract units — days → blocks (at ~0.2 s/block), percents → basis points — builds equal vesting tranches (rounding remainder on the last one so the total is exact), simulates the call first so covenant reverts surface as readable errors before the wallet prompt, then submits createLaunch and decodes the LaunchCreated event from the receipt for the real token/covenant/guardian addresses.

Step 2 — One transaction makes it real

VestalLaunchFactory.createLaunch performs seven actions atomically — one launch is exactly one transaction:

  1. Mint the fixed supply into the factory.
  2. Send the creator their unvested share — before the covenant starts watching, so the sell-cap window bases on real holdings.
  3. Deploy the GuardianCovenant, which immediately puts the creator's wallet under audit (tracked from block one).
  4. Move the entire vesting allocation into covenant custody.
  5. Bind the covenant into the token's transfer path (VestalToken.bindCovenant, one-shot, factory-only, irreversible).
  6. Provision the sovereign agent via the IGuardianProvider, committed to the covenant's termsHash (a keccak256 of the exact terms + tranches — anyone can re-hash the public terms to verify what the agent enforces is what was committed), and bind it as guardian.
  7. Register the launch in the CovenantRegistry.

After step 7 the factory has no remaining authority over the launch — and never had a key that could override the covenant. The factory contract itself validates the terms up front: LP lock must end in the future, sell window and monitor cadence must be non-zero, caps must be ≤ 100%, and vesting must be more than 0% and less than 100% of supply.

Step 3 — Enforcement lives in the transfer path

VestalToken is a fixed-supply ERC20 with exactly one twist: _beforeTokenTransfer calls GuardianCovenant.beforeTokenTransfer on every transfer (except transfers from the covenant itself — those moves are the enforcement, not its subject). Inside the hook:

  • A frozen wallet's outgoing transfers revert (WalletFrozen).
  • A tracked wallet (the creator from block one, plus any insider the guardian adds) is held to a rolling sell cap: at most devWalletCapBps of its holdings-at-window-start per sellWindowBlocks window. Exceeding it reverts (SellCapExceeded) with the remaining allowance in the error. The window re-bases on current holdings when it rolls forward.

There is no allow-list, no admin override, no pause switch. Enforcement is a revert, not a policy — hence "physics."

Step 4 — The guardian watches, forever

The guardian is a persistent sovereign agent. Its liveness is layered three deep:

  1. Self-scheduled wake-ups — the covenant registers a recurring task with Ritual's native Scheduler precompile (registerWithScheduler), so recurring execution is included by the block proposer itself. No keeper bots or off-chain cron jobs to bribe, break, or abandon. On environments where the Scheduler slot has no code yet, registration is deferred and the agent self-schedules from inside the TEE.
  2. Consensus revival — Ritual Chain monitors persistent agents at the consensus level. A guardian that misses heartbeats is restored from its last checkpoint by the chain itself, state and keys intact, and logs a Revival action when it comes back.
  3. Permissionless failsafe — as the final, trustless backstop, any vesting tranche left unclaimed FAILSAFE_GRACE_BLOCKS (3,024,000 blocks, ~7 days at 0.2s blocks) past its due block becomes executable by anyone — but only to the committed recipient, and never one block early. Even if Vestal and Ritual both vanished, funds can be neither rushed nor stranded.

Every guardian action — wake, audit result, release, flag, freeze, checkpoint, revival — emits an EnforcementAction(action, atBlock, attestation, detail) event carrying the TEE attestation hash that binds the action to the exact guardian build. This event stream is the enforcement log the frontend shows; the Guardian Panel is a straight event read. The ActionType enum matches the frontend's event union (wake | release | check_ok | flag | freeze | checkpoint | revival) one-to-one.

The covenant also derives a guardian status on-chain exactly like the frontend badge: Enforcing if any wallet is frozen, Reviving if heartbeats have gone quiet for more than 3 monitor intervals, otherwise Active. The staleness check only arms once recurring wake-ups can actually be expected — a Scheduler task was registered, or the guardian has heartbeaten after deployment. Until then (e.g. while the Scheduler precompile isn't live on the testnet) silence reads as Active, not Reviving.

Provider boundary: the published testnet ABIs for Ritual's agent precompiles are not final, so every touchpoint is isolated in two files — contracts/src/interfaces/IRitual.sol (assumed ABIs + slot addresses) and contracts/src/providers/RitualGuardianProvider.sol. Deploy.s.sol auto-detects: if precompile slot 0x080C has code it deploys the Ritual provider; otherwise it deploys MockGuardianProvider (guardian = deployer EOA) so the full flow runs on anvil and today's testnet unchanged. ABI changes never reach covenant/factory/token logic.

Running the guardian for real today: until the precompiles ship, the guardian's job is performed by a real off-chain agent, scripts/guardian-agent.mjs (npm run guardian). It signs with the guardian key (from contracts/.env), sweeps every registered covenant, and does exactly what the sovereign agent will do natively: sends heartbeat() checkpoints whose hash commits to a re-derivable snapshot of live covenant state, audits the tracked creator wallet against the committed sell cap via recordAudit(), and calls executeVestingRelease() the moment a tranche comes due. Its attestations are honest hash commitments over the same snapshot — a TEE quote replaces them when the real provider goes live. Note that the first heartbeat arms the on-chain staleness check: once the agent has spoken, it must keep heartbeating within 3 monitor intervals or guardianStatus() reads Reviving — which is precisely the behavior the status is meant to surface.

Step 5 — The market trades under the same covenant

LaunchPool is a minimal constant-product AMM pairing the launch token against the chain's native coin (tRITUAL), with a 0.3% fee accruing to LP shares. It is deliberately unprivileged: no owner, no fee switch, no pause. Reserves are tracked explicitly so stray donations can't skew pricing, and a reentrancy lock guards every mutation.

Two properties tie it into the covenant system:

  • LP shares are themselves an ERC20 — so the creator seeds liquidity, then deposits the shares into the launch's GuardianCovenant via lockLp(). "LP locked" on the token page is literal custody: the same contract that vests the team allocation holds the market's liquidity until lpLockUntilBlock, and withdrawLp reverts one block sooner.
  • Selling into the pool is a token transfer out of the seller's wallet — so the covenant's sell cap and freeze checks bite on every sell, at the token level, with zero extra wiring.

VestalPoolFactory is a permissionless one-pool-per-token registry (poolOf), deployed separately from the launch factory so existing launches can get markets without redeploying the protocol. Every trade emits Swap with the post-trade price (native per token, 1e18-scaled) — the frontend price chart is a straight event read.

Step 6 — The frontend is a straight read of chain state

Data flows one way, from a single config file to the pages:

src/config/ritual.js         chain constants: RPC, chain id, contracts, precompiles
        │
        ├── src/chain/wallet.js     injected EIP-1193 wallet as a useSyncExternalStore store;
        │                           connect / silent reconnect / switch-or-add Ritual Chain
        ├── src/chain/launches.js   registry enumeration → Launch objects (terms, vesting,
        │                           guardian summary, chunked EnforcementAction event history)
        ├── src/chain/market.js     poolOf → reserves, price, guarded liquidity, Swap history,
        │                           client-side buy/sell quotes, simulated-then-sent buy()/sell(),
        │                           and the creator's openMarket flow (createPool → seed → lockLp)
        └── src/chain/factory.js    the one deploy write: createLaunch
                │
                ├── src/data/useLaunches.js   module-level store: fetch once, share everywhere;
                │                             refreshLaunches() after a deploy
                └── src/data/useMarket.js     per-token market state; refresh() after a trade
                        │
                        └── src/pages/*      Landing · Explore · TokenDetail · Launch · Docs

Notable choices:

  • No state libraries, no storage (beyond one localStorage reconnect flag) — wallet and launch state are plain external stores consumed via useSyncExternalStore.
  • Launch ids are token addresses, so /token/:id routes are chain addresses.
  • Event fetches are chunked (90k-block windows, max 10 chunks, newest first) because public RPCs cap eth_getLogs ranges; tranche released-state comes from contract storage, not logs, so older covenants only lose timeline depth, never correctness.
  • Writes simulate first (simulateContract) so contract reverts surface as readable errors before the wallet prompt, and confirm the receipt before reporting success.
  • SSR-safewindow is only touched inside handlers/effects, so every module loads under the smoke/prerender script.

What is actually enforced

Guarantee Mechanism Escape hatch?
Vesting Tranches live in covenant custody; only the guardian can release, never before the committed block. Permissionless failsafe after ~7 days past due — to the committed recipient only.
Dev-wallet sell cap Token-level revert when a tracked wallet exceeds devWalletCapBps of holdings per rolling sellWindowBlocks window. None.
Freeze Guardian freezes a violating wallet; all outgoing transfers revert until unfrozen. Only the guardian can unfreeze.
LP lock LP shares held by the covenant; withdrawLp reverts before lpLockUntilBlock. None — not one block sooner.
Terms immutability termsHash committed at launch; no setter exists for terms, covenant binding, or guardian binding. None — there are no admin keys anywhere.
Auditability Every guardian action emits EnforcementAction with a TEE attestation hash. Failsafe releases carry a zero attestation (no TEE needed).

Contract reference

Contract Role
VestalToken.sol Fixed-supply ERC20; covenant hook on every transfer; covenant bound once by the factory, no admin, no upgrade path.
GuardianCovenant.sol One per launch. Custodies vesting + LP, enforces freeze/sell-cap in the transfer hook, exposes guardianSummary() / guardianStatus() / vesting() views shaped for the frontend, emits the enforcement log.
VestalLaunchFactory.sol The seven-step atomic launch (see Step 2). Validates terms and every tranche up front — zero-bps or past-due tranches revert (InvalidTranche) before anything deploys.
CovenantRegistry.sol Append-only, factory-only launch index; allLaunches() powers Explore; one-shot setFactory wiring, then the deployer holds no further power.
LaunchPool.sol Native-paired constant-product AMM, 0.3% fee, ERC20 LP shares, no privileges. addLiquidity(tokenIn, minShares) carries a slippage guard so a swap landing between quote and deposit can't silently dilute the depositor.
VestalPoolFactory.sol Permissionless token → pool registry.
interfaces/ICovenant.sol Shared types: CovenantTerms, VestingTranche, the ActionType/GuardianStatus enums (enum order is the wire format the frontend maps by index), and the transfer-hook interface.
interfaces/IGuardianProvider.sol The one interface covenant/factory logic sees for guardian provisioning — everything Ritual-specific hides behind it.
interfaces/IRitual.sol Assumed Ritual precompile ABIs + slot addresses — the only file (with the Ritual provider) that touches them.
providers/RitualGuardianProvider.sol Provisions real sovereign agents via precompile 0x080C / heartbeats via 0x0820.
providers/MockGuardianProvider.sol Guardian = deployer EOA, for anvil and pre-precompile testnets.
lib/ERC20.sol Minimal local ERC20 base (no OpenZeppelin dependency) used by the token and LP shares.
script/Deploy.s.sol Deploys registry + provider (auto-detecting mock vs Ritual) + factory, wires them.
script/DemoLaunch.s.sol Creates a live demo launch and writes guardian log entries.
script/DeployMarket.s.sol Deploys the pool factory and (given TOKEN=) creates, seeds, and covenant-locks a pool in one run.

Frontend ↔ contract mapping:

Frontend surface Contract source
Launch[] on Explore CovenantRegistry.allLaunches()
Guardian block on token page GuardianCovenant.guardianSummary()
CovenantTerms / VestingTranche[] covenant.terms() / covenant.vesting()
Enforcement log EnforcementAction event history
Guardian status badge covenant.guardianStatus() (same derivation)
UI percents basis points on-chain (bps / 100 = %)
Market card / price chart VestalPoolFactory.poolOf()LaunchPool reserves + Swap events
Trade widget (buy / sell) LaunchPool.buy() / LaunchPool.sell() (0.3% fee, constant product; sells run the covenant's sell-cap/freeze checks)
Open-market flow (creator, shown when no pool) VestalPoolFactory.createPool()LaunchPool.addLiquidity()GuardianCovenant.lockLp()
"Guarded liquidity" figure covenant's LaunchPool share balance × its slice of both reserves

Frontend reference

Stack: React 18 · Vite 6 · Tailwind CSS v4 · react-router 6 · viem 2. Fonts: Fraunces (display) + Inter (body). No state libraries, no server.

Routes (src/App.jsx):

Route Page What it shows
/ Landing.jsx Hero, how-it-works, comparison table, FAQ.
/explore Explore.jsx Every registered launch, read live from the registry.
/token/:id TokenDetail.jsx The showpiece: Guardian Panel (status, heartbeat, revivals, enforcement log with attestations), covenant terms, vesting timeline, live market (price chart from Swap events, reserves, buy/sell widget). When the token has no pool yet, the connected creator sees the open-market flow instead. :id is the token address.
/launch Launch.jsx The 4-step wizard ending in a real createLaunch transaction.
/docs Docs.jsx In-app documentation: architecture, lifecycle, contracts, developer guide, FAQ, roadmap.
/portfolio Portfolio.jsx The connected wallet's holdings, LP positions, and trade history across all launches.

Components (src/components/): Card, Badge, StatBlock, Timeline, StepIndicator, Accordion, Sparkline, AllocationDonut, HeartbeatMonitor, TrustMeter, TokenAvatar, FlameMark, Nav, Footer — presentational primitives; all data arrives via props from the pages.

Wallet (src/chain/wallet.js): injected EIP-1193 provider (MetaMask etc.) exposed as a single external store. Handles connect, silent reconnect on revisit, account/chain-change events without reload, disconnect with best-effort permission revocation, and switch-to-Ritual with add-chain fallback. useWallet().onRitual gates every write.


Getting started

Prerequisites

  • Node 18+ and npm
  • Foundry (forge, anvil) for contract work
  • A browser wallet (MetaMask or similar) for launching/trading

Run the frontend against the live testnet

npm install
npm run dev       # dev server — reads the deployed testnet contracts out of the box
npm run build     # production build to dist/
npm run preview   # serve the production build
npm run smoke     # SSR-render every route; catches runtime errors vite build cannot

The app ships pointed at Ritual Chain testnet with the deployed addresses baked into src/config/ritual.js — no configuration needed to browse. To launch or buy, connect a wallet; the app offers to add/switch to Ritual Chain (chain id 1979) itself.

Full-stack local development (anvil)

# 1. Start a local chain
anvil

# 2. Deploy the protocol (auto-selects MockGuardianProvider on anvil)
cd contracts
forge script script/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast

# 3. Create a demo launch with guardian log entries
FACTORY=0x... forge script script/DemoLaunch.s.sol --rpc-url http://localhost:8545 --broadcast

# 4. (Optional) Deploy the market layer and seed a covenant-locked pool
TOKEN=0x... forge script script/DeployMarket.s.sol --rpc-url http://localhost:8545 --broadcast

# 5. Point the frontend at anvil without touching source — .env.local:
#    VITE_RPC_URL=http://localhost:8545
#    VITE_CHAIN_ID=31337
#    VITE_LAUNCH_FACTORY=0x...
#    VITE_COVENANT_REGISTRY=0x...
#    VITE_POOL_FACTORY=0x...
npm run dev

Deploy contracts to Ritual testnet

cd contracts
forge build
forge test
forge script script/Deploy.s.sol                                        # dry run
forge script script/Deploy.s.sol --rpc-url $RITUAL_RPC_URL --broadcast  # real

Then copy the printed addresses into VESTAL_CONTRACTS in src/config/ritual.js (or .env.local overrides).


New developer guide

A guided path through the codebase for someone seeing it for the first time. The repo is two projects in one — a Foundry suite in contracts/ and a React app in src/ that reads all state directly from the deployed contracts (no mocks, no backend).

Read the code in this order

  1. contracts/src/interfaces/ICovenant.sol — the shared vocabulary: CovenantTerms, VestingTranche, the ActionType/GuardianStatus enums, and the transfer-hook interface. Every other file speaks these types.
  2. contracts/src/GuardianCovenant.sol — the heart of the system: custody, the transfer hook (freeze + sell cap), the attested enforcement log, the permissionless vesting failsafe. If you read one contract, read this one.
  3. contracts/src/VestalLaunchFactory.sol — how a launch becomes real: seven atomic steps in one transaction, with all terms validated up front.
  4. src/config/ritual.js — the frontend's single source of chain truth: RPC, chain id, deployed addresses, precompiles. Nothing else hardcodes chain values.
  5. src/chain/abi.js + src/chain/launches.js — the viem layer: hand-written minimal ABIs and the registry → Launch mapping. Everything on screen comes through here.
  6. src/data/launches.js — the read-model contract: JSDoc typedefs and formatters the UI depends on. The chain layer maps contract state into exactly these shapes.
  7. src/pages/TokenDetail.jsx — the showpiece page: store hooks, formatters, the simulate → write → confirm flow, and the WalletGate connect/switch ladder.

The six conventions that hold everywhere

  • One-way data flowconfig → src/chain (viem I/O) → src/data (stores + typedefs) → pages → components. Pages fetch via the src/data/ hooks; components are purely presentational.
  • ABIs are mirrored by handsrc/chain/abi.js holds minimal parseAbi strings, only what the frontend touches. Any change to a contract's read/write surface must be mirrored there.
  • Enum order is a wire formatACTION_TYPES/GUARDIAN_STATUSES in abi.js map by index to the enums in ICovenant.sol. Reordering either side breaks the Guardian Panel silently.
  • Units convert at the chain layer, never in UI — basis points → percent (bps / 100), days → blocks at 0.2 s/block (1 day ≈ 432,000 blocks), 18 decimals everywhere, prices in native tRITUAL per token.
  • Every write simulates first — simulate → write → waitForTransactionReceipt → check receipt.status, so covenant/pool reverts surface as readable errors before the wallet prompt.
  • Event reads are chunked and defensive — 90k-block eth_getLogs windows with retries and a session-level dedupe cache; correctness never depends on log completeness (state comes from contract storage, logs only feed timelines).

A good first exercise

Trace the dev-wallet sell cap end to end: the wizard collects a percent (Launch.jsx) → src/chain/factory.js converts it to basis points inside createLaunch → the factory stores it in the covenant's terms → GuardianCovenant.beforeTokenTransfer reverts any sell that exceeds it → the token page surfaces that revert as a readable error via simulation. One number, four layers, no trust in between.

Common tasks

Task What to touch
Add a page/route src/pages/, route in src/App.jsx, and the routes array in scripts/prerender.mjs (static routes only) — then npm run smoke
Read new contract state Add the fragment to src/chain/abi.js, map it in the relevant src/chain/ module into the src/data/launches.js typedef shapes
Change a contract's surface Edit in contracts/src/, forge test, mirror the ABI in src/chain/abi.js, keep both READMEs + /docs in sync
Redeploy to testnet forge script script/Deploy.s.sol --broadcast, then update VESTAL_CONTRACTS in src/config/ritual.js and the address tables in both READMEs
Verify frontend changes npm run smoke — SSR-executes every static route; there is no other frontend test suite

The in-app docs at /docs carry a condensed version of this guide plus the protocol-level documentation (architecture, enforcement, guardian lifecycle).


Deployed addresses (Ritual Chain testnet)

Chain id 1979 · RPC https://rpc.ritualfoundation.org · deployed 2026-07-11:

Contract Address
CovenantRegistry 0x56F78A7e8Afe11C69228283CCe3971F73486E7fE
VestalLaunchFactory 0x726D2c8e17d3d445c1c0088470dA2DBcCe345B35
MockGuardianProvider 0x15cce71713b686aaE99C441Ab4e3dEBf7853A889
VestalPoolFactory 0xCdB83Cdeba6CD12116925E6AF5cDF17d35D2530B
Demo NLT token 0x7EF4FCB9C878a9f3f7f6318d8C6229edc260ec01
Demo covenant 0x09Ca97c97325248A64ED849A2Ec4e9A2c5F37361
NLT LaunchPool 0x6d849a7c64e4Ff693D38235e2244dDD2f1055744

The agent precompile slots have no code on the current testnet, so the mock guardian provider is live (guardian = deployer EOA). Swap to RitualGuardianProvider when the precompiles ship — nothing above the provider boundary changes.


Configuration

Everything the app knows about the chain lives in src/config/ritual.js; no UI code hardcodes chain values. VITE_* variables in .env.local override it for local dev:

Env var Default Purpose
VITE_RPC_URL https://rpc.ritualfoundation.org JSON-RPC endpoint
VITE_CHAIN_ID 1979 Chain id (wallet + chain object)
VITE_LAUNCH_FACTORY deployed address createLaunch target
VITE_COVENANT_REGISTRY deployed address Launch enumeration
VITE_POOL_FACTORY deployed address Token → market resolution

Other constants there: EXPLORER_URL, BLOCK_TIME_SECONDS (0.2 — display countdowns only), and the PRECOMPILES map (Scheduler placeholder; HTTP 0x0801, LLM 0x0802, sovereign agent 0x080C, persistent agent 0x0820).


Testing

# Contracts — 31 Foundry tests: factory wiring + tranche validation, vesting
# (guardian + failsafe paths), sell caps and window rolling, freeze, LP lock/withdraw,
# status derivation, enforcement log shape, and the AMM (quotes, liquidity with
# slippage guard, swaps, covenant lock)
cd contracts && forge test

# Frontend — SSR-renders /, /explore, /launch, /docs, /portfolio through Vite's
# pipeline, executing every page component for real
npm run smoke

Ritual Chain quirks worth knowing

  • ~5 blocks per second (BLOCK_TIME_SECONDS = 0.2) — day-based UI terms translate to large block numbers (1 day ≈ 432,000 blocks).
  • Block timestamps are in milliseconds, not seconds — don't feed them to Date math that assumes Unix seconds without checking.
  • Public RPC caps eth_getLogs to ~100k-block ranges — all event reads in src/chain/ chunk requests (90k windows, bounded count) for this reason.
  • Agent precompiles are not live yet on the testnet; the deploy script auto-detects and falls back to the mock provider so the whole flow works today.

Trust model & current limitations

Honest accounting of where the "physics" currently ends:

  • Guardian provider is mocked on testnet — until Ritual's agent precompiles ship, the guardian is the deployer EOA, not a TEE agent. The covenant's structural guarantees (vesting custody, sell cap, freeze mechanics, LP lock, failsafe) hold regardless; the liveness and attestation guarantees arrive with the real provider.
  • Attestation hashes are recorded but not yet verifiable in-app — a public attestation verifier page is on the roadmap.
  • The sell cap tracks wallets, not identities — the guardian can track insider wallets it discovers, but a determined creator can pre-distribute before launch. The creator wallet itself is tracked from block one.
  • Unaudited testnet software. Do not use with real value. Mainnet follows independent audits (see the roadmap in the in-app docs at /docs).

Repository layout

vestal/
├── contracts/                  Foundry project (see contracts/README.md)
│   ├── src/
│   │   ├── VestalToken.sol           fixed-supply ERC20 + covenant hook
│   │   ├── GuardianCovenant.sol      custody + enforcement + attested log
│   │   ├── VestalLaunchFactory.sol   the one-transaction launch
│   │   ├── CovenantRegistry.sol      append-only launch index
│   │   ├── LaunchPool.sol            constant-product AMM, ERC20 LP shares
│   │   ├── VestalPoolFactory.sol     token → pool registry
│   │   ├── interfaces/               ICovenant, IGuardianProvider, IRitual (precompile boundary)
│   │   ├── providers/                RitualGuardianProvider, MockGuardianProvider
│   │   └── lib/                      minimal local ERC20 base (no OpenZeppelin)
│   ├── script/                 Deploy, DemoLaunch, DeployMarket
│   └── test/                   31 tests: VestalLaunch.t.sol, LaunchPool.t.sol
├── src/
│   ├── config/ritual.js        every chain constant, env-overridable
│   ├── chain/                  viem layer: wallet, launches, market, factory, portfolio, activity, abi
│   ├── data/                   store hooks (useLaunches, useMarket, usePortfolio, useActivity) + site copy + fixtures
│   ├── components/             presentational primitives
│   ├── pages/                  Landing, Explore, TokenDetail, Launch, Docs, Portfolio
│   ├── App.jsx                 routes
│   └── index.css               Tailwind v4 theme (ink/cream/ember palette)
├── scripts/prerender.mjs       SSR smoke test (npm run smoke)
└── vite.config.js

src/data/launches.js also keeps the JSDoc typedefs and static fixtures that document the Launch read model — the chain layer maps contract state into exactly these shapes.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors