Skip to main content

wp_evm_ramses_core/
data.rs

1//! Ramses-family data records.
2//!
3//! Re-exports shared types from `wp-evm-v3-core::data` where the ABI is
4//! identical, and defines Ramses-specific types where it differs.
5//!
6//! # ABI differences from Uniswap V3
7//!
8//! Ramses-fork NFPMs use `int24 tickSpacing` at field position 2 of
9//! `MintParams` (instead of Uniswap V3's `uint24 fee`). The `positions()`
10//! return also uses `int24 tickSpacing` rather than `uint24 fee`.
11//!
12//! Verified against:
13//! - Shadow: `Shadow-Exchange/shadow-core` `contracts/CL/periphery/interfaces/
14//!   INonfungiblePositionManager.sol` (11-field MintParams, 10-field positions())
15//! - Slipstream: `velodrome-finance/slipstream` `contracts/periphery/interfaces/
16//!   INonfungiblePositionManager.sol` (12-field MintParams with sqrtPriceX96,
17//!   12-field positions() with nonce/operator)
18
19use alloy_primitives::{Address, B256, U256};
20
21pub use wp_evm_v3_core::data::{
22    CollectFeesParams, ExactInParams, ExactOutParams, PlanFragment, PoolState, Quote,
23    RemoveAndCollectParams, RemoveLiquidityParams, TickInfo,
24};
25
26/// Parameters for an `add_liquidity` plan on a Ramses-family NFPM.
27///
28/// Uses `tick_spacing: i32` instead of `fee: u32` because Ramses-fork NFPMs
29/// identify pools by `(token0, token1, tickSpacing)` — there is no `fee`
30/// field in `MintParams`.
31///
32/// This replaces `wp_evm_v3_core::data::AddLiquidityParams` for all
33/// Ramses-family protocols (Shadow, Slipstream, Aerodrome).
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct RamsesAddLiquidityParams {
36    pub token0: Address,
37    pub token1: Address,
38    /// Pool-identifying tick spacing. Read from `PoolState.tick_spacing`.
39    pub tick_spacing: i32,
40    pub tick_lower: i32,
41    pub tick_upper: i32,
42    pub amount0_desired: U256,
43    pub amount1_desired: U256,
44    pub recipient: Address,
45}
46
47/// Hydrated state of a Ramses-family NFPM position.
48///
49/// Uses `tick_spacing: i32` instead of `fee: u32` because Ramses-fork
50/// `positions()` returns `int24 tickSpacing` at that field position.
51///
52/// Compatible with both Shadow (10-field `positions()` return, no nonce/operator)
53/// and Slipstream / Aerodrome (12-field return, has nonce/operator — those are
54/// dropped since they're not needed by plan/quote functions).
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct PositionState {
57    pub token_id: U256,
58    pub owner: Address,
59    pub token0: Address,
60    pub token1: Address,
61    pub tick_spacing: i32,
62    pub tick_lower: i32,
63    pub tick_upper: i32,
64    pub liquidity: u128,
65    pub fees_owed_0: U256,
66    pub fees_owed_1: U256,
67}
68
69/// A resolved gauge reward claim for one gauge.
70///
71/// Mirrors arbitrage `GaugeRewardsItem`. `reward_tokens` and `token_ids`
72/// are independent arrays (different lengths permitted — N reward tokens
73/// vs M positions); the encoder places them positionally as
74/// `claimClGaugeRewards(_gauges, _tokens, _nfpTokenIds)`.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct GaugeClaim {
77    pub gauge: Address,
78    pub reward_tokens: Vec<Address>,
79    pub token_ids: Vec<U256>,
80}
81
82/// Raw `earned` reads for one gauge, laid out token-by-position.
83///
84/// `earned[t][p] == Gauge.earned(reward_tokens[t], token_ids[p])`. Produced
85/// by the provider reader `gauge_earned_grids`; consumed by the pure
86/// `build_gauge_claims` pruner. Always rectangular:
87/// `earned.len() == reward_tokens.len()` and each row's len ==
88/// `token_ids.len()`.
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub struct GaugeEarnedGrid {
91    pub gauge: Address,
92    pub reward_tokens: Vec<Address>,
93    pub token_ids: Vec<U256>,
94    pub earned: Vec<Vec<U256>>,
95}
96
97/// Per-protocol configuration for a Ramses-family DEX.
98///
99/// Ramses pools are keyed by `(token0, token1, tickSpacing)` instead of
100/// Uniswap V3's `(token0, token1, fee)`. `tick_spacings` enumerates the
101/// valid tick spacings supported by the protocol.
102///
103/// Adding a new Ramses-fork DEX = creating a new protocol facade with a
104/// `pub const CONFIG: RamsesProtocolConfig = ...`. No family code changes.
105///
106/// **CREATE2 deployment**: Shadow / Ramses-core uses an independent
107/// `RamsesV3PoolDeployer` contract (≠ factory) for CREATE2. The
108/// `pool_deployer` field is the actual CREATE2 caller used by
109/// `pool_address::compute`. Velodrome-family protocols (Slipstream /
110/// Aerodrome) deploy via EIP-1167 Clones instead and set
111/// `pool_deployer = Address::ZERO`; their facade `pool_address` body
112/// passes `cfg.factory` as the deployer (the factory contract IS the
113/// CREATE2 caller for clone-pattern deployments).
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115pub struct RamsesProtocolConfig {
116    pub factory: Address,
117    /// CREATE2 deployer — `RamsesV3PoolDeployer` for Shadow-pattern,
118    /// `Address::ZERO` for EIP-1167-Clone-pattern (Slipstream / Aerodrome).
119    /// Read via `factory.ramsesV3PoolDeployer()` getter for CREATE2-deploying
120    /// Ramses forks; clone-pattern facades pass `cfg.factory` instead at the
121    /// `pool_address::compute` call site.
122    pub pool_deployer: Address,
123    pub router: Address,
124    pub position_mgr: Address,
125    pub init_code_hash: B256,
126    pub tick_spacings: &'static [i32],
127    pub multicall: Address,
128    pub quoter: Option<Address>,
129    pub voter: Address,
130}
131
132#[cfg(test)]
133mod tests {
134    use super::*;
135    use alloy_primitives::address;
136
137    #[test]
138    fn gauge_claim_holds_independent_token_and_position_arrays() {
139        let c = GaugeClaim {
140            gauge: address!("4444444444444444444444444444444444444444"),
141            reward_tokens: vec![
142                address!("1111111111111111111111111111111111111111"),
143                address!("2222222222222222222222222222222222222222"),
144            ],
145            token_ids: vec![U256::from(7u64)],
146        };
147        assert_eq!(c.reward_tokens.len(), 2);
148        assert_eq!(c.token_ids.len(), 1);
149    }
150
151    #[test]
152    fn gauge_earned_grid_is_rectangular_token_by_position() {
153        let g = GaugeEarnedGrid {
154            gauge: address!("4444444444444444444444444444444444444444"),
155            reward_tokens: vec![Address::ZERO, Address::ZERO],
156            token_ids: vec![U256::from(1u64), U256::from(2u64), U256::from(3u64)],
157            earned: vec![vec![U256::ZERO; 3], vec![U256::ZERO; 3]],
158        };
159        assert_eq!(g.earned.len(), g.reward_tokens.len());
160        assert_eq!(g.earned[0].len(), g.token_ids.len());
161    }
162}