wp-evm-ramses-core 0.1.2

Pure data + quote + plan for Ramses-family CL DEXes
Documentation
//! Pure CREATE2 pool address derivation for Ramses-family CL DEXes.
//!
//! Mirrors Shadow-Exchange/shadow-core's `PoolAddress.computeAddress` and
//! also supports Velodrome-family deployments that use the factory itself as
//! the CREATE2 caller.

use alloy_primitives::{aliases::I24, aliases::U24, keccak256, Address, B256};
use alloy_sol_types::SolValue;

/// Derive the Ramses-family pool address for `(token_a, token_b, tick_spacing)`.
/// Tokens may be passed in any order — the function sorts them internally so
/// `token0 < token1`.
pub fn compute(
    deployer: Address,
    init_code_hash: B256,
    token_a: Address,
    token_b: Address,
    tick_spacing: i32,
) -> Address {
    debug_assert!(token_a != token_b, "pool_address::compute requires distinct tokens");

    let (token0, token1) = if token_a < token_b { (token_a, token_b) } else { (token_b, token_a) };

    const I24_MIN: i32 = -(1 << 23);
    const I24_MAX: i32 = (1 << 23) - 1;
    debug_assert!(
        (I24_MIN..=I24_MAX).contains(&tick_spacing),
        "tick_spacing must fit in int24 (-2^23 ..= 2^23 - 1)",
    );

    let tick_spacing_raw = U24::from((tick_spacing as u32) & 0x00ff_ffff);
    let tick_spacing = I24::from_raw(tick_spacing_raw);
    let salt = keccak256((token0, token1, tick_spacing).abi_encode());
    deployer.create2(salt, init_code_hash)
}

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

    const DEPLOYER: Address = address!("8BBDc15759a8eCf99A92E004E0C64ea9A5142d59");
    const INIT_CODE_HASH: B256 =
        b256!("c701ee63862761c31d620a4a083c61bdc1e81761e6b9c9267fd19afd22e0821d");

    /// USDC.e on Sonic
    const USDC_E: Address = address!("29219dD400f2Bf60E5a23d13Be72B486D4038894");
    /// Wrapped Sonic native (wS)
    const WS: Address = address!("039e2fb66102314Ce7b64Ce5CE3E5183bc94aD38");
    /// USDC.e / wS pool with tickSpacing=50 on Shadow Exchange Sonic.
    const USDCE_WS_TS50_POOL: Address = address!("324963c267C354c7660Ce8CA3F5f167E05649970");

    #[test]
    fn canonical_shadow_sonic_usdce_ws_tickspacing_50_matches_pool() {
        let pool = compute(DEPLOYER, INIT_CODE_HASH, USDC_E, WS, 50);
        assert_eq!(pool, USDCE_WS_TS50_POOL);
    }

    #[test]
    fn token_order_does_not_change_result() {
        let forward = compute(DEPLOYER, INIT_CODE_HASH, USDC_E, WS, 50);
        let reversed = compute(DEPLOYER, INIT_CODE_HASH, WS, USDC_E, 50);
        assert_eq!(forward, reversed);
        assert_eq!(forward, USDCE_WS_TS50_POOL);
    }

    #[test]
    fn different_tick_spacing_yields_different_address() {
        let p50 = compute(DEPLOYER, INIT_CODE_HASH, USDC_E, WS, 50);
        let p100 = compute(DEPLOYER, INIT_CODE_HASH, USDC_E, WS, 100);
        assert_ne!(p50, p100);
    }

    #[test]
    #[cfg(debug_assertions)]
    #[should_panic(expected = "distinct tokens")]
    fn identical_tokens_panics_in_debug() {
        let _ = compute(DEPLOYER, INIT_CODE_HASH, USDC_E, USDC_E, 50);
    }
}