Skip to content

RedaRahmani/Vertex

Repository files navigation

Vertex – Meteora DAMM v2 Permissionless Fee Router

Built for the @stardotfun bounty “Permissionless Fee Routing for Meteora DAMM v2”, Vertex is a production-ready Solana module that automates daily quote-fee collection and distribution for DAMM v2 pools — no manual cranks, no creator intervention.

🧩 Core Functionality

Vertex provides:

Honorary LP Position (Quote-Only)

Opens a Meteora DLMM v2 liquidity position owned by a program PDA, guaranteed to accrue only quote-mint fees.

24-Hour Permissionless Distribution Crank

Anyone can trigger the crank once per UTC day to:

  • Claim accrued quote fees from the Meteora pool
  • Compute pro-rata investor payouts using Streamflow’s still-locked balances
  • Route the remaining carry (creator share) to the creator’s quote ATA

Resumable Pagination

Supports multi-page investor sets with deterministic continuation (no double-payouts, safe retries).

Streamflow Integration

Reads on-chain vesting streams to compute real-time locked ratios for fair, transparent fee sharing.

We packaged the on-chain program, Rust/TypeScript SDKs, a CLI, and a reference Next.js UI so the Star team can drop it directly into their stack.


Repository Layout

programs/fee_router      Anchor program for honorary position + distribution crank
apps/ui-temp             Reference Next.js UI (policy setup, honorary position, daily crank)
sdk/rust, sdk/ts         Instruction builders and account helpers
scripts/                 Local validator automation and fixtures
audits/                  Threat model + security checklist
docs/                    MkDocs documentation site

Running the Demo Locally

These steps produce the exact flow shown in the screenshots below. Everything runs on a local validator; Phantom (or any Solana wallet) must be set to Localnet and connected to localhost:3000.

# 1. Start a clean validator
solana-test-validator

# in a new shell: build + deploy the fee router program
anchor build -p keystone_fee_router
anchor deploy -p keystone_fee_router

# 2. Seed deterministic fixture accounts (quote mint, cp_pool, honorary position PDA seeds)
node scripts/setup-local-fixture.js

# 3. Launch the reference UI
npm --prefix apps/ui-temp run dev

Every fixture run rewrites target/local-fixture.json and apps/ui-temp/app/config.ts with the program ID, Meteora pool, quote mint, PDAs, and ATAs that the UI pre-fills.


UI Walkthrough

Where do I find the Program ID and IDL?
Both values are copied by scripts/setup-local-fixture.js. The script writes:

  • Program ID: LOCAL_FIXTURE.programId
  • Full IDL JSON: Vertex/target/idl/keystone_fee_router.json
  • All PDAs / ATAs needed for the flow: target/local-fixture.json

1. Dashboard – Environment & State Inspection

Dashboard

image
  1. Paste the Program ID and IDL JSON recorded in target/local-fixture.json.
    (The UI caches these in localStorage after you press Save.)
  2. Enter the Meteora cp_pool from the same fixture file and press Fetch State.
    Before the first crank runs you’ll see a policy summary plus a note indicating the Progress account will appear after the initial distribution.

2. Policy Setup – Initialize the Daily Policy

Policy Setup

image

All inputs are pre-populated from the fixture:

  • Meteora cp_poolLOCAL_FIXTURE.cpPool
  • Quote mintLOCAL_FIXTURE.quoteMint
  • Creator quote ATALOCAL_FIXTURE.creatorAta
  • Treasury quote ATALOCAL_FIXTURE.treasuryAta
  • Economic knobs (Y0, bps, cap, min payout)

Click Initialize Policy and approve the Phantom prompt. The transaction signature is displayed on success (e.g. Init Policy tx: ...). This stores the immutable configuration and the PDAs are displayed above the button for reference.

3. Honorary Position – Bind the Meteora LP

Honorary Position

image

