1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
//! Algebra V1.9 pool immutable/deploy-parameter reads.
use alloy_sol_types::sol;
sol! {
interface IAlgebraPoolImmutables {
function token0() external view returns (address);
function token1() external view returns (address);
function tickSpacing() external view returns (int24);
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_sol_types::SolCall;
#[test]
fn selectors_match_known_values() {
assert_eq!(IAlgebraPoolImmutables::token0Call::SELECTOR, [0x0d, 0xfe, 0x16, 0x81]);
assert_eq!(IAlgebraPoolImmutables::token1Call::SELECTOR, [0xd2, 0x12, 0x20, 0xa7]);
assert_eq!(IAlgebraPoolImmutables::tickSpacingCall::SELECTOR, [0xd0, 0xc9, 0x3a, 0x7c]);
}
/// Lock the three Algebra immutable reads against real-world bytes
/// from the canonical QuickSwap V3 USDC.e/USDT pool
/// (`0x7b925e617aefd7fb3a93abe3a701135d7a1ba710`) on Polygon at
/// block `86_077_558` (`0x520F046`).
///
/// **Three separate captures, one test.** Each `cast rpc eth_call`
/// hits one immutable selector; the Multicall3 path was rejected
/// per plan F2 in favor of clearer per-function provenance and no
/// new test-deps. No `fee()` because Algebra pools are dynamic-fee
/// — the per-pool fee lives in `globalState().fee`, not in an
/// immutable.
///
/// At this pool: `token0 = USDC.e` (Polygon bridged USDC),
/// `token1 = USDT`, `tickSpacing = 60` (Algebra V1 default for
/// 0.05%-class pools).
///
/// Captured 2026-05-28 via (one cast per selector):
/// cast rpc eth_call --rpc-url <POLYGON_MAINNET_RPC> \
/// '{"to":"0x7b925e617aefd7fb3a93abe3a701135d7a1ba710",\
/// "data":"0x0dfe1681"}' \
/// 0x520F046
/// cast rpc eth_call --rpc-url <POLYGON_MAINNET_RPC> \
/// '{"to":"0x7b925e617aefd7fb3a93abe3a701135d7a1ba710",\
/// "data":"0xd21220a7"}' \
/// 0x520F046
/// cast rpc eth_call --rpc-url <POLYGON_MAINNET_RPC> \
/// '{"to":"0x7b925e617aefd7fb3a93abe3a701135d7a1ba710",\
/// "data":"0xd0c93a7c"}' \
/// 0x520F046
#[test]
fn real_quickswap_immutables_decode() {
use alloy_primitives::{address, aliases::I24};
// token0() — USDC.e on Polygon.
let raw_token0 = alloy_primitives::hex!(
"0000000000000000000000002791bca1f2de4661ed88a30c99a7a9449aa84174" // token0
);
let token0 = IAlgebraPoolImmutables::token0Call::abi_decode_returns(&raw_token0)
.expect("decode token0 address");
assert_eq!(token0, address!("2791Bca1f2de4661ED88A30C99A7a9449Aa84174"));
// token1() — USDT on Polygon.
let raw_token1 = alloy_primitives::hex!(
"000000000000000000000000c2132d05d31c914a87c6611c10748aeb04b58e8f" // token1
);
let token1 = IAlgebraPoolImmutables::token1Call::abi_decode_returns(&raw_token1)
.expect("decode token1 address");
assert_eq!(token1, address!("c2132D05D31c914a87C6611C10748AEb04B58e8F"));
// tickSpacing() — 60 (Algebra V1 default for narrow-band pools).
let raw_tick_spacing = alloy_primitives::hex!(
"000000000000000000000000000000000000000000000000000000000000003c" // tickSpacing (i24)
);
let tick_spacing =
IAlgebraPoolImmutables::tickSpacingCall::abi_decode_returns(&raw_tick_spacing)
.expect("decode tickSpacing i24");
assert_eq!(tick_spacing, I24::try_from(60i32).unwrap());
}
}