1use crate::types::AddTokensOnEvmAction;
5use crate::types::AddTokensOnSuiAction;
6use crate::types::AssetPriceUpdateAction;
7use crate::types::BlocklistCommitteeAction;
8use crate::types::BridgeAction;
9use crate::types::BridgeActionType;
10use crate::types::EmergencyAction;
11use crate::types::EthToSuiBridgeAction;
12use crate::types::EthToSuiTokenTransferV2;
13use crate::types::EvmContractUpgradeAction;
14use crate::types::LimitUpdateAction;
15use crate::types::SuiToEthBridgeAction;
16use crate::types::SuiToEthTokenTransfer;
17use crate::types::SuiToEthTokenTransferV2;
18use alloy::primitives::Address as EthAddress;
19use alloy::sol_types::SolValue;
20use anyhow::Result;
21use enum_dispatch::enum_dispatch;
22use sui_types::base_types::SUI_ADDRESS_LENGTH;
23
24pub const TOKEN_TRANSFER_MESSAGE_VERSION_V1: u8 = 1;
25pub const TOKEN_TRANSFER_MESSAGE_VERSION_V2: u8 = 2;
26pub const COMMITTEE_BLOCKLIST_MESSAGE_VERSION: u8 = 1;
27pub const EMERGENCY_BUTTON_MESSAGE_VERSION: u8 = 1;
28pub const LIMIT_UPDATE_MESSAGE_VERSION: u8 = 1;
29pub const ASSET_PRICE_UPDATE_MESSAGE_VERSION: u8 = 1;
30pub const EVM_CONTRACT_UPGRADE_MESSAGE_VERSION: u8 = 1;
31pub const ADD_TOKENS_ON_SUI_MESSAGE_VERSION: u8 = 1;
32pub const ADD_TOKENS_ON_EVM_MESSAGE_VERSION: u8 = 1;
33
34pub const BRIDGE_MESSAGE_PREFIX: &[u8] = b"SUI_BRIDGE_MESSAGE";
35
36#[enum_dispatch]
43pub trait BridgeMessageEncoding {
44 fn as_bytes(&self) -> anyhow::Result<Vec<u8>>;
46 fn as_payload_bytes(&self) -> anyhow::Result<Vec<u8>>;
48}
49
50impl BridgeMessageEncoding for SuiToEthBridgeAction {
51 fn as_bytes(&self) -> Result<Vec<u8>> {
52 let mut bytes = Vec::new();
53 let e = &self.sui_bridge_event;
54 bytes.push(BridgeActionType::TokenTransfer as u8);
56 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION_V1);
58 bytes.extend_from_slice(&e.nonce.to_be_bytes());
60 bytes.push(e.sui_chain_id as u8);
62
63 bytes.extend_from_slice(&self.as_payload_bytes()?);
65
66 Ok(bytes)
67 }
68
69 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
70 let mut bytes = Vec::new();
71 let e = &self.sui_bridge_event;
72
73 bytes.push(SUI_ADDRESS_LENGTH as u8);
75 bytes.extend_from_slice(&e.sui_address.to_vec());
77 bytes.push(e.eth_chain_id as u8);
79 bytes.push(EthAddress::len_bytes() as u8);
81 bytes.extend_from_slice(e.eth_address.as_slice());
83
84 bytes.push(e.token_id);
86
87 bytes.extend_from_slice(&e.amount_sui_adjusted.to_be_bytes());
89
90 Ok(bytes)
91 }
92}
93
94impl BridgeMessageEncoding for SuiToEthTokenTransfer {
95 fn as_bytes(&self) -> Result<Vec<u8>> {
96 let mut bytes = Vec::new();
97 bytes.push(BridgeActionType::TokenTransfer as u8);
99 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION_V1);
101 bytes.extend_from_slice(&self.nonce.to_be_bytes());
103 bytes.push(self.sui_chain_id as u8);
105
106 bytes.extend_from_slice(&self.as_payload_bytes()?);
108
109 Ok(bytes)
110 }
111
112 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
113 let mut bytes = Vec::new();
114
115 bytes.push(SUI_ADDRESS_LENGTH as u8);
117 bytes.extend_from_slice(&self.sui_address.to_vec());
119 bytes.push(self.eth_chain_id as u8);
121 bytes.push(EthAddress::len_bytes() as u8);
123 bytes.extend_from_slice(self.eth_address.as_slice());
125
126 bytes.push(self.token_id);
128
129 bytes.extend_from_slice(&self.amount_adjusted.to_be_bytes());
131
132 Ok(bytes)
133 }
134}
135
136impl BridgeMessageEncoding for SuiToEthTokenTransferV2 {
137 fn as_bytes(&self) -> Result<Vec<u8>> {
138 let mut bytes = Vec::new();
139 bytes.push(BridgeActionType::TokenTransfer as u8);
141 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION_V2);
143 bytes.extend_from_slice(&self.nonce.to_be_bytes());
145 bytes.push(self.sui_chain_id as u8);
147
148 bytes.extend_from_slice(&self.as_payload_bytes()?);
150
151 Ok(bytes)
152 }
153
154 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
155 let mut bytes = Vec::new();
156
157 bytes.push(SUI_ADDRESS_LENGTH as u8);
159 bytes.extend_from_slice(&self.sui_address.to_vec());
161 bytes.push(self.eth_chain_id as u8);
163 bytes.push(EthAddress::len_bytes() as u8);
165 bytes.extend_from_slice(self.eth_address.as_slice());
167
168 bytes.push(self.token_id);
170
171 bytes.extend_from_slice(&self.amount_adjusted.to_be_bytes());
173
174 bytes.extend_from_slice(&self.timestamp_ms.to_be_bytes());
176
177 Ok(bytes)
178 }
179}
180
181impl BridgeMessageEncoding for EthToSuiBridgeAction {
182 fn as_bytes(&self) -> Result<Vec<u8>> {
183 let mut bytes = Vec::new();
184 let e = &self.eth_bridge_event;
185 bytes.push(BridgeActionType::TokenTransfer as u8);
187 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION_V1);
189 bytes.extend_from_slice(&e.nonce.to_be_bytes());
191 bytes.push(e.eth_chain_id as u8);
193
194 bytes.extend_from_slice(&self.as_payload_bytes()?);
196
197 Ok(bytes)
198 }
199
200 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
201 let mut bytes = Vec::new();
202 let e = &self.eth_bridge_event;
203
204 bytes.push(EthAddress::len_bytes() as u8);
206 bytes.extend_from_slice(e.eth_address.as_slice());
208 bytes.push(e.sui_chain_id as u8);
210 bytes.push(SUI_ADDRESS_LENGTH as u8);
212 bytes.extend_from_slice(&e.sui_address.to_vec());
214
215 bytes.push(e.token_id);
217
218 bytes.extend_from_slice(&e.sui_adjusted_amount.to_be_bytes());
220
221 Ok(bytes)
222 }
223}
224
225impl BridgeMessageEncoding for EthToSuiTokenTransferV2 {
226 fn as_bytes(&self) -> Result<Vec<u8>> {
227 let mut bytes = Vec::new();
228 let e = &self.eth_bridge_event;
229 bytes.push(BridgeActionType::TokenTransfer as u8);
231 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION_V2);
233 bytes.extend_from_slice(&e.nonce.to_be_bytes());
235 bytes.push(e.eth_chain_id as u8);
237
238 bytes.extend_from_slice(&self.as_payload_bytes()?);
240
241 Ok(bytes)
242 }
243
244 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
245 let mut bytes = Vec::new();
246 let e = &self.eth_bridge_event;
247
248 bytes.push(EthAddress::len_bytes() as u8);
250 bytes.extend_from_slice(e.eth_address.as_slice());
252 bytes.push(e.sui_chain_id as u8);
254 bytes.push(SUI_ADDRESS_LENGTH as u8);
256 bytes.extend_from_slice(&e.sui_address.to_vec());
258
259 bytes.push(e.token_id);
261
262 bytes.extend_from_slice(&e.sui_adjusted_amount.to_be_bytes());
264
265 let timestamp_ms = e.timestamp_seconds * 1000;
267 bytes.extend_from_slice(×tamp_ms.to_be_bytes());
268
269 Ok(bytes)
270 }
271}
272
273impl BridgeMessageEncoding for BlocklistCommitteeAction {
274 fn as_bytes(&self) -> Result<Vec<u8>> {
275 let mut bytes = Vec::new();
276 bytes.push(BridgeActionType::UpdateCommitteeBlocklist as u8);
278 bytes.push(COMMITTEE_BLOCKLIST_MESSAGE_VERSION);
280 bytes.extend_from_slice(&self.nonce.to_be_bytes());
282 bytes.push(self.chain_id as u8);
284
285 bytes.extend_from_slice(&self.as_payload_bytes()?);
287
288 Ok(bytes)
289 }
290
291 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
292 let mut bytes = Vec::new();
293
294 bytes.push(self.blocklist_type as u8);
296 bytes.push(u8::try_from(self.members_to_update.len())?);
299
300 let members_bytes = self
303 .members_to_update
304 .iter()
305 .map(|m| m.to_eth_address().to_vec())
306 .collect::<Vec<_>>();
307 for members_bytes in members_bytes {
308 bytes.extend_from_slice(&members_bytes);
309 }
310
311 Ok(bytes)
312 }
313}
314
315impl BridgeMessageEncoding for EmergencyAction {
316 fn as_bytes(&self) -> Result<Vec<u8>> {
317 let mut bytes = Vec::new();
318 bytes.push(BridgeActionType::EmergencyButton as u8);
320 bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION);
322 bytes.extend_from_slice(&self.nonce.to_be_bytes());
324 bytes.push(self.chain_id as u8);
326
327 bytes.extend_from_slice(&self.as_payload_bytes()?);
329
330 Ok(bytes)
331 }
332
333 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
334 Ok(vec![self.action_type as u8])
335 }
336}
337
338impl BridgeMessageEncoding for LimitUpdateAction {
339 fn as_bytes(&self) -> Result<Vec<u8>> {
340 let mut bytes = Vec::new();
341 bytes.push(BridgeActionType::LimitUpdate as u8);
343 bytes.push(LIMIT_UPDATE_MESSAGE_VERSION);
345 bytes.extend_from_slice(&self.nonce.to_be_bytes());
347 bytes.push(self.chain_id as u8);
349
350 bytes.extend_from_slice(&self.as_payload_bytes()?);
352
353 Ok(bytes)
354 }
355
356 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
357 let mut bytes = Vec::new();
358 bytes.push(self.sending_chain_id as u8);
360 bytes.extend_from_slice(&self.new_usd_limit.to_be_bytes());
362 Ok(bytes)
363 }
364}
365
366impl BridgeMessageEncoding for AssetPriceUpdateAction {
367 fn as_bytes(&self) -> Result<Vec<u8>> {
368 let mut bytes = Vec::new();
369 bytes.push(BridgeActionType::AssetPriceUpdate as u8);
371 bytes.push(ASSET_PRICE_UPDATE_MESSAGE_VERSION);
373 bytes.extend_from_slice(&self.nonce.to_be_bytes());
375 bytes.push(self.chain_id as u8);
377
378 bytes.extend_from_slice(&self.as_payload_bytes()?);
380
381 Ok(bytes)
382 }
383
384 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
385 let mut bytes = Vec::new();
386 bytes.push(self.token_id);
388 bytes.extend_from_slice(&self.new_usd_price.to_be_bytes());
390 Ok(bytes)
391 }
392}
393
394impl BridgeMessageEncoding for EvmContractUpgradeAction {
395 fn as_bytes(&self) -> Result<Vec<u8>> {
396 let mut bytes = Vec::new();
397 bytes.push(BridgeActionType::EvmContractUpgrade as u8);
399 bytes.push(EVM_CONTRACT_UPGRADE_MESSAGE_VERSION);
401 bytes.extend_from_slice(&self.nonce.to_be_bytes());
403 bytes.push(self.chain_id as u8);
405
406 bytes.extend_from_slice(&self.as_payload_bytes()?);
408
409 Ok(bytes)
410 }
411
412 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
413 Ok((self.proxy_address, self.new_impl_address, &self.call_data).abi_encode_params())
414 }
415}
416
417impl BridgeMessageEncoding for AddTokensOnSuiAction {
418 fn as_bytes(&self) -> Result<Vec<u8>> {
419 let mut bytes = Vec::new();
420 bytes.push(BridgeActionType::AddTokensOnSui as u8);
422 bytes.push(ADD_TOKENS_ON_SUI_MESSAGE_VERSION);
424 bytes.extend_from_slice(&self.nonce.to_be_bytes());
426 bytes.push(self.chain_id as u8);
428
429 bytes.extend_from_slice(&self.as_payload_bytes()?);
431
432 Ok(bytes)
433 }
434
435 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
436 let mut bytes = Vec::new();
437 bytes.push(self.native as u8);
439 bytes.extend_from_slice(&bcs::to_bytes(&self.token_ids)?);
441
442 bytes.extend_from_slice(&bcs::to_bytes(
444 &self
445 .token_type_names
446 .iter()
447 .map(|m| m.to_canonical_string(false))
448 .collect::<Vec<_>>(),
449 )?);
450
451 bytes.extend_from_slice(&bcs::to_bytes(&self.token_prices)?);
453
454 Ok(bytes)
455 }
456}
457
458impl BridgeMessageEncoding for AddTokensOnEvmAction {
459 fn as_bytes(&self) -> Result<Vec<u8>> {
460 let mut bytes = Vec::new();
461 bytes.push(BridgeActionType::AddTokensOnEvm as u8);
463 bytes.push(ADD_TOKENS_ON_EVM_MESSAGE_VERSION);
465 bytes.extend_from_slice(&self.nonce.to_be_bytes());
467 bytes.push(self.chain_id as u8);
469
470 bytes.extend_from_slice(&self.as_payload_bytes()?);
472
473 Ok(bytes)
474 }
475
476 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
477 let mut bytes = Vec::new();
478 bytes.push(self.native as u8);
480 bytes.push(u8::try_from(self.token_ids.len())?);
482 for token_id in &self.token_ids {
483 bytes.push(*token_id);
484 }
485
486 bytes.push(u8::try_from(self.token_addresses.len())?);
488 for token_address in &self.token_addresses {
489 bytes.extend_from_slice(token_address.as_slice());
490 }
491
492 bytes.push(u8::try_from(self.token_sui_decimals.len())?);
494 for token_sui_decimal in &self.token_sui_decimals {
495 bytes.push(*token_sui_decimal);
496 }
497
498 bytes.push(u8::try_from(self.token_prices.len())?);
500 for token_price in &self.token_prices {
501 bytes.extend_from_slice(&token_price.to_be_bytes());
502 }
503 Ok(bytes)
504 }
505}
506
507impl BridgeAction {
508 pub fn to_bytes(&self) -> Result<Vec<u8>> {
510 let mut bytes = Vec::new();
511 bytes.extend_from_slice(BRIDGE_MESSAGE_PREFIX);
513 bytes.extend_from_slice(&self.as_bytes()?);
515 Ok(bytes)
516 }
517}
518
519#[cfg(test)]
520mod tests {
521 use crate::abi::EthToSuiTokenBridgeV1;
522 use crate::crypto::BridgeAuthorityKeyPair;
523 use crate::crypto::BridgeAuthorityPublicKeyBytes;
524 use crate::crypto::BridgeAuthoritySignInfo;
525 use crate::events::EmittedSuiToEthTokenBridgeV1;
526 use crate::types::BlocklistType;
527 use crate::types::EmergencyActionType;
528 use crate::types::USD_MULTIPLIER;
529 use alloy::primitives::{Address as EthAddress, TxHash};
530 use fastcrypto::encoding::Encoding;
531 use fastcrypto::encoding::Hex;
532 use fastcrypto::hash::HashFunction;
533 use fastcrypto::hash::Keccak256;
534 use fastcrypto::traits::ToFromBytes;
535 use prometheus::Registry;
536 use std::str::FromStr;
537 use sui_types::TypeTag;
538 use sui_types::base_types::{SuiAddress, TransactionDigest};
539 use sui_types::bridge::BridgeChainId;
540 use sui_types::bridge::TOKEN_ID_BTC;
541 use sui_types::bridge::TOKEN_ID_USDC;
542
543 use super::*;
544
545 #[test]
546 fn test_bridge_message_encoding() -> anyhow::Result<()> {
547 telemetry_subscribers::init_for_testing();
548 let registry = Registry::new();
549 mysten_metrics::init_metrics(®istry);
550 let nonce = 54321u64;
551 let sui_tx_digest = TransactionDigest::random();
552 let sui_chain_id = BridgeChainId::SuiTestnet;
553 let sui_tx_event_index = 1u16;
554 let eth_chain_id = BridgeChainId::EthSepolia;
555 let sui_address = SuiAddress::random_for_testing_only();
556 let eth_address = EthAddress::random();
557 let token_id = TOKEN_ID_USDC;
558 let amount_sui_adjusted = 1_000_000;
559
560 let sui_bridge_event = EmittedSuiToEthTokenBridgeV1 {
561 nonce,
562 sui_chain_id,
563 eth_chain_id,
564 sui_address,
565 eth_address,
566 token_id,
567 amount_sui_adjusted,
568 };
569
570 let encoded_bytes = BridgeAction::SuiToEthBridgeAction(SuiToEthBridgeAction {
571 sui_tx_digest,
572 sui_tx_event_index,
573 sui_bridge_event,
574 })
575 .to_bytes()?;
576
577 let prefix_bytes = BRIDGE_MESSAGE_PREFIX.to_vec(); let message_type = vec![BridgeActionType::TokenTransfer as u8]; let message_version = vec![TOKEN_TRANSFER_MESSAGE_VERSION_V1]; let nonce_bytes = nonce.to_be_bytes().to_vec(); let source_chain_id_bytes = vec![sui_chain_id as u8]; let sui_address_length_bytes = vec![SUI_ADDRESS_LENGTH as u8]; let sui_address_bytes = sui_address.to_vec(); let dest_chain_id_bytes = vec![eth_chain_id as u8]; let eth_address_length_bytes = vec![EthAddress::len_bytes() as u8]; let eth_address_bytes = eth_address.to_vec(); let token_id_bytes = vec![token_id]; let token_amount_bytes = amount_sui_adjusted.to_be_bytes().to_vec(); let mut combined_bytes = Vec::new();
594 combined_bytes.extend_from_slice(&prefix_bytes);
595 combined_bytes.extend_from_slice(&message_type);
596 combined_bytes.extend_from_slice(&message_version);
597 combined_bytes.extend_from_slice(&nonce_bytes);
598 combined_bytes.extend_from_slice(&source_chain_id_bytes);
599 combined_bytes.extend_from_slice(&sui_address_length_bytes);
600 combined_bytes.extend_from_slice(&sui_address_bytes);
601 combined_bytes.extend_from_slice(&dest_chain_id_bytes);
602 combined_bytes.extend_from_slice(ð_address_length_bytes);
603 combined_bytes.extend_from_slice(ð_address_bytes);
604 combined_bytes.extend_from_slice(&token_id_bytes);
605 combined_bytes.extend_from_slice(&token_amount_bytes);
606
607 assert_eq!(combined_bytes, encoded_bytes);
608
609 assert_eq!(
612 combined_bytes.len(),
613 18 + 1 + 1 + 8 + 1 + 1 + 32 + 1 + 20 + 1 + 1 + 8
614 );
615 Ok(())
616 }
617
618 #[test]
619 fn test_bridge_message_encoding_regression_emitted_sui_to_eth_token_bridge_v1()
620 -> anyhow::Result<()> {
621 telemetry_subscribers::init_for_testing();
622 let registry = Registry::new();
623 mysten_metrics::init_metrics(®istry);
624 let sui_tx_digest = TransactionDigest::random();
625 let sui_tx_event_index = 1u16;
626
627 let nonce = 10u64;
628 let sui_chain_id = BridgeChainId::SuiTestnet;
629 let eth_chain_id = BridgeChainId::EthSepolia;
630 let sui_address = SuiAddress::from_str(
631 "0x0000000000000000000000000000000000000000000000000000000000000064",
632 )
633 .unwrap();
634 let eth_address =
635 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
636 let token_id = TOKEN_ID_USDC;
637 let amount_sui_adjusted = 12345;
638
639 let sui_bridge_event = EmittedSuiToEthTokenBridgeV1 {
640 nonce,
641 sui_chain_id,
642 eth_chain_id,
643 sui_address,
644 eth_address,
645 token_id,
646 amount_sui_adjusted,
647 };
648 let encoded_bytes = BridgeAction::SuiToEthBridgeAction(SuiToEthBridgeAction {
649 sui_tx_digest,
650 sui_tx_event_index,
651 sui_bridge_event,
652 })
653 .to_bytes()?;
654 assert_eq!(
655 encoded_bytes,
656 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a012000000000000000000000000000000000000000000000000000000000000000640b1400000000000000000000000000000000000000c8030000000000003039").unwrap(),
657 );
658
659 let hash = Keccak256::digest(encoded_bytes).digest;
660 assert_eq!(
661 hash.to_vec(),
662 Hex::decode("6ab34c52b6264cbc12fe8c3874f9b08f8481d2e81530d136386646dbe2f8baf4")
663 .unwrap(),
664 );
665 Ok(())
666 }
667
668 #[test]
669 fn test_bridge_message_encoding_blocklist_update_v1() {
670 telemetry_subscribers::init_for_testing();
671 let registry = Registry::new();
672 mysten_metrics::init_metrics(®istry);
673
674 let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes(
675 &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4")
676 .unwrap(),
677 )
678 .unwrap();
679 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
680 nonce: 129,
681 chain_id: BridgeChainId::SuiCustom,
682 blocklist_type: BlocklistType::Blocklist,
683 members_to_update: vec![pub_key_bytes.clone()],
684 });
685 let bytes = blocklist_action.to_bytes().unwrap();
686 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000008102000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
699
700 let pub_key_bytes_2 = BridgeAuthorityPublicKeyBytes::from_bytes(
701 &Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279")
702 .unwrap(),
703 )
704 .unwrap();
705 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
707 nonce: 68,
708 chain_id: BridgeChainId::SuiCustom,
709 blocklist_type: BlocklistType::Unblocklist,
710 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
711 });
712 let bytes = blocklist_action.to_bytes().unwrap();
713 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
727
728 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
729 nonce: 49,
730 chain_id: BridgeChainId::EthCustom,
731 blocklist_type: BlocklistType::Blocklist,
732 members_to_update: vec![pub_key_bytes.clone()],
733 });
734 let bytes = blocklist_action.to_bytes().unwrap();
735 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d455353414745010100000000000000310c000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
748
749 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
750 nonce: 94,
751 chain_id: BridgeChainId::EthSepolia,
752 blocklist_type: BlocklistType::Unblocklist,
753 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
754 });
755 let bytes = blocklist_action.to_bytes().unwrap();
756 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000005e0b010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
770 }
771
772 #[test]
773 fn test_bridge_message_encoding_emergency_action() {
774 let action = BridgeAction::EmergencyAction(EmergencyAction {
775 nonce: 55,
776 chain_id: BridgeChainId::SuiCustom,
777 action_type: EmergencyActionType::Pause,
778 });
779 let bytes = action.to_bytes().unwrap();
780 assert_eq!(
789 bytes,
790 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000370200").unwrap()
791 );
792
793 let action = BridgeAction::EmergencyAction(EmergencyAction {
794 nonce: 56,
795 chain_id: BridgeChainId::EthSepolia,
796 action_type: EmergencyActionType::Unpause,
797 });
798 let bytes = action.to_bytes().unwrap();
799 assert_eq!(
808 bytes,
809 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000380b01").unwrap()
810 );
811 }
812
813 #[test]
814 fn test_bridge_message_encoding_limit_update_action() {
815 let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
816 nonce: 15,
817 chain_id: BridgeChainId::SuiCustom,
818 sending_chain_id: BridgeChainId::EthCustom,
819 new_usd_limit: 1_000_000 * USD_MULTIPLIER, });
821 let bytes = action.to_bytes().unwrap();
822 assert_eq!(
832 bytes,
833 Hex::decode(
834 "5355495f4252494447455f4d4553534147450301000000000000000f020c00000002540be400"
835 )
836 .unwrap()
837 );
838 }
839
840 #[test]
841 fn test_bridge_message_encoding_asset_price_update_action() {
842 let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
843 nonce: 266,
844 chain_id: BridgeChainId::SuiCustom,
845 token_id: TOKEN_ID_BTC,
846 new_usd_price: 100_000 * USD_MULTIPLIER, });
848 let bytes = action.to_bytes().unwrap();
849 assert_eq!(
859 bytes,
860 Hex::decode(
861 "5355495f4252494447455f4d4553534147450401000000000000010a0201000000003b9aca00"
862 )
863 .unwrap()
864 );
865 }
866
867 #[test]
868 fn test_bridge_message_encoding_evm_contract_upgrade_action() {
869 let function_signature = "initializeV2()";
871 let selector = &Keccak256::digest(function_signature).digest[0..4];
872 let call_data = selector.to_vec();
873 assert_eq!(Hex::encode(call_data.clone()), "5cd8a76b");
874
875 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
876 nonce: 123,
877 chain_id: BridgeChainId::EthCustom,
878 proxy_address: EthAddress::repeat_byte(6),
879 new_impl_address: EthAddress::repeat_byte(9),
880 call_data,
881 });
882 assert_eq!(
896 Hex::encode(action.to_bytes().unwrap().clone()),
897 "5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"
898 );
899
900 let function_signature = "newMockFunction(bool)";
902 let selector = &Keccak256::digest(function_signature).digest[0..4];
903 let mut call_data = selector.to_vec();
904 call_data.extend(true.abi_encode());
905 assert_eq!(
906 Hex::encode(call_data.clone()),
907 "417795ef0000000000000000000000000000000000000000000000000000000000000001"
908 );
909 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
910 nonce: 123,
911 chain_id: BridgeChainId::EthCustom,
912 proxy_address: EthAddress::repeat_byte(6),
913 new_impl_address: EthAddress::repeat_byte(9),
914 call_data,
915 });
916 assert_eq!(
931 Hex::encode(action.to_bytes().unwrap().clone()),
932 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"
933 );
934
935 let function_signature = "newMockFunction(bool,uint8)";
937 let selector = &Keccak256::digest(function_signature).digest[0..4];
938 let mut call_data = selector.to_vec();
939 call_data.extend((true, alloy::primitives::U256::from(42)).abi_encode());
940 assert_eq!(
941 Hex::encode(call_data.clone()),
942 "be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a"
943 );
944 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
945 nonce: 123,
946 chain_id: BridgeChainId::EthCustom,
947 proxy_address: EthAddress::repeat_byte(6),
948 new_impl_address: EthAddress::repeat_byte(9),
949 call_data,
950 });
951 assert_eq!(
967 Hex::encode(action.to_bytes().unwrap().clone()),
968 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"
969 );
970
971 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
973 nonce: 123,
974 chain_id: BridgeChainId::EthCustom,
975 proxy_address: EthAddress::repeat_byte(6),
976 new_impl_address: EthAddress::repeat_byte(9),
977 call_data: vec![],
978 });
979 let data = action.to_bytes().unwrap();
992 assert_eq!(
993 Hex::encode(data.clone()),
994 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
995 );
996 let _decoded =
998 <(EthAddress, EthAddress, alloy::primitives::Bytes)>::abi_decode_params(&data[29..])
999 .unwrap();
1000 }
1001
1002 #[test]
1003 fn test_bridge_message_encoding_regression_eth_to_sui_token_bridge_v1() -> anyhow::Result<()> {
1004 telemetry_subscribers::init_for_testing();
1005 let registry = Registry::new();
1006 mysten_metrics::init_metrics(®istry);
1007 let eth_tx_hash = TxHash::random();
1008 let eth_event_index = 1u16;
1009
1010 let nonce = 10u64;
1011 let sui_chain_id = BridgeChainId::SuiTestnet;
1012 let eth_chain_id = BridgeChainId::EthSepolia;
1013 let sui_address = SuiAddress::from_str(
1014 "0x0000000000000000000000000000000000000000000000000000000000000064",
1015 )
1016 .unwrap();
1017 let eth_address =
1018 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
1019 let token_id = TOKEN_ID_USDC;
1020 let sui_adjusted_amount = 12345;
1021
1022 let eth_bridge_event = EthToSuiTokenBridgeV1 {
1023 nonce,
1024 sui_chain_id,
1025 eth_chain_id,
1026 sui_address,
1027 eth_address,
1028 token_id,
1029 sui_adjusted_amount,
1030 };
1031 let encoded_bytes = BridgeAction::EthToSuiBridgeAction(EthToSuiBridgeAction {
1032 eth_tx_hash,
1033 eth_event_index,
1034 eth_bridge_event,
1035 })
1036 .to_bytes()?;
1037
1038 assert_eq!(
1039 encoded_bytes,
1040 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a0b1400000000000000000000000000000000000000c801200000000000000000000000000000000000000000000000000000000000000064030000000000003039").unwrap(),
1041 );
1042
1043 let hash = Keccak256::digest(encoded_bytes).digest;
1044 assert_eq!(
1045 hash.to_vec(),
1046 Hex::decode("b352508c301a37bb1b68a75dd0fc42b6f692b2650818631c8f8a4d4d3e5bef46")
1047 .unwrap(),
1048 );
1049 Ok(())
1050 }
1051
1052 #[test]
1053 fn test_bridge_message_encoding_regression_add_coins_on_sui() -> anyhow::Result<()> {
1054 telemetry_subscribers::init_for_testing();
1055
1056 let action = BridgeAction::AddTokensOnSuiAction(AddTokensOnSuiAction {
1057 nonce: 0,
1058 chain_id: BridgeChainId::SuiCustom,
1059 native: false,
1060 token_ids: vec![1, 2, 3, 4],
1061 token_type_names: vec![
1062 TypeTag::from_str("0x9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC").unwrap(),
1063 TypeTag::from_str("0x7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH").unwrap(),
1064 TypeTag::from_str("0x500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC").unwrap(),
1065 TypeTag::from_str("0x46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT").unwrap(),
1066 ],
1067 token_prices: vec![
1068 500_000_000u64,
1069 30_000_000u64,
1070 1_000u64,
1071 1_000u64,
1072 ]
1073 });
1074 let encoded_bytes = action.to_bytes().unwrap();
1075
1076 assert_eq!(
1077 Hex::encode(encoded_bytes),
1078 "5355495f4252494447455f4d4553534147450601000000000000000002000401020304044a396235653133626364306362323366663235633037363938653839643438303536633734353333386438633964626430333361343137326238373032373037333a3a6274633a3a4254434a373937306437316330333537336635343061373135376630643339373065313137656666613661653136636566643530623435633734393637306232346536613a3a6574683a3a4554484c353030653432396132343437383430356435313330323232623230663835373061373436623662633232343233663134623464346536613865613538303733363a3a757364633a3a555344434c343662666535316461316264393531313931396139326562313135343134396233366330663432313231323138303865313365336535383537643630376139633a3a757364743a3a55534454040065cd1d0000000080c3c90100000000e803000000000000e803000000000000",
1079 );
1080 Ok(())
1081 }
1082
1083 #[test]
1084 fn test_bridge_message_encoding_regression_add_coins_on_evm() -> anyhow::Result<()> {
1085 let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction {
1086 nonce: 0,
1087 chain_id: BridgeChainId::EthCustom,
1088 native: true,
1089 token_ids: vec![99, 100, 101],
1090 token_addresses: vec![
1091 EthAddress::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(),
1092 EthAddress::from_str("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84").unwrap(),
1093 EthAddress::from_str("0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72").unwrap(),
1094 ],
1095 token_sui_decimals: vec![5, 6, 7],
1096 token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000],
1097 });
1098 let encoded_bytes = action.to_bytes().unwrap();
1099
1100 assert_eq!(
1101 Hex::encode(encoded_bytes),
1102 "5355495f4252494447455f4d455353414745070100000000000000000c0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00",
1103 );
1104 let keys = get_bridge_encoding_regression_test_keys();
1106 for key in keys {
1107 let pub_key = key.public.as_bytes();
1108 println!("pub_key: {:?}", Hex::encode(pub_key));
1109 println!(
1110 "sig: {:?}",
1111 Hex::encode(
1112 BridgeAuthoritySignInfo::new(&action, &key)
1113 .signature
1114 .as_bytes()
1115 )
1116 );
1117 }
1118 Ok(())
1119 }
1120
1121 fn get_bridge_encoding_regression_test_keys() -> Vec<BridgeAuthorityKeyPair> {
1122 vec![
1123 BridgeAuthorityKeyPair::from_bytes(
1124 &Hex::decode("e42c82337ce12d4a7ad6cd65876d91b2ab6594fd50cdab1737c91773ba7451db")
1125 .unwrap(),
1126 )
1127 .unwrap(),
1128 BridgeAuthorityKeyPair::from_bytes(
1129 &Hex::decode("1aacd610da3d0cc691a04b83b01c34c6c65cda0fe8d502df25ff4b3185c85687")
1130 .unwrap(),
1131 )
1132 .unwrap(),
1133 BridgeAuthorityKeyPair::from_bytes(
1134 &Hex::decode("53e7baf8378fbc62692e3056c2e10c6666ef8b5b3a53914830f47636d1678140")
1135 .unwrap(),
1136 )
1137 .unwrap(),
1138 BridgeAuthorityKeyPair::from_bytes(
1139 &Hex::decode("08b5350a091faabd5f25b6e290bfc3f505d43208775b9110dfed5ee6c7a653f0")
1140 .unwrap(),
1141 )
1142 .unwrap(),
1143 ]
1144 }
1145}