Again, values are filled automatically:

  • Policy PDALOCAL_FIXTURE.policy
  • Meteora cp_poolLOCAL_FIXTURE.cpPool
  • Quote mintLOCAL_FIXTURE.quoteMint
  • cp-position accountLOCAL_FIXTURE.cpPosition

Press Initialize Honorary Position to create the position PDA and tie it to the DLMM owner PDA. The UI echoes both the Honorary Position PDA and the fee-owner PDA derived from [VAULT_SEED, policy, investor_fee_pos_owner].

4. Daily Crank – Quote Fee Distribution

Daily Crank

image

This form requires both static and per-investor values:

Input Default Source Notes
Policy PDA LOCAL_FIXTURE.policy Static
Meteora cp_pool LOCAL_FIXTURE.cpPool Static
Treasury quote ATA LOCAL_FIXTURE.treasuryAta Static, owned by the vault PDA
Creator quote ATA LOCAL_FIXTURE.creatorAta Static, receives end-of-day remainder
Investor quote ATA (must supply per investor page) Streamflow-provided token account, same quote mint
Stream account (must supply per investor page) Streamflow stream PDA; fee router reads locked at crank time
Page cursor (u64) Free-form Caller-chosen opaque value for pagination bookkeeping
Carry cursor (u64) Free-form We reuse the fixture default 0; the crank stores it in the Progress account
“Is last page?” checkbox Off by default Toggle after you submit the final investor page of the UTC day (routes remainder to creator)

For real payout pages you’ll pass the Streamflow stream and investor ATA for each investor. The UI accepts additional investor entries (ATA + stream) via the “Additional Investors” section (not shown in the screenshot) to cover multi-investor pages.

5. Dashboard – Fetch Policy State

Fetch State

image

After the policy is initialized, Fetch State displays:

  • Policy PDA and derived Progress PDA
  • Immutable economic configuration
  • Quote mint, Y0 total, fee share, caps, dust threshold
  • Creator and treasury ATAs

Once you run the crank for the first time, the Progress card is populated with:

  • Current UTC day (floor(ts/86400))
  • Last crank timestamp
  • Claimed/distributed quote totals
  • Carry-over, cursor, and day_closed flag

Bounty Requirements Checklist

Requirement Implementation Reference
Honorary position owned by PDA, quote-only fees programs/fee_router/src/lib.rs::init_honorary_position (quote mint validation + Meteora CPI guard)
24h distribution crank with pagination programs/fee_router/src/lib.rs::crank_distribute
Idempotent daily tracking (Progress account) Progress account schema (current_day, page_cursor, carry_quote_today, day_closed)
Per-investor pro-rata math with caps & dust compute_investor_quote and distribution loop
Remainder routed to creator when is_last_page Final branch in crank_distribute
Quote-only guard (base fees rejected) assert_cp_pool_quote_only + Meteora CPI result checks
Streamflow integration stream_adapter::StreamLockedReader (mock adapter)
Events emitted PolicyInitialized, HonoraryPositionInitialized, InvestorPayoutPage, CreatorPayoutDayClosed

Tests and scripts that exercise the happy path live under tests/, scripts/, and the Next.js UI.


Security Notes

  • All PDAs store bumps on-chain to prevent accidental drift.
  • Honorary position initialization validates pool mint ordering and quote-only accrual.
  • Crank enforces 24h gating, handles retries safely, and never double-pays.
  • Arithmetic uses checked 128-bit intermediates to avoid overflow.

See audits/threat_model.md and audits/checklist.md before mainnet deployment.


Documentation & Tooling

  • Rust + TypeScript SDKs: instruction builders and account parsers for integrating the module into Star’s backend.

  • CLI (cli/): operational commands for deployments and configuration.

  • Docs (MkDocs) can be served locally:

    pip install mkdocs-material
    mkdocs serve

Star can integrate Vertex to manage their token sale fee flows on-chain, ensuring transparent, automatic investor revenue distribution via Meteora liquidity pools. 🚀

About

No description, website, or topics provided.

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •