wp-evm-ramses-core 0.1.2

Pure data + quote + plan for Ramses-family CL DEXes
Documentation
//! Ramses-family data records.
//!
//! Re-exports shared types from `wp-evm-v3-core::data` where the ABI is
//! identical, and defines Ramses-specific types where it differs.
//!
//! # ABI differences from Uniswap V3
//!
//! Ramses-fork NFPMs use `int24 tickSpacing` at field position 2 of
//! `MintParams` (instead of Uniswap V3's `uint24 fee`). The `positions()`
//! return also uses `int24 tickSpacing` rather than `uint24 fee`.
//!
//! Verified against:
//! - Shadow: `Shadow-Exchange/shadow-core` `contracts/CL/periphery/interfaces/
//!   INonfungiblePositionManager.sol` (11-field MintParams, 10-field positions())
//! - Slipstream: `velodrome-finance/slipstream` `contracts/periphery/interfaces/
//!   INonfungiblePositionManager.sol` (12-field MintParams with sqrtPriceX96,
//!   12-field positions() with nonce/operator)

use alloy_primitives::{Address, B256, U256};

pub use wp_evm_v3_core::data::{
    CollectFeesParams, ExactInParams, ExactOutParams, PlanFragment, PoolState, Quote,
    RemoveAndCollectParams, RemoveLiquidityParams, TickInfo,
};

/// Parameters for an `add_liquidity` plan on a Ramses-family NFPM.
///
/// Uses `tick_spacing: i32` instead of `fee: u32` because Ramses-fork NFPMs
/// identify pools by `(token0, token1, tickSpacing)` — there is no `fee`
/// field in `MintParams`.
///
/// This replaces `wp_evm_v3_core::data::AddLiquidityParams` for all
/// Ramses-family protocols (Shadow, Slipstream, Aerodrome).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RamsesAddLiquidityParams {
    pub token0: Address,
    pub token1: Address,
    /// Pool-identifying tick spacing. Read from `PoolState.tick_spacing`.
    pub tick_spacing: i32,
    pub tick_lower: i32,
    pub tick_upper: i32,
    pub amount0_desired: U256,
    pub amount1_desired: U256,
    pub recipient: Address,
}

/// Hydrated state of a Ramses-family NFPM position.
///
/// Uses `tick_spacing: i32` instead of `fee: u32` because Ramses-fork
/// `positions()` returns `int24 tickSpacing` at that field position.
///
/// Compatible with both Shadow (10-field `positions()` return, no nonce/operator)
/// and Slipstream / Aerodrome (12-field return, has nonce/operator — those are
/// dropped since they're not needed by plan/quote functions).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PositionState {
    pub token_id: U256,
    pub owner: Address,
    pub token0: Address,
    pub token1: Address,
    pub tick_spacing: i32,
    pub tick_lower: i32,
    pub tick_upper: i32,
    pub liquidity: u128,
    pub fees_owed_0: U256,
    pub fees_owed_1: U256,
}

/// A resolved gauge reward claim for one gauge.
///
/// Mirrors arbitrage `GaugeRewardsItem`. `reward_tokens` and `token_ids`
/// are independent arrays (different lengths permitted — N reward tokens
/// vs M positions); the encoder places them positionally as
/// `claimClGaugeRewards(_gauges, _tokens, _nfpTokenIds)`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GaugeClaim {
    pub gauge: Address,
    pub reward_tokens: Vec<Address>,
    pub token_ids: Vec<U256>,
}

/// Raw `earned` reads for one gauge, laid out token-by-position.
///
/// `earned[t][p] == Gauge.earned(reward_tokens[t], token_ids[p])`. Produced
/// by the provider reader `gauge_earned_grids`; consumed by the pure
/// `build_gauge_claims` pruner. Always rectangular:
/// `earned.len() == reward_tokens.len()` and each row's len ==
/// `token_ids.len()`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GaugeEarnedGrid {
    pub gauge: Address,
    pub reward_tokens: Vec<Address>,
    pub token_ids: Vec<U256>,
    pub earned: Vec<Vec<U256>>,
}

/// Per-protocol configuration for a Ramses-family DEX.
///
/// Ramses pools are keyed by `(token0, token1, tickSpacing)` instead of
/// Uniswap V3's `(token0, token1, fee)`. `tick_spacings` enumerates the
/// valid tick spacings supported by the protocol.
///
/// Adding a new Ramses-fork DEX = creating a new protocol facade with a
/// `pub const CONFIG: RamsesProtocolConfig = ...`. No family code changes.
///
/// **CREATE2 deployment**: Shadow / Ramses-core uses an independent
/// `RamsesV3PoolDeployer` contract (≠ factory) for CREATE2. The
/// `pool_deployer` field is the actual CREATE2 caller used by
/// `pool_address::compute`. Velodrome-family protocols (Slipstream /
/// Aerodrome) deploy via EIP-1167 Clones instead and set
/// `pool_deployer = Address::ZERO`; their facade `pool_address` body
/// passes `cfg.factory` as the deployer (the factory contract IS the
/// CREATE2 caller for clone-pattern deployments).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RamsesProtocolConfig {
    pub factory: Address,
    /// CREATE2 deployer — `RamsesV3PoolDeployer` for Shadow-pattern,
    /// `Address::ZERO` for EIP-1167-Clone-pattern (Slipstream / Aerodrome).
    /// Read via `factory.ramsesV3PoolDeployer()` getter for CREATE2-deploying
    /// Ramses forks; clone-pattern facades pass `cfg.factory` instead at the
    /// `pool_address::compute` call site.
    pub pool_deployer: Address,
    pub router: Address,
    pub position_mgr: Address,
    pub init_code_hash: B256,
    pub tick_spacings: &'static [i32],
    pub multicall: Address,
    pub quoter: Option<Address>,
    pub voter: Address,
}

#[cfg(test)]
mod tests {
    use super::*;
    use alloy_primitives::address;

    #[test]
    fn gauge_claim_holds_independent_token_and_position_arrays() {
        let c = GaugeClaim {
            gauge: address!("4444444444444444444444444444444444444444"),
            reward_tokens: vec![
                address!("1111111111111111111111111111111111111111"),
                address!("2222222222222222222222222222222222222222"),
            ],
            token_ids: vec![U256::from(7u64)],
        };
        assert_eq!(c.reward_tokens.len(), 2);
        assert_eq!(c.token_ids.len(), 1);
    }

    #[test]
    fn gauge_earned_grid_is_rectangular_token_by_position() {
        let g = GaugeEarnedGrid {
            gauge: address!("4444444444444444444444444444444444444444"),
            reward_tokens: vec![Address::ZERO, Address::ZERO],
            token_ids: vec![U256::from(1u64), U256::from(2u64), U256::from(3u64)],
            earned: vec![vec![U256::ZERO; 3], vec![U256::ZERO; 3]],
        };
        assert_eq!(g.earned.len(), g.reward_tokens.len());
        assert_eq!(g.earned[0].len(), g.token_ids.len());
    }
}