Skip to content

Rahul-Prasad-07/star-honorary-fee-module

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

17 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

DAMM v2 Honorary Quote-Only Fee Module

This repository packages the "honorary LP" module we built for Star's DAMM v2 bounty. It lets the Star program hold a dedicated LP position that accrues fees exclusively in the quote mint and runs a permissionless, once-per-day distribution crank that pays investors pro-rata to their still-locked Streamflow allocations while routing the remainder to the creator.

The work is split into a core contract under programs/honorary-lp. All logic is Anchor-compatible and no unsafe blocks are used. End-to-end tests exercise the full initialize โ†’ claim โ†’ distribute flow against the real cp-amm program in a local validator.


๐ŸŽฏ Problem Statement & Solution

The Challenge

Star's fundraising platform needs to:

  • Accrue trading fees from CP-AMM liquidity pools without exposing treasury funds to impermanent loss
  • Distribute fees fairly to investors based on their locked Streamflow vesting allocations
  • Maintain creator control over fee distribution parameters and timing
  • Ensure permissionless operation so distributions can't be blocked by any single party
  • Guarantee quote-only economics to protect against base token price volatility

Our Solution

The Honorary LP Module provides:

  • PDA-owned quote-only position that accrues fees exclusively in the quote mint
  • Real-time Streamflow integration for pro-rata investor payouts based on locked balances
  • Permissionless 24h distribution crank with pagination for scalability
  • Deterministic PDA architecture ensuring no signer risk or external dependencies
  • Comprehensive security invariants including quote-only enforcement and dust carry-forward

๐Ÿ—๏ธ Architecture Overview

graph TB
    subgraph "Star Honorary LP Module (Anchor Program)"
        A[Initialize Honorary Position] --> B[Policy PDA<br/>Immutable Config]
        A --> C[Progress PDA<br/>Daily State]
        A --> D[Authority PDA<br/>Signer for Transfers]
        A --> E[Treasury ATA<br/>Quote Fee Accumulation]
        A --> F[Position NFT<br/>CP-AMM LP Slot]

        G[Distribute Fees Crank<br/>Permissionless 24h] --> H[Claim CP-AMM Fees<br/>Quote-Only Validation]
        H --> I[Calculate Pro-Rata Shares<br/>Streamflow Weighted]
        I --> J[Investor Payouts<br/>Paginated Transfers]
        J --> K[Creator Remainder<br/>Final Distribution]

        B --> G
        C --> G
        D --> G
        E --> G
        F --> G
    end

    subgraph "External Dependencies"
        L[CP-AMM Program<br/>Fee Claiming]
        M[Streamflow Program<br/>Locked Balance Queries]
        N[SPL Token Program<br/>Transfers & ATAs]
    end

    H --> L
    I --> M
    J --> N
    K --> N

    style A fill:#e1f5fe
    style G fill:#fff3e0
    style L fill:#f3e5f5
    style M fill:#f3e5f5
Loading

๐Ÿ”„ Full Workflow with Real Example

sequenceDiagram
    participant Creator
    participant Program
    participant CPAMM as CP-AMM Program
    participant Investors
    participant Streamflow

    rect rgb(240, 248, 255)
        Creator->>Program: Initialize Honorary Position
        Note over Creator,Program: Policy: 90% to investors,<br/>Daily cap: 1M USDC,<br/>Min payout: 1K USDC,<br/>Y0 total: 100 USDC
        Program->>CPAMM: Create PDA-owned Position NFT
        Program-->>Creator: HonoraryPositionInitialized Event
    end

    loop Daily Distribution Crank (24h intervals)
        rect rgb(255, 248, 220)
            Investors->>Program: Distribute Fees (Page 0/2)
            Note over Investors,Program: total_investors=2,<br/>total_locked=100 USDC
            Program->>CPAMM: Claim Position Fees
            CPAMM-->>Program: 1,000,000 USDC (Quote Only)
            Program->>Streamflow: Query Locked Balances
            Streamflow-->>Program: Investor A: 60 USDC<br/>Investor B: 40 USDC

            Program->>Program: Calculate Distribution
            Note over Program: Eligible share: 90%<br/>Investor pool: 900K USDC<br/>Creator remainder: 100K USDC

            Program->>Investors: Transfer to Investor A: 540K USDC
            Program-->>Investors: InvestorPayoutPage Event<br/>(page_start=0, processed=1)
        end

        rect rgb(255, 248, 220)
            Investors->>Program: Distribute Fees (Page 1/2)
            Note over Investors,Program: page_start=1
            Program->>Investors: Transfer to Investor B: 360K USDC
            Program->>Creator: Transfer Creator Remainder: 100K USDC
            Program-->>Creator: CreatorPayoutDayClosed Event<br/>(investor_total=900K, creator=100K)
        end
    end
