use alloy_primitives::{keccak256, Address, B256, U256};
use wp_evm_algebra_interfaces::periphery::{
nfpm::IAlgebraNonfungiblePositionManager, nfpm_custom_deployer::IAlgebraCustomDeployerNfpm,
};
use wp_evm_base::evm::sign_extend_i24;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AlgebraPositionView {
pub token_id: U256,
pub nonce: U256,
pub operator: Address,
pub token0: Address,
pub token1: Address,
pub tick_lower: i32,
pub tick_upper: i32,
pub liquidity: u128,
pub fee_growth_inside_0_last_x128: U256,
pub fee_growth_inside_1_last_x128: U256,
pub tokens_owed_0_stale: u128,
pub tokens_owed_1_stale: u128,
}
impl AlgebraPositionView {
pub fn from_nfpm_returns(
token_id: U256,
ret: &IAlgebraNonfungiblePositionManager::positionsReturn,
) -> Self {
Self {
token_id,
nonce: U256::from(ret.nonce),
operator: ret.operator,
token0: ret.token0,
token1: ret.token1,
tick_lower: sign_extend_i24(ret.tickLower),
tick_upper: sign_extend_i24(ret.tickUpper),
liquidity: ret.liquidity,
fee_growth_inside_0_last_x128: ret.feeGrowthInside0LastX128,
fee_growth_inside_1_last_x128: ret.feeGrowthInside1LastX128,
tokens_owed_0_stale: ret.tokensOwed0,
tokens_owed_1_stale: ret.tokensOwed1,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CustomDeployerPositionView {
pub token_id: U256,
pub nonce: U256,
pub operator: Address,
pub token0: Address,
pub token1: Address,
pub deployer: Address,
pub tick_lower: i32,
pub tick_upper: i32,
pub liquidity: u128,
pub fee_growth_inside_0_last_x128: U256,
pub fee_growth_inside_1_last_x128: U256,
pub tokens_owed_0_stale: u128,
pub tokens_owed_1_stale: u128,
}
impl CustomDeployerPositionView {
pub fn from_nfpm_returns(
token_id: U256,
ret: &IAlgebraCustomDeployerNfpm::positionsReturn,
) -> Self {
Self {
token_id,
nonce: U256::from(ret.nonce),
operator: ret.operator,
token0: ret.token0,
token1: ret.token1,
deployer: ret.deployer,
tick_lower: sign_extend_i24(ret.tickLower),
tick_upper: sign_extend_i24(ret.tickUpper),
liquidity: ret.liquidity,
fee_growth_inside_0_last_x128: ret.feeGrowthInside0LastX128,
fee_growth_inside_1_last_x128: ret.feeGrowthInside1LastX128,
tokens_owed_0_stale: ret.tokensOwed0,
tokens_owed_1_stale: ret.tokensOwed1,
}
}
}
pub fn position_key(owner: Address, tick_lower: i32, tick_upper: i32) -> B256 {
assert!(
(-8_388_608..=8_388_607).contains(&tick_lower),
"tick_lower {tick_lower} outside int24 range",
);
assert!(
(-8_388_608..=8_388_607).contains(&tick_upper),
"tick_upper {tick_upper} outside int24 range",
);
let mut buf = [0u8; 26];
buf[..20].copy_from_slice(owner.as_slice());
buf[20..23].copy_from_slice(&i24_to_be_bytes(tick_lower));
buf[23..26].copy_from_slice(&i24_to_be_bytes(tick_upper));
keccak256(buf)
}
fn i24_to_be_bytes(tick: i32) -> [u8; 3] {
let bytes = tick.to_be_bytes();
[bytes[1], bytes[2], bytes[3]]
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{address, aliases::I24};
#[test]
fn position_key_matches_v3_reference_vector() {
let owner = address!("000000000000000000000000000000000000dEaD");
let key = position_key(owner, -887_220, 887_220);
assert_eq!(
key,
alloy_primitives::b256!(
"1de7686f3203885e60772ba1129871b5a5d3c9b611ecdb79223ca2e8da545eb2"
)
);
}
#[test]
fn from_nfpm_returns_sign_extends_ticks() {
let ret = IAlgebraNonfungiblePositionManager::positionsReturn {
nonce: alloy_primitives::aliases::U96::ZERO,
operator: Address::ZERO,
token0: address!("0000000000000000000000000000000000000001"),
token1: address!("0000000000000000000000000000000000000002"),
tickLower: I24::try_from(-120i32).unwrap(),
tickUpper: I24::try_from(240i32).unwrap(),
liquidity: 42,
feeGrowthInside0LastX128: U256::from(7),
feeGrowthInside1LastX128: U256::from(8),
tokensOwed0: 9,
tokensOwed1: 10,
};
let view = AlgebraPositionView::from_nfpm_returns(U256::from(1), &ret);
assert_eq!(view.tick_lower, -120);
assert_eq!(view.tick_upper, 240);
assert_eq!(view.tokens_owed_1_stale, 10);
}
}
#[cfg(test)]
mod custom_deployer_tests {
use super::*;
use alloy_primitives::{address, aliases::U88};
#[test]
fn maps_deployer_and_tokens_owed() {
let deployer = address!("5D433A94A4a2aA8f9AA34D8D15692Dc2E9960584");
let ret = IAlgebraCustomDeployerNfpm::positionsReturn {
nonce: U88::from(1u64),
operator: Address::ZERO,
token0: address!("2222222222222222222222222222222222222222"),
token1: address!("3333333333333333333333333333333333333333"),
deployer,
tickLower: alloy_primitives::aliases::I24::try_from(-60i32).unwrap(),
tickUpper: alloy_primitives::aliases::I24::try_from(60i32).unwrap(),
liquidity: 5u128,
feeGrowthInside0LastX128: U256::ZERO,
feeGrowthInside1LastX128: U256::ZERO,
tokensOwed0: 1000u128,
tokensOwed1: 2000u128,
};
let v = CustomDeployerPositionView::from_nfpm_returns(U256::from(42u64), &ret);
assert_eq!(v.deployer, deployer);
assert_eq!(v.tick_lower, -60);
assert_eq!(v.tokens_owed_0_stale, 1000);
assert_eq!(v.tokens_owed_1_stale, 2000);
}
}