1#![allow(non_upper_case_globals)]
10
11use crate::crypto::BridgeAuthorityPublicKey;
12use crate::error::BridgeError;
13use crate::error::BridgeResult;
14use crate::types::BridgeAction;
15use crate::types::SuiToEthTokenTransfer;
16use crate::types::SuiToEthTokenTransferV2;
17use alloy::primitives::Address as EthAddress;
18use fastcrypto::encoding::Encoding;
19use fastcrypto::encoding::Hex;
20use move_core_types::language_storage::StructTag;
21use once_cell::sync::OnceCell;
22use serde::{Deserialize, Serialize};
23use std::str::FromStr;
24use sui_json_rpc_types::SuiEvent;
25use sui_types::BRIDGE_PACKAGE_ID;
26use sui_types::TypeTag;
27use sui_types::base_types::SuiAddress;
28use sui_types::bridge::BridgeChainId;
29use sui_types::bridge::MoveTypeBridgeMessageKey;
30use sui_types::bridge::MoveTypeCommitteeMember;
31use sui_types::bridge::MoveTypeCommitteeMemberRegistration;
32use sui_types::collection_types::VecMap;
33use sui_types::crypto::ToFromBytes;
34use sui_types::parse_sui_type_tag;
35
36#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
38pub struct MoveTokenDepositedEvent {
39 pub seq_num: u64,
40 pub source_chain: u8,
41 pub sender_address: Vec<u8>,
42 pub target_chain: u8,
43 pub target_address: Vec<u8>,
44 pub token_type: u8,
45 pub amount_sui_adjusted: u64,
46}
47
48#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
50pub struct MoveTokenDepositedEventV2 {
51 pub seq_num: u64,
52 pub source_chain: u8,
53 pub sender_address: Vec<u8>,
54 pub target_chain: u8,
55 pub target_address: Vec<u8>,
56 pub token_type: u8,
57 pub amount_sui_adjusted: u64,
58 pub timestamp_ms: u64,
59}
60
61macro_rules! new_move_event {
62 ($struct_name:ident, $move_struct_name:ident) => {
63
64 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
66 pub struct $move_struct_name {
67 pub message_key: MoveTypeBridgeMessageKey,
68 }
69
70 #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)]
72 pub struct $struct_name {
73 pub nonce: u64,
74 pub source_chain: BridgeChainId,
75 }
76
77 impl TryFrom<$move_struct_name> for $struct_name {
78 type Error = BridgeError;
79
80 fn try_from(event: $move_struct_name) -> BridgeResult<Self> {
81 let source_chain = BridgeChainId::try_from(event.message_key.source_chain).map_err(|_e| {
82 BridgeError::Generic(format!(
83 "Failed to convert {} to {}. Failed to convert source chain {} to BridgeChainId",
84 stringify!($move_struct_name),
85 stringify!($struct_name),
86 event.message_key.source_chain,
87 ))
88 })?;
89 Ok(Self {
90 nonce: event.message_key.bridge_seq_num,
91 source_chain,
92 })
93 }
94 }
95 };
96}
97
98new_move_event!(TokenTransferClaimed, MoveTokenTransferClaimed);
99new_move_event!(TokenTransferApproved, MoveTokenTransferApproved);
100new_move_event!(
101 TokenTransferAlreadyApproved,
102 MoveTokenTransferAlreadyApproved
103);
104new_move_event!(TokenTransferAlreadyClaimed, MoveTokenTransferAlreadyClaimed);
105new_move_event!(TokenTransferLimitExceed, MoveTokenTransferLimitExceed);
106
107#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
109pub struct EmergencyOpEvent {
110 pub frozen: bool,
111}
112
113#[derive(Debug, Serialize, Deserialize, Clone)]
115pub struct MoveCommitteeUpdateEvent {
116 pub members: VecMap<Vec<u8>, MoveTypeCommitteeMember>,
117 pub stake_participation_percentage: u64,
118}
119
120#[derive(Debug, Serialize, Deserialize, Clone)]
122pub struct MoveCommitteeMemberUrlUpdateEvent {
123 pub member: Vec<u8>,
124 pub new_url: Vec<u8>,
125}
126
127#[derive(Debug, Serialize, Deserialize, Clone)]
129pub struct MoveBlocklistValidatorEvent {
130 pub blocklisted: bool,
131 pub public_keys: Vec<Vec<u8>>,
132}
133
134#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
136pub struct UpdateRouteLimitEvent {
137 pub sending_chain: u8,
138 pub receiving_chain: u8,
139 pub new_limit: u64,
140}
141
142#[derive(Debug, Serialize, Deserialize, Clone)]
144pub struct MoveTokenRegistrationEvent {
145 pub type_name: String,
146 pub decimal: u8,
147 pub native_token: bool,
148}
149
150#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
152pub struct TokenRegistrationEvent {
153 pub type_name: TypeTag,
154 pub decimal: u8,
155 pub native_token: bool,
156}
157
158impl TryFrom<MoveTokenRegistrationEvent> for TokenRegistrationEvent {
159 type Error = BridgeError;
160
161 fn try_from(event: MoveTokenRegistrationEvent) -> BridgeResult<Self> {
162 let type_name = parse_sui_type_tag(&format!("0x{}", event.type_name)).map_err(|e| {
163 BridgeError::InternalError(format!(
164 "Failed to parse TypeTag: {e}, type name: {}",
165 event.type_name
166 ))
167 })?;
168
169 Ok(Self {
170 type_name,
171 decimal: event.decimal,
172 native_token: event.native_token,
173 })
174 }
175}
176
177#[derive(Debug, Serialize, Deserialize, Clone)]
179pub struct MoveNewTokenEvent {
180 pub token_id: u8,
181 pub type_name: String,
182 pub native_token: bool,
183 pub decimal_multiplier: u64,
184 pub notional_value: u64,
185}
186
187#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
189pub struct NewTokenEvent {
190 pub token_id: u8,
191 pub type_name: TypeTag,
192 pub native_token: bool,
193 pub decimal_multiplier: u64,
194 pub notional_value: u64,
195}
196
197impl TryFrom<MoveNewTokenEvent> for NewTokenEvent {
198 type Error = BridgeError;
199
200 fn try_from(event: MoveNewTokenEvent) -> BridgeResult<Self> {
201 let type_name = parse_sui_type_tag(&format!("0x{}", event.type_name)).map_err(|e| {
202 BridgeError::InternalError(format!(
203 "Failed to parse TypeTag: {e}, type name: {}",
204 event.type_name
205 ))
206 })?;
207
208 Ok(Self {
209 token_id: event.token_id,
210 type_name,
211 native_token: event.native_token,
212 decimal_multiplier: event.decimal_multiplier,
213 notional_value: event.notional_value,
214 })
215 }
216}
217
218#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
220pub struct UpdateTokenPriceEvent {
221 pub token_id: u8,
222 pub new_price: u64,
223}
224
225#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)]
227pub struct EmittedSuiToEthTokenBridgeV1 {
228 pub nonce: u64,
229 pub sui_chain_id: BridgeChainId,
230 pub eth_chain_id: BridgeChainId,
231 pub sui_address: SuiAddress,
232 pub eth_address: EthAddress,
233 pub token_id: u8,
234 pub amount_sui_adjusted: u64,
236}
237
238#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Hash)]
240pub struct EmittedSuiToEthTokenBridgeV2 {
241 pub nonce: u64,
242 pub sui_chain_id: BridgeChainId,
243 pub eth_chain_id: BridgeChainId,
244 pub sui_address: SuiAddress,
245 pub eth_address: EthAddress,
246 pub token_id: u8,
247 pub amount_sui_adjusted: u64,
249 pub timestamp_ms: u64,
250}
251
252#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
254pub struct CommitteeUpdate {
255 pub members: Vec<MoveTypeCommitteeMember>,
256 pub stake_participation_percentage: u64,
257}
258
259impl TryFrom<MoveCommitteeUpdateEvent> for CommitteeUpdate {
260 type Error = BridgeError;
261
262 fn try_from(event: MoveCommitteeUpdateEvent) -> BridgeResult<Self> {
263 let members = event
264 .members
265 .contents
266 .into_iter()
267 .map(|v| v.value)
268 .collect();
269 Ok(Self {
270 members,
271 stake_participation_percentage: event.stake_participation_percentage,
272 })
273 }
274}
275
276#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
278pub struct BlocklistValidatorEvent {
279 pub blocklisted: bool,
280 pub public_keys: Vec<BridgeAuthorityPublicKey>,
281}
282
283impl TryFrom<MoveBlocklistValidatorEvent> for BlocklistValidatorEvent {
284 type Error = BridgeError;
285
286 fn try_from(event: MoveBlocklistValidatorEvent) -> BridgeResult<Self> {
287 let public_keys = event.public_keys.into_iter().map(|bytes|
288 BridgeAuthorityPublicKey::from_bytes(&bytes).map_err(|e|
289 BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert public key to BridgeAuthorityPublicKey: {:?}", e))
290 )
291 ).collect::<BridgeResult<Vec<_>>>()?;
292 Ok(Self {
293 blocklisted: event.blocklisted,
294 public_keys,
295 })
296 }
297}
298
299#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
301pub struct CommitteeMemberUrlUpdateEvent {
302 pub member: BridgeAuthorityPublicKey,
303 pub new_url: String,
304}
305
306impl TryFrom<MoveCommitteeMemberUrlUpdateEvent> for CommitteeMemberUrlUpdateEvent {
307 type Error = BridgeError;
308
309 fn try_from(event: MoveCommitteeMemberUrlUpdateEvent) -> BridgeResult<Self> {
310 let member = BridgeAuthorityPublicKey::from_bytes(&event.member).map_err(|e|
311 BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert public key to BridgeAuthorityPublicKey: {:?}", e))
312 )?;
313 let new_url = String::from_utf8(event.new_url).map_err(|e|
314 BridgeError::Generic(format!("Failed to convert MoveBlocklistValidatorEvent to BlocklistValidatorEvent. Failed to convert new_url to String: {:?}", e))
315 )?;
316 Ok(Self { member, new_url })
317 }
318}
319
320impl TryFrom<MoveTokenDepositedEvent> for EmittedSuiToEthTokenBridgeV1 {
321 type Error = BridgeError;
322
323 fn try_from(event: MoveTokenDepositedEvent) -> BridgeResult<Self> {
324 if event.amount_sui_adjusted == 0 {
325 return Err(BridgeError::ZeroValueBridgeTransfer(format!(
326 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Manual intervention is required. 0 value transfer should not be allowed in Move: {:?}",
327 event,
328 )));
329 }
330
331 let token_id = event.token_type;
332 let sui_chain_id = BridgeChainId::try_from(event.source_chain).map_err(|_e| {
333 BridgeError::Generic(format!(
334 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert source chain {} to BridgeChainId",
335 event.token_type,
336 ))
337 })?;
338 let eth_chain_id = BridgeChainId::try_from(event.target_chain).map_err(|_e| {
339 BridgeError::Generic(format!(
340 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert target chain {} to BridgeChainId",
341 event.token_type,
342 ))
343 })?;
344 if !sui_chain_id.is_sui_chain() {
345 return Err(BridgeError::Generic(format!(
346 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Invalid source chain {}",
347 event.source_chain
348 )));
349 }
350 if eth_chain_id.is_sui_chain() {
351 return Err(BridgeError::Generic(format!(
352 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Invalid target chain {}",
353 event.target_chain
354 )));
355 }
356
357 let sui_address = SuiAddress::from_bytes(event.sender_address)
358 .map_err(|e| BridgeError::Generic(format!("Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert sender_address to SuiAddress: {:?}", e)))?;
359 let eth_address = EthAddress::from_str(&Hex::encode(&event.target_address))?;
360
361 Ok(Self {
362 nonce: event.seq_num,
363 sui_chain_id,
364 eth_chain_id,
365 sui_address,
366 eth_address,
367 token_id,
368 amount_sui_adjusted: event.amount_sui_adjusted,
369 })
370 }
371}
372
373impl TryFrom<MoveTokenDepositedEventV2> for EmittedSuiToEthTokenBridgeV2 {
374 type Error = BridgeError;
375
376 fn try_from(event: MoveTokenDepositedEventV2) -> BridgeResult<Self> {
377 if event.amount_sui_adjusted == 0 {
378 return Err(BridgeError::ZeroValueBridgeTransfer(format!(
379 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Manual intervention is required. 0 value transfer should not be allowed in Move: {:?}",
380 event,
381 )));
382 }
383
384 let token_id = event.token_type;
385 let sui_chain_id = BridgeChainId::try_from(event.source_chain).map_err(|_e| {
386 BridgeError::Generic(format!(
387 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert source chain {} to BridgeChainId",
388 event.token_type,
389 ))
390 })?;
391 let eth_chain_id = BridgeChainId::try_from(event.target_chain).map_err(|_e| {
392 BridgeError::Generic(format!(
393 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert target chain {} to BridgeChainId",
394 event.token_type,
395 ))
396 })?;
397 if !sui_chain_id.is_sui_chain() {
398 return Err(BridgeError::Generic(format!(
399 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Invalid source chain {}",
400 event.source_chain
401 )));
402 }
403 if eth_chain_id.is_sui_chain() {
404 return Err(BridgeError::Generic(format!(
405 "Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Invalid target chain {}",
406 event.target_chain
407 )));
408 }
409
410 let sui_address = SuiAddress::from_bytes(event.sender_address)
411 .map_err(|e| BridgeError::Generic(format!("Failed to convert MoveTokenDepositedEvent to EmittedSuiToEthTokenBridgeV1. Failed to convert sender_address to SuiAddress: {:?}", e)))?;
412 let eth_address = EthAddress::from_str(&Hex::encode(&event.target_address))?;
413
414 Ok(Self {
415 nonce: event.seq_num,
416 sui_chain_id,
417 eth_chain_id,
418 sui_address,
419 eth_address,
420 token_id,
421 amount_sui_adjusted: event.amount_sui_adjusted,
422 timestamp_ms: event.timestamp_ms,
423 })
424 }
425}
426
427crate::declare_events!(
428 SuiToEthTokenBridgeV1(EmittedSuiToEthTokenBridgeV1) => ("bridge::TokenDepositedEvent", MoveTokenDepositedEvent),
429 TokenTransferApproved(TokenTransferApproved) => ("bridge::TokenTransferApproved", MoveTokenTransferApproved),
430 TokenTransferClaimed(TokenTransferClaimed) => ("bridge::TokenTransferClaimed", MoveTokenTransferClaimed),
431 TokenTransferAlreadyApproved(TokenTransferAlreadyApproved) => ("bridge::TokenTransferAlreadyApproved", MoveTokenTransferAlreadyApproved),
432 TokenTransferAlreadyClaimed(TokenTransferAlreadyClaimed) => ("bridge::TokenTransferAlreadyClaimed", MoveTokenTransferAlreadyClaimed),
433 TokenTransferLimitExceed(TokenTransferLimitExceed) => ("bridge::TokenTransferLimitExceed", MoveTokenTransferLimitExceed),
434 EmergencyOpEvent(EmergencyOpEvent) => ("bridge::EmergencyOpEvent", EmergencyOpEvent),
435 CommitteeMemberRegistration(MoveTypeCommitteeMemberRegistration) => ("committee::CommitteeMemberRegistration", MoveTypeCommitteeMemberRegistration),
438 CommitteeUpdateEvent(CommitteeUpdate) => ("committee::CommitteeUpdateEvent", MoveCommitteeUpdateEvent),
439 CommitteeMemberUrlUpdateEvent(CommitteeMemberUrlUpdateEvent) => ("committee::CommitteeMemberUrlUpdateEvent", MoveCommitteeMemberUrlUpdateEvent),
440 BlocklistValidatorEvent(BlocklistValidatorEvent) => ("committee::BlocklistValidatorEvent", MoveBlocklistValidatorEvent),
441 TokenRegistrationEvent(TokenRegistrationEvent) => ("treasury::TokenRegistrationEvent", MoveTokenRegistrationEvent),
442 NewTokenEvent(NewTokenEvent) => ("treasury::NewTokenEvent", MoveNewTokenEvent),
443 UpdateTokenPriceEvent(UpdateTokenPriceEvent) => ("treasury::UpdateTokenPriceEvent", UpdateTokenPriceEvent),
444 UpdateRouteLimitEvent(UpdateRouteLimitEvent) => ("limiter::UpdateRouteLimitEvent", UpdateRouteLimitEvent),
445 SuiToEthTokenBridgeV2(EmittedSuiToEthTokenBridgeV2) => ("bridge::TokenDepositedEventV2", MoveTokenDepositedEventV2),
446
447 );
450
451#[macro_export]
452macro_rules! declare_events {
453 ($($variant:ident($type:path) => ($event_tag:expr, $event_struct:path)),* $(,)?) => {
454
455 #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
456 pub enum SuiBridgeEvent {
457 $($variant($type),)*
458 }
459
460 $(pub static $variant: OnceCell<StructTag> = OnceCell::new();)*
461
462 pub(crate) fn init_all_struct_tags() {
463 $($variant.get_or_init(|| {
464 StructTag::from_str(&format!("0x{}::{}", BRIDGE_PACKAGE_ID.to_hex(), $event_tag)).unwrap()
465 });)*
466 }
467
468 impl SuiBridgeEvent {
470 pub fn try_from_sui_event(event: &SuiEvent) -> BridgeResult<Option<SuiBridgeEvent>> {
471 init_all_struct_tags(); $(
475 if &event.type_ == $variant.get().unwrap() {
476 let event_struct: $event_struct = bcs::from_bytes(event.bcs.bytes()).map_err(|e| BridgeError::InternalError(format!("Failed to deserialize event to {}: {:?}", stringify!($event_struct), e)))?;
477 return Ok(Some(SuiBridgeEvent::$variant(event_struct.try_into()?)));
478 }
479 )*
480 Ok(None)
481 }
482 }
483 };
484}
485
486impl SuiBridgeEvent {
487 pub fn try_into_bridge_action(self) -> Option<BridgeAction> {
488 match self {
489 SuiBridgeEvent::SuiToEthTokenBridgeV1(event) => {
490 let EmittedSuiToEthTokenBridgeV1 {
491 nonce,
492 sui_chain_id,
493 eth_chain_id,
494 sui_address,
495 eth_address,
496 token_id,
497 amount_sui_adjusted,
498 } = event;
499
500 Some(BridgeAction::SuiToEthTokenTransfer(SuiToEthTokenTransfer {
501 nonce,
502 sui_chain_id,
503 eth_chain_id,
504 sui_address,
505 eth_address,
506 token_id,
507 amount_adjusted: amount_sui_adjusted,
508 }))
509 }
510 SuiBridgeEvent::SuiToEthTokenBridgeV2(event) => Some(
511 BridgeAction::SuiToEthTokenTransferV2(SuiToEthTokenTransferV2 {
512 nonce: event.nonce,
513 sui_chain_id: event.sui_chain_id,
514 eth_chain_id: event.eth_chain_id,
515 sui_address: event.sui_address,
516 eth_address: event.eth_address,
517 token_id: event.token_id,
518 amount_adjusted: event.amount_sui_adjusted,
519 timestamp_ms: event.timestamp_ms,
520 }),
521 ),
522 SuiBridgeEvent::TokenTransferApproved(_event) => None,
523 SuiBridgeEvent::TokenTransferClaimed(_event) => None,
524 SuiBridgeEvent::TokenTransferAlreadyApproved(_event) => None,
525 SuiBridgeEvent::TokenTransferAlreadyClaimed(_event) => None,
526 SuiBridgeEvent::TokenTransferLimitExceed(_event) => None,
527 SuiBridgeEvent::EmergencyOpEvent(_event) => None,
528 SuiBridgeEvent::CommitteeMemberRegistration(_event) => None,
529 SuiBridgeEvent::CommitteeUpdateEvent(_event) => None,
530 SuiBridgeEvent::CommitteeMemberUrlUpdateEvent(_event) => None,
531 SuiBridgeEvent::BlocklistValidatorEvent(_event) => None,
532 SuiBridgeEvent::TokenRegistrationEvent(_event) => None,
533 SuiBridgeEvent::NewTokenEvent(_event) => None,
534 SuiBridgeEvent::UpdateTokenPriceEvent(_event) => None,
535 SuiBridgeEvent::UpdateRouteLimitEvent(_event) => None,
536 }
537 }
538}
539
540#[cfg(test)]
541pub mod tests {
542 use std::collections::HashSet;
543
544 use super::*;
545 use crate::crypto::BridgeAuthorityKeyPair;
546 use crate::e2e_tests::test_utils::BridgeTestClusterBuilder;
547 use crate::types::BridgeAction;
548 use crate::types::SuiToEthBridgeAction;
549 use alloy::primitives::Address as EthAddress;
550 use sui_json_rpc_types::BcsEvent;
551 use sui_json_rpc_types::SuiEvent;
552 use sui_types::Identifier;
553 use sui_types::base_types::ObjectID;
554 use sui_types::base_types::SuiAddress;
555 use sui_types::bridge::BridgeChainId;
556 use sui_types::bridge::TOKEN_ID_SUI;
557 use sui_types::crypto::get_key_pair;
558 use sui_types::digests::TransactionDigest;
559 use sui_types::event::EventID;
560
561 pub fn get_test_sui_event_and_action(identifier: Identifier) -> (SuiEvent, BridgeAction) {
563 init_all_struct_tags(); let sanitized_event = EmittedSuiToEthTokenBridgeV1 {
565 nonce: 1,
566 sui_chain_id: BridgeChainId::SuiTestnet,
567 sui_address: SuiAddress::random_for_testing_only(),
568 eth_chain_id: BridgeChainId::EthSepolia,
569 eth_address: EthAddress::random(),
570 token_id: TOKEN_ID_SUI,
571 amount_sui_adjusted: 100,
572 };
573 let emitted_event = MoveTokenDepositedEvent {
574 seq_num: sanitized_event.nonce,
575 source_chain: sanitized_event.sui_chain_id as u8,
576 sender_address: sanitized_event.sui_address.to_vec(),
577 target_chain: sanitized_event.eth_chain_id as u8,
578 target_address: sanitized_event.eth_address.to_vec(),
579 token_type: sanitized_event.token_id,
580 amount_sui_adjusted: sanitized_event.amount_sui_adjusted,
581 };
582
583 let tx_digest = TransactionDigest::random();
584 let event_idx = 10u16;
585 let bridge_action = BridgeAction::SuiToEthBridgeAction(SuiToEthBridgeAction {
586 sui_tx_digest: tx_digest,
587 sui_tx_event_index: event_idx,
588 sui_bridge_event: sanitized_event.clone(),
589 });
590 let event = SuiEvent {
591 type_: SuiToEthTokenBridgeV1.get().unwrap().clone(),
592 bcs: BcsEvent::new(bcs::to_bytes(&emitted_event).unwrap()),
593 id: EventID {
594 tx_digest,
595 event_seq: event_idx as u64,
596 },
597
598 package_id: ObjectID::ZERO,
601 transaction_module: identifier.clone(),
602 sender: SuiAddress::random_for_testing_only(),
603 parsed_json: serde_json::json!({"test": "test"}),
604 timestamp_ms: None,
605 };
606 (event, bridge_action)
607 }
608
609 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
610 async fn test_bridge_events_when_init() {
611 telemetry_subscribers::init_for_testing();
612 init_all_struct_tags();
613 let mut bridge_test_cluster = BridgeTestClusterBuilder::new()
614 .with_eth_env(false)
615 .with_bridge_cluster(false)
616 .with_num_validators(2)
617 .build()
618 .await;
619
620 let events = bridge_test_cluster
621 .new_bridge_events(
622 HashSet::from_iter([
623 CommitteeMemberRegistration.get().unwrap().clone(),
624 CommitteeUpdateEvent.get().unwrap().clone(),
625 TokenRegistrationEvent.get().unwrap().clone(),
626 NewTokenEvent.get().unwrap().clone(),
627 ]),
628 false,
629 )
630 .await;
631 let mut mask = 0u8;
632 for event in events.iter() {
633 match SuiBridgeEvent::try_from_sui_event(event).unwrap().unwrap() {
634 SuiBridgeEvent::CommitteeMemberRegistration(_event) => mask |= 0x1,
635 SuiBridgeEvent::CommitteeUpdateEvent(_event) => mask |= 0x2,
636 SuiBridgeEvent::TokenRegistrationEvent(_event) => mask |= 0x4,
637 SuiBridgeEvent::NewTokenEvent(_event) => mask |= 0x8,
638 _ => panic!("Got unexpected event: {:?}", event),
639 }
640 }
641 assert_eq!(mask, 0xF);
643
644 }
646
647 #[test]
648 fn test_conversion_for_committee_member_url_update_event() {
649 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
650 let new_url = "https://example.com:443";
651 let event: CommitteeMemberUrlUpdateEvent = MoveCommitteeMemberUrlUpdateEvent {
652 member: kp.public.as_bytes().to_vec(),
653 new_url: new_url.as_bytes().to_vec(),
654 }
655 .try_into()
656 .unwrap();
657 assert_eq!(event.member, kp.public);
658 assert_eq!(event.new_url, new_url);
659
660 CommitteeMemberUrlUpdateEvent::try_from(MoveCommitteeMemberUrlUpdateEvent {
661 member: vec![1, 2, 3],
662 new_url: new_url.as_bytes().to_vec(),
663 })
664 .unwrap_err();
665
666 CommitteeMemberUrlUpdateEvent::try_from(MoveCommitteeMemberUrlUpdateEvent {
667 member: kp.public.as_bytes().to_vec(),
668 new_url: [240, 130, 130, 172].into(),
669 })
670 .unwrap_err();
671 }
672
673 #[test]
676 fn test_0_sui_amount_conversion_for_sui_event() {
677 let emitted_event = MoveTokenDepositedEvent {
678 seq_num: 1,
679 source_chain: BridgeChainId::SuiTestnet as u8,
680 sender_address: SuiAddress::random_for_testing_only().to_vec(),
681 target_chain: BridgeChainId::EthSepolia as u8,
682 target_address: EthAddress::random().to_vec(),
683 token_type: TOKEN_ID_SUI,
684 amount_sui_adjusted: 0,
685 };
686 match EmittedSuiToEthTokenBridgeV1::try_from(emitted_event).unwrap_err() {
687 BridgeError::ZeroValueBridgeTransfer(_) => (),
688 other => panic!("Expected Generic error, got: {:?}", other),
689 }
690 }
691}