Loading

Real Mainnet Example

Pool: SOL/USDC with Star as creator

  • Honorary Position: Price range 100-200 ticks, accrues ~50K USDC daily in fees
  • Investors: 5 investors with Streamflow streams totaling 1M USDC initial allocation
  • Day 1 Distribution:
    • Total locked: 800K USDC (20% unlocked)
    • Eligible investor share: 80% (min of 90% policy, 80% locked ratio)
    • Investor pool: 40K USDC โ†’ Pro-rata payouts based on locked balances
    • Creator remainder: 10K USDC
  • Day 30 Distribution:
    • Total locked: 200K USDC (80% unlocked)
    • Eligible investor share: 20% โ†’ Smaller investor payouts, larger creator share

๐Ÿงช How to Run Tests

Prerequisites

  • Rust 1.85.0, Anchor 0.31.0, Solana 2.1.0
  • Node.js 18+ (for Bankrun tests)
  • pnpm or yarn

Running the Full Test Suite

# Install dependencies
pnpm install

# Build programs with local features
anchor build -- --features local

# Run end-to-end integration tests
yarn test:full:bankrun

Test Coverage

  • โœ… Full Flow Test (CreateInitializeClaimDistribute.test.ts): Pool creation โ†’ Position initialization โ†’ Fee accrual simulation โ†’ Paginated distribution
  • โœ… Quote-Only Enforcement: Aborts if CP-AMM claims return base tokens
  • โœ… Pagination Idempotence: Cursor tracking prevents double-payments
  • โœ… 24h Gate Enforcement: Rejects distribution attempts before cooldown
  • โœ… Pro-Rata Math: Validates investor payouts match locked proportions
  • โœ… Dust Carry-Forward: Tracks and carries over sub-minimum payouts

Example Test Output

=== STARTING HONORARY FEE MODULE TEST ===
Test Flow: Create Pool โ†’ Initialize Policy โ†’ Claim Fees โ†’ Distribute Fees

๐Ÿ”‘ Payer public key: [REDACTED]
๐Ÿ‘‘ Admin public key: [REDACTED]
๐Ÿ’ฐ Created test accounts:
  - Creator: [REDACTED]
  - Investor A: [REDACTED]
  - Investor B: [REDACTED]

๐Ÿฆ Creating tokens and minting initial supply...
โœ… Tokens created and minted to creator

๐Ÿ—๏ธ Setting up CP-AMM pool configuration...
โœ… Pool initialized: [POOL_PUBKEY]

๐ŸŽฏ Setting up Honorary Fee Module...
Configuration:
  - Investor Share: 90%
  - Daily Cap: 1000000 tokens
  - Min Payout: 1000 tokens
  - Total Locked: 100 tokens

๐Ÿ“‹ Initializing Honorary Policy...
โœ… Policy initialized successfully
โœ… Policy validation passed

๐Ÿ’ฐ Setting up fee claiming infrastructure...
๐Ÿงพ First fee claim (setup - should claim 0)...

๐Ÿ’ธ Minting test tokens to simulate trading fees...
๐Ÿ”„ Performing swaps to accrue fees on honorary position...
โœ… Fee accrual simulation complete

๐Ÿš€ Starting distribution crank...
๐Ÿ“„ Page 0/2: Processing first investor...
โœ… Investor A payout: 540000 tokens
๐Ÿ“„ Page 1/2: Processing second investor...
โœ… Investor B payout: 360000 tokens
๐Ÿ’ฐ Creator remainder: 100000 tokens

โœ… All distributions completed successfully
โœ… 24h gate test passed

๐Ÿ“š How to Use the Module

For Developers (General Usage)

  1. Import the Program: Add honorary-lp to your Anchor workspace
  2. Derive PDAs: Use/Implement SDK helpers to calculate deterministic addresses
  3. Initialize Once: Call initialize_policy with your distribution parameters
  4. Crank Daily: Anyone can call distribute_fees once per 24h period
  5. Monitor Events: Subscribe to distribution events for transparency

For Star Team (Integration)

Step 1: Add to Workspace

# In your Anchor.toml
[[programs.mainnet]]
honorary-lp = "YOUR_DEPLOYED_PROGRAM_ID"

