use std::{collections::HashMap, sync::Arc, time::Duration};
use alloy::primitives::{Address, U256};
use futures::StreamExt;
use serde::Deserialize;
use tokio::{
sync::watch,
time::{sleep, timeout},
};
use tokio_tungstenite::{connect_async, tungstenite::Message};
use tracing::{info, warn};
use super::{OverrideSnapshot, StateOverrideProvider};
type VenueOverrides = HashMap<Address, HashMap<U256, U256>>;
#[derive(Debug, Deserialize)]
struct TitanMessage {
#[serde(rename = "blockNumber", default)]
block_number: Option<u64>,
#[serde(default)]
timestamp: Option<u64>,
#[serde(flatten)]
overrides: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Deserialize)]
struct AddressEntry {
#[serde(rename = "stateOverride", default)]
state_override: HashMap<Address, AccountState>,
}
#[derive(Debug, Deserialize)]
struct AccountState {
#[serde(rename = "stateDiff", default)]
state_diff: HashMap<U256, U256>,
}
const TITAN_URL: &str = "wss://eu.rpc.titanbuilder.xyz/ws/pamm_quote_stream";
const TITAN_URL_ENV: &str = "TITAN_PAMM_STREAM_URL";
const FERMISWAP_PROTOCOL_SYSTEM: &str = "vm:fermiswap";
const KIPSELI_PROTOCOL_SYSTEM: &str = "vm:kipseli";
const BOPAMM_PROTOCOL_SYSTEM: &str = "vm:bopamm";
const FERMISWAP_VENUE: &str = "0xb1076fe3ab5e28005c7c323bac5ac06a680d452e";
const KIPSELI_VENUE: &str = "0x5cdbe59400cc2efdcc2b54acca4a99fe00dd588c";
const BOPAMM_VENUE: &str = "0x160141a205f5ddcf096ba3f48b7ed21eb52c62ea";
const READ_IDLE_TIMEOUT: Duration = Duration::from_secs(30);
const CONNECT_TIMEOUT: Duration = Duration::from_secs(10);
const MAX_BACKOFF_EXP: u32 = 5;
const TITAN_QUOTE_TTL_SECS: u64 = 12;
fn known_pamm_protocols() -> &'static [&'static str] {
&[FERMISWAP_PROTOCOL_SYSTEM, KIPSELI_PROTOCOL_SYSTEM, BOPAMM_PROTOCOL_SYSTEM]
}
pub fn default_providers(
protocols: impl IntoIterator<Item = String>,
) -> HashMap<String, Arc<dyn StateOverrideProvider>> {
let needed: Vec<&'static str> = protocols
.into_iter()
.filter_map(|protocol| {
known_pamm_protocols()
.iter()
.copied()
.find(|&known| known == protocol.as_str())
})
.collect();
if needed.is_empty() {
return HashMap::new();
}
let url = std::env::var(TITAN_URL_ENV).unwrap_or_else(|_| TITAN_URL.to_string());
let provider: Arc<dyn StateOverrideProvider> = Arc::new(TitanProvider::spawn(url, &needed));
needed
.into_iter()
.map(|protocol| (protocol.to_string(), provider.clone()))
.collect()
}
pub struct TitanProvider {
receivers: HashMap<String, watch::Receiver<OverrideSnapshot>>,
}
impl TitanProvider {
pub fn spawn(url: String, protocols: &[&str]) -> Self {
let mut receivers = HashMap::new();
let mut senders = Vec::new();
for &protocol in protocols {
let Some(venue) = Self::venue_for_protocol(protocol) else {
warn!("Unknown protocol: {protocol}");
continue;
};
let (tx, rx) = watch::channel(OverrideSnapshot::default());
receivers.insert(protocol.to_string(), rx);
senders.push((venue, tx));
}
tokio::spawn(Self::run(url, senders));
Self { receivers }
}
async fn run(url: String, senders: Vec<(Address, watch::Sender<OverrideSnapshot>)>) {
let mut attempt: u32 = 0;
loop {
if senders
.iter()
.all(|(_, tx)| tx.is_closed())
{
warn!("All Titan override receivers dropped; stopping quote-stream task");
return;
}
match timeout(CONNECT_TIMEOUT, connect_async(url.as_str())).await {
Ok(Ok((mut ws_stream, _))) => {
info!(%url, "Connected to Titan pAMM quote stream");
loop {
let message = match timeout(READ_IDLE_TIMEOUT, ws_stream.next()).await {
Ok(Some(message)) => message,
Ok(None) => {
warn!("Titan quote stream ended; reconnecting");
break;
}
Err(_elapsed) => {
warn!(
idle_secs = READ_IDLE_TIMEOUT.as_secs(),
"No Titan message within idle timeout; reconnecting"
);
break;
}
};
match message {
Ok(Message::Text(text)) => {
attempt = 0;
match serde_json::from_str::<TitanMessage>(text.as_str()) {
Ok(message) => {
for (venue, sender) in &senders {
match Self::extract_venue(&message, venue) {
Ok(Some(snapshot)) => {
let _ = sender.send(snapshot);
}
Ok(None) => {}
Err(e) => {
warn!(%venue, error = %e, "Failed to extract Titan venue snapshot");
}
}
}
}
Err(e) => warn!(error = %e, "Failed to parse Titan message"),
}
if senders
.iter()
.all(|(_, tx)| tx.is_closed())
{
warn!("All Titan override receivers dropped; stopping quote-stream task");
return;
}
}
Ok(Message::Binary(bytes)) => {
warn!(len = bytes.len(), "Ignoring unexpected binary Titan frame");
}
Ok(Message::Ping(_)) | Ok(Message::Pong(_)) => {}
Ok(Message::Frame(_)) => {}
Ok(Message::Close(frame)) => {
warn!(?frame, "Titan quote stream closed by server; reconnecting");
break;
}
Err(e) => {
warn!(error = %e, "Titan quote stream read error; reconnecting");
break;
}
}
}
}
Ok(Err(e)) => {
warn!(error = %e, "Failed to connect to Titan quote stream; retrying");
}
Err(_elapsed) => {
warn!(
timeout_secs = CONNECT_TIMEOUT.as_secs(),
"Titan connect timed out; retrying"
);
}
}
attempt = attempt.saturating_add(1);
let backoff = Duration::from_secs(2u64.pow(attempt.min(MAX_BACKOFF_EXP)));
warn!(seconds = backoff.as_secs(), attempt, "Backing off before reconnecting to Titan");
sleep(backoff).await;
}
}
fn venue_for_protocol(protocol_system: &str) -> Option<Address> {
let raw = match protocol_system {
FERMISWAP_PROTOCOL_SYSTEM => FERMISWAP_VENUE,
KIPSELI_PROTOCOL_SYSTEM => KIPSELI_VENUE,
BOPAMM_PROTOCOL_SYSTEM => BOPAMM_VENUE,
_ => return None,
};
raw.parse().ok()
}
fn extract_venue(
message: &TitanMessage,
venue: &Address,
) -> Result<Option<OverrideSnapshot>, String> {
let venue_key = format!("{venue:#x}");
let Some(raw) = message.overrides.get(&venue_key) else {
return Ok(None);
};
let entry: AddressEntry = serde_json::from_value(raw.clone())
.map_err(|e| format!("invalid venue override: {e}"))?;
let block_number = message.block_number;
let mut storage: VenueOverrides = HashMap::new();
for (account, account_override) in entry.state_override {
if !account_override.state_diff.is_empty() {
storage.insert(account, account_override.state_diff);
}
}
let block_timestamp = Self::max_lane_timestamp(&storage);
let expires_at = message
.timestamp
.map(|ns| ns / 1_000_000_000 + TITAN_QUOTE_TTL_SECS);
Ok(Some(OverrideSnapshot {
block_number,
block_timestamp,
storage: Arc::new(storage),
expires_at,
}))
}
fn max_lane_timestamp(overrides: &VenueOverrides) -> Option<u64> {
const MIN_PLAUSIBLE: u64 = 1_000_000_000; const MAX_PLAUSIBLE: u64 = u32::MAX as u64; overrides
.values()
.flat_map(|slots| slots.values())
.filter_map(|value| {
let candidate = u64::try_from(*value >> 224).ok()?;
(MIN_PLAUSIBLE..=MAX_PLAUSIBLE)
.contains(&candidate)
.then_some(candidate)
})
.max()
}
}
impl StateOverrideProvider for TitanProvider {
fn subscribe(&self, protocol_system: &str) -> Option<watch::Receiver<OverrideSnapshot>> {
self.receivers
.get(protocol_system)
.cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn known_pamm_protocols_contains_fermiswap() {
assert!(known_pamm_protocols().contains(&FERMISWAP_PROTOCOL_SYSTEM));
}
#[test]
fn venue_for_protocol_resolves_all_known_venues() {
let cases = [
(FERMISWAP_PROTOCOL_SYSTEM, FERMISWAP_VENUE),
(KIPSELI_PROTOCOL_SYSTEM, KIPSELI_VENUE),
(BOPAMM_PROTOCOL_SYSTEM, BOPAMM_VENUE),
];
for (protocol, expected) in cases {
assert_eq!(
TitanProvider::venue_for_protocol(protocol).expect("venue must resolve"),
expected.parse::<Address>().unwrap(),
"venue mismatch for {protocol}",
);
}
}
#[test]
fn venue_for_protocol_returns_none_for_unknown() {
assert!(TitanProvider::venue_for_protocol("vm:unknown").is_none());
}
const SAMPLE_MESSAGE: &str = r#"{
"slot": 14285824,
"blockNumber": 25051224,
"timestamp": 1778253913749564761,
"0xb1076fe3ab5e28005c7c323bac5ac06a680d452e": {
"stateOverride": {
"0x1038c87766e36d1925889e6f26d10e0012d50fed": {
"balance": "0x0",
"nonce": "0x1",
"stateDiff": {
"0x156b1d71de08fed89d0fce38008e2b9d03a8998077e394b20597ef3d148f5ebc": "0x000000000000000000000000000000000000000000000000000000017e405801"
}
}
}
}
}"#;
#[test]
fn extract_venue_reads_state_diff() {
let venue = FERMISWAP_VENUE
.parse::<Address>()
.unwrap();
let message = serde_json::from_str::<TitanMessage>(SAMPLE_MESSAGE).expect("valid JSON");
let snapshot = TitanProvider::extract_venue(&message, &venue)
.expect("extract must succeed")
.expect("venue is present");
assert_eq!(snapshot.block_number, Some(25051224));
assert_eq!(snapshot.expires_at, Some(1778253913 + TITAN_QUOTE_TTL_SECS));
let account = "0x1038c87766e36d1925889e6f26d10e0012d50fed"
.parse::<Address>()
.unwrap();
let slot = "0x156b1d71de08fed89d0fce38008e2b9d03a8998077e394b20597ef3d148f5ebc"
.parse::<U256>()
.unwrap();
let value = "0x000000000000000000000000000000000000000000000000000000017e405801"
.parse::<U256>()
.unwrap();
assert_eq!(
snapshot
.storage
.get(&account)
.and_then(|s| s.get(&slot)),
Some(&value)
);
}
#[test]
fn extract_venue_returns_none_for_absent_venue() {
let venue = KIPSELI_VENUE
.parse::<Address>()
.unwrap();
let message = serde_json::from_str::<TitanMessage>(SAMPLE_MESSAGE).expect("valid JSON");
assert!(TitanProvider::extract_venue(&message, &venue)
.unwrap()
.is_none());
}
#[test]
fn max_lane_timestamp_picks_newest_plausible_top_word() {
let addr = FERMISWAP_VENUE
.parse::<Address>()
.unwrap();
let older = U256::from(1_700_000_000u64) << 224;
let newer = U256::from(1_800_000_000u64) << 224;
let non_lane = U256::from(42u64);
let overrides = HashMap::from([(
addr,
HashMap::from([
(U256::from(0u64), older),
(U256::from(1u64), newer),
(U256::from(2u64), non_lane),
]),
)]);
assert_eq!(TitanProvider::max_lane_timestamp(&overrides), Some(1_800_000_000));
}
#[test]
fn max_lane_timestamp_none_when_no_plausible_values() {
let addr = FERMISWAP_VENUE
.parse::<Address>()
.unwrap();
let overrides =
HashMap::from([(addr, HashMap::from([(U256::from(0u64), U256::from(7u64))]))]);
assert!(TitanProvider::max_lane_timestamp(&overrides).is_none());
}
}