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