# In your Cargo.toml
[dependencies]
honorary-lp = { path = "../honorary-lp", features = ["cpi"] }

Step 2: Initialize Per Pool

// In your program
use honorary_lp::cpi::initialize_policy;

// During pool creation or upgrade
let policy_params = InitializePolicyParams {
    investor_fee_share_bps: 9000, // 90%
    daily_cap: 1_000_000, // 1M tokens
    min_payout: 1_000, // 1K tokens minimum
    y0_total_allocation: total_investment_amount,
    tick_lower: price_range.min,
    tick_upper: price_range.max,
};

// Call initialize_policy instruction
honorary_lp::cpi::initialize_policy(
    ctx,
    policy_params,
    pool_key,
    quote_mint,
)?;

Step 3: Schedule Daily Cranks

// Off-chain keeper bot
const crankDistribution = async (poolId: PublicKey) => {
    // Get current progress state
    const progress = await getProgressPda(poolId);

    // Check if 24h has passed
    if (!progress.canDistribute()) return;

    // Get investor list for this pool
    const investors = await getPoolInvestors(poolId);

    // Paginate through investors
    for (let page = 0; page < Math.ceil(investors.length / MAX_PER_PAGE); page++) {
        const pageInvestors = investors.slice(page * MAX_PER_PAGE, (page + 1) * MAX_PER_PAGE);

        await program.methods.distributeFees(
            new BN(page), // page_start
            new BN(investors.length), // total_investors
            totalLockedAmount, // from Streamflow queries
        )
        .accounts({
            // ... account mappings
            remainingAccounts: pageInvestors.flatMap(inv => [inv.streamAccount, inv.ata])
        })
        .rpc();
    }
};

Required Integration Inputs

  • Creator Details: Wallet pubkey and quote ATA for remainder payouts
  • Streamflow Config: Program ID for locked balance validation
  • Policy Parameters:
    • investor_fee_share_bps: Max percentage to investors (0-10000)
    • daily_cap: Maximum tokens distributable per day
    • min_payout: Minimum payout per investor (dust threshold)
    • y0_total_allocation: Initial total investment amount
  • Pool Details: CP-AMM pool pubkey, position NFT, token mints

๐Ÿ”ง Technical Details

Instruction Flow

1. Initialize Honorary Position

  1. Validation: Confirm quote mint matches pool's quote token
  2. PDA Derivation: Calculate all deterministic addresses from pool ID
  3. Position Creation: Initialize empty CP-AMM position owned by PDA
  4. Policy Storage: Persist distribution parameters in Policy PDA
  5. Event Emission: HonoraryPositionInitialized with configuration

2. Distribute Fees (Permissionless Crank)

First Page Processing:

  • 24h Gate Check: Enforce now >= last_distribution + 86400
  • Fee Claim: Call CP-AMM to claim position fees (quote-only validation)
  • Target Calculation: Combine claimed fees + carried dust, apply daily cap
  • Share Allocation: Calculate investor vs creator portions

Investor Payout Pages:

  • Streamflow Queries: Fetch real-time locked balances for each investor
  • Pro-Rata Math: payout_i = floor(investor_pool ร— locked_i รท locked_total)
  • Dust Filtering: Skip payouts below min_payout threshold
  • Transfer Execution: CPI to SPL Token for secure transfers

Final Page Closing:

  • Remainder Routing: Transfer unallocated investor pool to creator
  • State Reset: Update progress PDA for next day
  • Event Emission: CreatorPayoutDayClosed with final totals

Accounts & PDAs

Account Seeds Purpose
policy [b"policy", pool] Immutable configuration and invariants
progress [b"progress", pool] Daily distribution state and cursor
authority_pda [b"honorary-authority", pool] Signs all token transfers and CPI calls
quote_treasury [b"treasury", pool, quote_mint] Accumulates claimed quote fees
position CP-AMM derived PDA-owned LP position NFT
temp_a/b_accounts Associated with authority Temporary holders for CP-AMM claim outputs
remaining_accounts Streamflow + Investor ATA pairs Per-investor data for distribution

Math & Policy Invariants

Share Calculation:

eligible_investor_share_bps = min(
    policy.investor_fee_share_bps,
    floor(locked_total / y0_total_allocation ร— 10000)
)
investor_pool_target = floor(total_to_distribute ร— eligible_share_bps / 10000)
creator_remainder_target = total_to_distribute - investor_pool_target

Per-Investor Payout:

payout_i = floor(investor_pool_target ร— locked_i / locked_total)
if payout_i < min_payout: skip (add to dust carry)

