Skip to content

Commit dc50252

Browse files
authored
Merge pull request #333 from propeller-heads/hooks/tnl/ENG-5030-pass-block
fix: Update block in UniswapV4State and EVMPoolState
2 parents 23c3b1f + e929d61 commit dc50252

File tree

3 files changed

+182
-4
lines changed

3 files changed

+182
-4
lines changed

src/evm/decoder.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use alloy::primitives::{Address, U256};
1010
use thiserror::Error;
1111
use tokio::sync::{RwLock, RwLockReadGuard};
1212
use tracing::{debug, error, info, warn};
13-
use tycho_client::feed::{synchronizer::ComponentWithState, FeedMessage, HeaderLike};
13+
use tycho_client::feed::{synchronizer::ComponentWithState, BlockHeader, FeedMessage, HeaderLike};
1414
use tycho_common::{
1515
dto::{ChangeType, ProtocolStateDelta},
1616
models::{token::Token, Chain},
@@ -229,6 +229,11 @@ where
229229
.header
230230
.clone();
231231

232+
let block_number_or_timestamp = header
233+
.clone()
234+
.block_number_or_timestamp();
235+
let current_block = header.clone().block();
236+
232237
for (protocol, protocol_msg) in msg.state_msgs.iter() {
233238
// Add any new tokens
234239
if let Some(deltas) = protocol_msg.deltas.as_ref() {
@@ -698,9 +703,11 @@ where
698703

699704
// update states with protocol state deltas (attribute changes etc.)
700705
for (id, update) in deltas.state_updates {
706+
let update_with_block =
707+
Self::add_block_info_to_delta(update, current_block.clone());
701708
match Self::apply_update(
702709
&id,
703-
update,
710+
update_with_block,
704711
&mut updated_states,
705712
&state_guard,
706713
&all_balances,
@@ -733,9 +740,13 @@ where
733740

734741
// update remaining pools linked to updated contracts/updated balances
735742
for pool in pools_to_update {
743+
let default_delta_with_block = Self::add_block_info_to_delta(
744+
ProtocolStateDelta::default(),
745+
current_block.clone(),
746+
);
736747
match Self::apply_update(
737748
&pool,
738-
ProtocolStateDelta::default(),
749+
default_delta_with_block,
739750
&mut updated_states,
740751
&state_guard,
741752
&all_balances,
@@ -792,11 +803,31 @@ where
792803
}
793804

794805
// Send the tick with all updated states
795-
Ok(Update::new(header.block_number_or_timestamp(), updated_states, new_pairs)
806+
Ok(Update::new(block_number_or_timestamp, updated_states, new_pairs)
796807
.set_removed_pairs(removed_pairs)
797808
.set_sync_states(msg.sync_states.clone()))
798809
}
799810

811+
/// Add block information (number and timestamp) to a ProtocolStateDelta
812+
fn add_block_info_to_delta(
813+
mut delta: ProtocolStateDelta,
814+
block_header_opt: Option<BlockHeader>,
815+
) -> ProtocolStateDelta {
816+
if let Some(header) = block_header_opt {
817+
// Add block_number and block_timestamp attributes to ensure pool states
818+
// receive current block information during delta_transition
819+
delta.updated_attributes.insert(
820+
"block_number".to_string(),
821+
Bytes::from(header.number.to_be_bytes().to_vec()),
822+
);
823+
delta.updated_attributes.insert(
824+
"block_timestamp".to_string(),
825+
Bytes::from(header.timestamp.to_be_bytes().to_vec()),
826+
);
827+
}
828+
delta
829+
}
830+
800831
fn apply_update(
801832
id: &String,
802833
update: ProtocolStateDelta,

src/evm/protocol/uniswap_v4/state.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,39 @@ impl ProtocolSim for UniswapV4State {
712712
}
713713
}
714714

715+
// Update block information if provided in the delta attributes
716+
let block_number_bytes = delta
717+
.updated_attributes
718+
.get("block_number")
719+
.ok_or_else(|| {
720+
SimulationError::FatalError("block_number not found in updated attributes".into())
721+
})?;
722+
723+
self.block.number = u64::from_be_bytes(
724+
block_number_bytes[block_number_bytes.len() - 8..]
725+
.try_into()
726+
.map_err(|_| {
727+
TransitionError::DecodeError("Invalid block_number bytes".to_string())
728+
})?,
729+
);
730+
731+
let block_timestamp_bytes = delta
732+
.updated_attributes
733+
.get("block_timestamp")
734+
.ok_or_else(|| {
735+
SimulationError::FatalError(
736+
"block_timestamp not found in updated attributes".into(),
737+
)
738+
})?;
739+
740+
self.block.timestamp = u64::from_be_bytes(
741+
block_timestamp_bytes[block_timestamp_bytes.len() - 8..]
742+
.try_into()
743+
.map_err(|_| {
744+
TransitionError::DecodeError("Invalid block_timestamp bytes".to_string())
745+
})?,
746+
);
747+
715748
// Apply attribute changes
716749
if let Some(liquidity) = delta
717750
.updated_attributes
@@ -862,6 +895,46 @@ mod tests {
862895
)
863896
}
864897

898+
#[test]
899+
fn test_delta_transition_missing_block_update() {
900+
let block = BlockHeader {
901+
number: 1000,
902+
hash: Bytes::from_str(
903+
"0x28d41d40f2ac275a4f5f621a636b9016b527d11d37d610a45ac3a821346ebf8c",
904+
)
905+
.expect("Invalid block hash"),
906+
parent_hash: Bytes::from(vec![0; 32]),
907+
revert: false,
908+
timestamp: 1758201863,
909+
};
910+
let mut pool = UniswapV4State::new(
911+
1000,
912+
U256::from_str("1000").unwrap(),
913+
UniswapV4Fees { zero_for_one: 100, one_for_zero: 90, lp_fee: 700 },
914+
100,
915+
60,
916+
vec![TickInfo::new(120, 10000), TickInfo::new(180, -10000)],
917+
block,
918+
);
919+
920+
assert_eq!(pool.block.number, 1000);
921+
assert_eq!(pool.block.timestamp, 1758201863);
922+
923+
let attributes: HashMap<String, Bytes> =
924+
[("block_number".to_string(), Bytes::from(2000_u64.to_be_bytes().to_vec()))]
925+
.into_iter()
926+
.collect();
927+
928+
let delta = ProtocolStateDelta {
929+
component_id: "State1".to_owned(),
930+
updated_attributes: attributes,
931+
deleted_attributes: HashSet::new(),
932+
};
933+
934+
let result = pool.delta_transition(delta, &HashMap::new(), &Balances::default());
935+
assert!(result.is_err())
936+
}
937+
865938
#[test]
866939
fn test_delta_transition() {
867940
let block = BlockHeader {
@@ -893,6 +966,8 @@ mod tests {
893966
("fee".to_string(), Bytes::from(100_u32.to_be_bytes().to_vec())),
894967
("ticks/-120/net_liquidity".to_string(), Bytes::from(10200_u64.to_be_bytes().to_vec())),
895968
("ticks/120/net_liquidity".to_string(), Bytes::from(9800_u64.to_be_bytes().to_vec())),
969+
("block_number".to_string(), Bytes::from(2000_u64.to_be_bytes().to_vec())),
970+
("block_timestamp".to_string(), Bytes::from(1758201935_u64.to_be_bytes().to_vec())),
896971
]
897972
.into_iter()
898973
.collect();
@@ -926,6 +1001,9 @@ mod tests {
9261001
.net_liquidity,
9271002
9800
9281003
);
1004+
// Verify block was updated
1005+
assert_eq!(pool.block.number, 2000);
1006+
assert_eq!(pool.block.timestamp, 1758201935);
9291007
}
9301008

9311009
#[tokio::test]

src/evm/protocol/vm/state.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,39 @@ where
656656
tokens: &HashMap<Bytes, Token>,
657657
balances: &Balances,
658658
) -> Result<(), TransitionError<String>> {
659+
// Update block information if provided in the delta attributes
660+
let block_number_bytes = delta
661+
.updated_attributes
662+
.get("block_number")
663+
.ok_or_else(|| {
664+
SimulationError::FatalError("block_number not found in updated attributes".into())
665+
})?;
666+
667+
self.block.number = u64::from_be_bytes(
668+
block_number_bytes[block_number_bytes.len() - 8..]
669+
.try_into()
670+
.map_err(|_| {
671+
TransitionError::DecodeError("Invalid block_number bytes".to_string())
672+
})?,
673+
);
674+
675+
let block_timestamp_bytes = delta
676+
.updated_attributes
677+
.get("block_timestamp")
678+
.ok_or_else(|| {
679+
SimulationError::FatalError(
680+
"block_timestamp not found in updated attributes".into(),
681+
)
682+
})?;
683+
684+
self.block.timestamp = u64::from_be_bytes(
685+
block_timestamp_bytes[block_timestamp_bytes.len() - 8..]
686+
.try_into()
687+
.map_err(|_| {
688+
TransitionError::DecodeError("Invalid block_timestamp bytes".to_string())
689+
})?,
690+
);
691+
659692
if self.manual_updates {
660693
// Directly check for "update_marker" in `updated_attributes`
661694
if let Some(marker) = delta
@@ -1230,4 +1263,40 @@ mod tests {
12301263
"New token balance should be unchanged"
12311264
);
12321265
}
1266+
1267+
#[tokio::test]
1268+
async fn test_delta_transition_block_update() {
1269+
let mut pool_state = setup_pool_state().await;
1270+
1271+
let initial_number = pool_state.block.number;
1272+
let initial_timestamp = pool_state.block.timestamp;
1273+
1274+
let new_block_number = initial_number + 1;
1275+
let new_timestamp = initial_timestamp + 100;
1276+
1277+
let attributes: HashMap<String, Bytes> = [
1278+
("block_number".to_string(), Bytes::from(new_block_number.to_be_bytes().to_vec())),
1279+
("block_timestamp".to_string(), Bytes::from(new_timestamp.to_be_bytes().to_vec())),
1280+
]
1281+
.into_iter()
1282+
.collect();
1283+
1284+
let delta = ProtocolStateDelta {
1285+
component_id: "test_pool".to_owned(),
1286+
updated_attributes: attributes,
1287+
deleted_attributes: HashSet::new(),
1288+
};
1289+
1290+
let mut tokens = HashMap::new();
1291+
tokens.insert(dai().address, dai());
1292+
tokens.insert(bal().address, bal());
1293+
let balances = Balances::default();
1294+
1295+
pool_state
1296+
.delta_transition(delta, &tokens, &balances)
1297+
.unwrap();
1298+
1299+
assert_eq!(pool_state.block.number, new_block_number);
1300+
assert_eq!(pool_state.block.timestamp, new_timestamp);
1301+
}
12331302
}

0 commit comments

Comments
 (0)