Edge Cases:

  • locked_total = 0: All fees go to creator
  • Base fees detected: Transaction aborts with BaseFeesNotAllowed
  • Insufficient treasury: InsufficientTreasuryBalance error

Events & Monitoring

Event Trigger Key Data
HonoraryPositionInitialized Initialization pool, policy, quote_mint, investor_share_bps
QuoteFeesClaimed Fee claim success pool, amount_claimed
InvestorPayoutPage Per page completion page_start, investors_processed, total_payout
CreatorPayoutDayClosed Final page investor_total, creator_amount, dust_carry_forward

Error Handling

Error Code Description Mitigation
DistributionTooSoon 24h cooldown not elapsed Wait for gate to open
InvalidPaginationState Cursor mismatch or invalid page Check pagination logic
InvalidStreamAccount Malformed Streamflow data Validate account ownership
BaseFeesNotAllowed CP-AMM returned base tokens Position configuration error
InsufficientTreasuryBalance Treasury underfunded Check fee accrual

๐Ÿ› ๏ธ Local Development

Prerequisites

  • Rust: 1.85.0 (via rust-toolchain.toml)
  • Anchor: 0.31.0
  • Solana: 2.1.0
  • Node.js: 18+ (Bankrun requirement)

Setup & Build

# Install dependencies
pnpm install

# Build with local features
anchor build -- --features local

# Run tests
yarn test:full:bankrun

# Lint and format
cargo fmt --all
cargo clippy -p honorary-lp -- -D warnings

Development Workflow

  1. Modify Code: Edit Rust/TS files in programs/ and tests/
  2. Build: anchor build -- --features local
  3. Test: yarn test:full:bankrun
  4. Iterate: Fix any compilation or test failures

๐Ÿš€ Deployment & Production

Deployment Steps

# Build for mainnet
anchor build

# Deploy program
anchor deploy --provider.cluster mainnet

# Update IDL
anchor idl init --filepath target/idl/honorary_lp.json [PROGRAM_ID]

Production Considerations

  • Program ID: Update all references after deployment
  • Keeper Bot: Set up automated daily cranks (24h intervals)
  • Monitoring: Subscribe to events for distribution tracking
  • Upgrades: Use Anchor's upgradeable program pattern if needed

Security Features

  • โœ… No External Signers: All operations use PDAs
  • โœ… Deterministic Addresses: No seed guessing required
  • โœ… Quote-Only Enforcement: Protects against IL exposure
  • โœ… Pagination Safety: Idempotent and resumable operations
  • โœ… Time-Locked Distributions: 24h minimum intervals

๐Ÿ”ฎ Future Enhancements

  • Multi-Chain Support: Adapt for other AMM protocols
  • Dynamic Fee Schedules: Time-based investor share adjustments
  • Advanced Streamflow Parsing: Support for complex vesting schedules
  • Keeper Incentives: Built-in rewards for crank operators
  • Governance Integration: On-chain parameter updates
  • Enhanced Monitoring: Real-time dashboard for distributions

Bounty Fulfillment

This submission completely fulfills Star's DAMM v2 Honorary Quote-Only Fee Position + 24h Distribution Crank bounty:

โœ… Work Package A: PDA-Owned Quote-Only Position

  • Deterministic PDA-owned CP-AMM position
  • Quote-only fee accrual with strict validation
  • Configurable price ranges and policy parameters
  • Comprehensive initialization with event emission

โœ… Work Package B: 24h Distribution Crank

  • Permissionless daily distribution with 24h gating
  • Real-time Streamflow locked balance integration
  • Pro-rata investor payouts with pagination
  • Creator remainder routing and dust carry-forward
  • Full security invariants and error handling

๐Ÿ“‹ Acceptance Criteria Met

  • Quote-Only Guarantee: Aborts on base token fees
  • Streamflow Integration: Parses real locked balances
  • Pagination: Handles large investor sets efficiently
  • 24h Gate: Enforces minimum distribution intervals
  • Pro-Rata Math: Accurate weighted distributions
  • Creator Control: Configurable policy parameters
  • End-to-End Tests: Full integration test suite
  • Documentation: Complete integration guide

๐ŸŽฏ Ready for Integration

The module is production-ready and includes:

  • Complete Rust implementation with no unsafe code
  • Comprehensive TypeScript test suite
  • Full integration documentation
  • SDK helpers for PDA derivation
  • Event-based monitoring capabilities

Contact: Ready for deployment support and any final integration questions!

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

โšก