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::EvmContractUpgradeAction;
13use crate::types::LimitUpdateAction;
14use crate::types::SuiToEthBridgeAction;
15use crate::types::SuiToEthTokenTransfer;
16use anyhow::Result;
17use enum_dispatch::enum_dispatch;
18use ethers::types::Address as EthAddress;
19use sui_types::base_types::SUI_ADDRESS_LENGTH;
20
21pub const TOKEN_TRANSFER_MESSAGE_VERSION: u8 = 1;
22pub const COMMITTEE_BLOCKLIST_MESSAGE_VERSION: u8 = 1;
23pub const EMERGENCY_BUTTON_MESSAGE_VERSION: u8 = 1;
24pub const LIMIT_UPDATE_MESSAGE_VERSION: u8 = 1;
25pub const ASSET_PRICE_UPDATE_MESSAGE_VERSION: u8 = 1;
26pub const EVM_CONTRACT_UPGRADE_MESSAGE_VERSION: u8 = 1;
27pub const ADD_TOKENS_ON_SUI_MESSAGE_VERSION: u8 = 1;
28pub const ADD_TOKENS_ON_EVM_MESSAGE_VERSION: u8 = 1;
29
30pub const BRIDGE_MESSAGE_PREFIX: &[u8] = b"SUI_BRIDGE_MESSAGE";
31
32#[enum_dispatch]
39pub trait BridgeMessageEncoding {
40 fn as_bytes(&self) -> anyhow::Result<Vec<u8>>;
42 fn as_payload_bytes(&self) -> anyhow::Result<Vec<u8>>;
44}
45
46impl BridgeMessageEncoding for SuiToEthBridgeAction {
47 fn as_bytes(&self) -> Result<Vec<u8>> {
48 let mut bytes = Vec::new();
49 let e = &self.sui_bridge_event;
50 bytes.push(BridgeActionType::TokenTransfer as u8);
52 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION);
54 bytes.extend_from_slice(&e.nonce.to_be_bytes());
56 bytes.push(e.sui_chain_id as u8);
58
59 bytes.extend_from_slice(&self.as_payload_bytes()?);
61
62 Ok(bytes)
63 }
64
65 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
66 let mut bytes = Vec::new();
67 let e = &self.sui_bridge_event;
68
69 bytes.push(SUI_ADDRESS_LENGTH as u8);
71 bytes.extend_from_slice(&e.sui_address.to_vec());
73 bytes.push(e.eth_chain_id as u8);
75 bytes.push(EthAddress::len_bytes() as u8);
77 bytes.extend_from_slice(e.eth_address.as_bytes());
79
80 bytes.push(e.token_id);
82
83 bytes.extend_from_slice(&e.amount_sui_adjusted.to_be_bytes());
85
86 Ok(bytes)
87 }
88}
89
90impl BridgeMessageEncoding for SuiToEthTokenTransfer {
91 fn as_bytes(&self) -> Result<Vec<u8>> {
92 let mut bytes = Vec::new();
93 bytes.push(BridgeActionType::TokenTransfer as u8);
95 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION);
97 bytes.extend_from_slice(&self.nonce.to_be_bytes());
99 bytes.push(self.sui_chain_id as u8);
101
102 bytes.extend_from_slice(&self.as_payload_bytes()?);
104
105 Ok(bytes)
106 }
107
108 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
109 let mut bytes = Vec::new();
110
111 bytes.push(SUI_ADDRESS_LENGTH as u8);
113 bytes.extend_from_slice(&self.sui_address.to_vec());
115 bytes.push(self.eth_chain_id as u8);
117 bytes.push(EthAddress::len_bytes() as u8);
119 bytes.extend_from_slice(self.eth_address.as_bytes());
121
122 bytes.push(self.token_id);
124
125 bytes.extend_from_slice(&self.amount_adjusted.to_be_bytes());
127
128 Ok(bytes)
129 }
130}
131
132impl BridgeMessageEncoding for EthToSuiBridgeAction {
133 fn as_bytes(&self) -> Result<Vec<u8>> {
134 let mut bytes = Vec::new();
135 let e = &self.eth_bridge_event;
136 bytes.push(BridgeActionType::TokenTransfer as u8);
138 bytes.push(TOKEN_TRANSFER_MESSAGE_VERSION);
140 bytes.extend_from_slice(&e.nonce.to_be_bytes());
142 bytes.push(e.eth_chain_id as u8);
144
145 bytes.extend_from_slice(&self.as_payload_bytes()?);
147
148 Ok(bytes)
149 }
150
151 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
152 let mut bytes = Vec::new();
153 let e = &self.eth_bridge_event;
154
155 bytes.push(EthAddress::len_bytes() as u8);
157 bytes.extend_from_slice(e.eth_address.as_bytes());
159 bytes.push(e.sui_chain_id as u8);
161 bytes.push(SUI_ADDRESS_LENGTH as u8);
163 bytes.extend_from_slice(&e.sui_address.to_vec());
165
166 bytes.push(e.token_id);
168
169 bytes.extend_from_slice(&e.sui_adjusted_amount.to_be_bytes());
171
172 Ok(bytes)
173 }
174}
175
176impl BridgeMessageEncoding for BlocklistCommitteeAction {
177 fn as_bytes(&self) -> Result<Vec<u8>> {
178 let mut bytes = Vec::new();
179 bytes.push(BridgeActionType::UpdateCommitteeBlocklist as u8);
181 bytes.push(COMMITTEE_BLOCKLIST_MESSAGE_VERSION);
183 bytes.extend_from_slice(&self.nonce.to_be_bytes());
185 bytes.push(self.chain_id as u8);
187
188 bytes.extend_from_slice(&self.as_payload_bytes()?);
190
191 Ok(bytes)
192 }
193
194 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
195 let mut bytes = Vec::new();
196
197 bytes.push(self.blocklist_type as u8);
199 bytes.push(u8::try_from(self.members_to_update.len())?);
202
203 let members_bytes = self
206 .members_to_update
207 .iter()
208 .map(|m| m.to_eth_address().to_fixed_bytes().to_vec())
209 .collect::<Vec<_>>();
210 for members_bytes in members_bytes {
211 bytes.extend_from_slice(&members_bytes);
212 }
213
214 Ok(bytes)
215 }
216}
217
218impl BridgeMessageEncoding for EmergencyAction {
219 fn as_bytes(&self) -> Result<Vec<u8>> {
220 let mut bytes = Vec::new();
221 bytes.push(BridgeActionType::EmergencyButton as u8);
223 bytes.push(EMERGENCY_BUTTON_MESSAGE_VERSION);
225 bytes.extend_from_slice(&self.nonce.to_be_bytes());
227 bytes.push(self.chain_id as u8);
229
230 bytes.extend_from_slice(&self.as_payload_bytes()?);
232
233 Ok(bytes)
234 }
235
236 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
237 Ok(vec![self.action_type as u8])
238 }
239}
240
241impl BridgeMessageEncoding for LimitUpdateAction {
242 fn as_bytes(&self) -> Result<Vec<u8>> {
243 let mut bytes = Vec::new();
244 bytes.push(BridgeActionType::LimitUpdate as u8);
246 bytes.push(LIMIT_UPDATE_MESSAGE_VERSION);
248 bytes.extend_from_slice(&self.nonce.to_be_bytes());
250 bytes.push(self.chain_id as u8);
252
253 bytes.extend_from_slice(&self.as_payload_bytes()?);
255
256 Ok(bytes)
257 }
258
259 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
260 let mut bytes = Vec::new();
261 bytes.push(self.sending_chain_id as u8);
263 bytes.extend_from_slice(&self.new_usd_limit.to_be_bytes());
265 Ok(bytes)
266 }
267}
268
269impl BridgeMessageEncoding for AssetPriceUpdateAction {
270 fn as_bytes(&self) -> Result<Vec<u8>> {
271 let mut bytes = Vec::new();
272 bytes.push(BridgeActionType::AssetPriceUpdate as u8);
274 bytes.push(ASSET_PRICE_UPDATE_MESSAGE_VERSION);
276 bytes.extend_from_slice(&self.nonce.to_be_bytes());
278 bytes.push(self.chain_id as u8);
280
281 bytes.extend_from_slice(&self.as_payload_bytes()?);
283
284 Ok(bytes)
285 }
286
287 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
288 let mut bytes = Vec::new();
289 bytes.push(self.token_id);
291 bytes.extend_from_slice(&self.new_usd_price.to_be_bytes());
293 Ok(bytes)
294 }
295}
296
297impl BridgeMessageEncoding for EvmContractUpgradeAction {
298 fn as_bytes(&self) -> Result<Vec<u8>> {
299 let mut bytes = Vec::new();
300 bytes.push(BridgeActionType::EvmContractUpgrade as u8);
302 bytes.push(EVM_CONTRACT_UPGRADE_MESSAGE_VERSION);
304 bytes.extend_from_slice(&self.nonce.to_be_bytes());
306 bytes.push(self.chain_id as u8);
308
309 bytes.extend_from_slice(&self.as_payload_bytes()?);
311
312 Ok(bytes)
313 }
314
315 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
316 Ok(ethers::abi::encode(&[
317 ethers::abi::Token::Address(self.proxy_address),
318 ethers::abi::Token::Address(self.new_impl_address),
319 ethers::abi::Token::Bytes(self.call_data.clone()),
320 ]))
321 }
322}
323
324impl BridgeMessageEncoding for AddTokensOnSuiAction {
325 fn as_bytes(&self) -> Result<Vec<u8>> {
326 let mut bytes = Vec::new();
327 bytes.push(BridgeActionType::AddTokensOnSui as u8);
329 bytes.push(ADD_TOKENS_ON_SUI_MESSAGE_VERSION);
331 bytes.extend_from_slice(&self.nonce.to_be_bytes());
333 bytes.push(self.chain_id as u8);
335
336 bytes.extend_from_slice(&self.as_payload_bytes()?);
338
339 Ok(bytes)
340 }
341
342 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
343 let mut bytes = Vec::new();
344 bytes.push(self.native as u8);
346 bytes.extend_from_slice(&bcs::to_bytes(&self.token_ids)?);
348
349 bytes.extend_from_slice(&bcs::to_bytes(
351 &self
352 .token_type_names
353 .iter()
354 .map(|m| m.to_canonical_string(false))
355 .collect::<Vec<_>>(),
356 )?);
357
358 bytes.extend_from_slice(&bcs::to_bytes(&self.token_prices)?);
360
361 Ok(bytes)
362 }
363}
364
365impl BridgeMessageEncoding for AddTokensOnEvmAction {
366 fn as_bytes(&self) -> Result<Vec<u8>> {
367 let mut bytes = Vec::new();
368 bytes.push(BridgeActionType::AddTokensOnEvm as u8);
370 bytes.push(ADD_TOKENS_ON_EVM_MESSAGE_VERSION);
372 bytes.extend_from_slice(&self.nonce.to_be_bytes());
374 bytes.push(self.chain_id as u8);
376
377 bytes.extend_from_slice(&self.as_payload_bytes()?);
379
380 Ok(bytes)
381 }
382
383 fn as_payload_bytes(&self) -> Result<Vec<u8>> {
384 let mut bytes = Vec::new();
385 bytes.push(self.native as u8);
387 bytes.push(u8::try_from(self.token_ids.len())?);
389 for token_id in &self.token_ids {
390 bytes.push(*token_id);
391 }
392
393 bytes.push(u8::try_from(self.token_addresses.len())?);
395 for token_address in &self.token_addresses {
396 bytes.extend_from_slice(&token_address.to_fixed_bytes());
397 }
398
399 bytes.push(u8::try_from(self.token_sui_decimals.len())?);
401 for token_sui_decimal in &self.token_sui_decimals {
402 bytes.push(*token_sui_decimal);
403 }
404
405 bytes.push(u8::try_from(self.token_prices.len())?);
407 for token_price in &self.token_prices {
408 bytes.extend_from_slice(&token_price.to_be_bytes());
409 }
410 Ok(bytes)
411 }
412}
413
414impl BridgeAction {
415 pub fn to_bytes(&self) -> Result<Vec<u8>> {
417 let mut bytes = Vec::new();
418 bytes.extend_from_slice(BRIDGE_MESSAGE_PREFIX);
420 bytes.extend_from_slice(&self.as_bytes()?);
422 Ok(bytes)
423 }
424}
425
426#[cfg(test)]
427mod tests {
428 use crate::abi::EthToSuiTokenBridgeV1;
429 use crate::crypto::BridgeAuthorityKeyPair;
430 use crate::crypto::BridgeAuthorityPublicKeyBytes;
431 use crate::crypto::BridgeAuthoritySignInfo;
432 use crate::events::EmittedSuiToEthTokenBridgeV1;
433 use crate::types::BlocklistType;
434 use crate::types::EmergencyActionType;
435 use crate::types::USD_MULTIPLIER;
436 use ethers::abi::ParamType;
437 use ethers::types::{Address as EthAddress, TxHash};
438 use fastcrypto::encoding::Encoding;
439 use fastcrypto::encoding::Hex;
440 use fastcrypto::hash::HashFunction;
441 use fastcrypto::hash::Keccak256;
442 use fastcrypto::traits::ToFromBytes;
443 use prometheus::Registry;
444 use std::str::FromStr;
445 use sui_types::TypeTag;
446 use sui_types::base_types::{SuiAddress, TransactionDigest};
447 use sui_types::bridge::BridgeChainId;
448 use sui_types::bridge::TOKEN_ID_BTC;
449 use sui_types::bridge::TOKEN_ID_USDC;
450
451 use super::*;
452
453 #[test]
454 fn test_bridge_message_encoding() -> anyhow::Result<()> {
455 telemetry_subscribers::init_for_testing();
456 let registry = Registry::new();
457 mysten_metrics::init_metrics(®istry);
458 let nonce = 54321u64;
459 let sui_tx_digest = TransactionDigest::random();
460 let sui_chain_id = BridgeChainId::SuiTestnet;
461 let sui_tx_event_index = 1u16;
462 let eth_chain_id = BridgeChainId::EthSepolia;
463 let sui_address = SuiAddress::random_for_testing_only();
464 let eth_address = EthAddress::random();
465 let token_id = TOKEN_ID_USDC;
466 let amount_sui_adjusted = 1_000_000;
467
468 let sui_bridge_event = EmittedSuiToEthTokenBridgeV1 {
469 nonce,
470 sui_chain_id,
471 eth_chain_id,
472 sui_address,
473 eth_address,
474 token_id,
475 amount_sui_adjusted,
476 };
477
478 let encoded_bytes = BridgeAction::SuiToEthBridgeAction(SuiToEthBridgeAction {
479 sui_tx_digest,
480 sui_tx_event_index,
481 sui_bridge_event,
482 })
483 .to_bytes()?;
484
485 let prefix_bytes = BRIDGE_MESSAGE_PREFIX.to_vec(); let message_type = vec![BridgeActionType::TokenTransfer as u8]; let message_version = vec![TOKEN_TRANSFER_MESSAGE_VERSION]; 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.as_bytes().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();
502 combined_bytes.extend_from_slice(&prefix_bytes);
503 combined_bytes.extend_from_slice(&message_type);
504 combined_bytes.extend_from_slice(&message_version);
505 combined_bytes.extend_from_slice(&nonce_bytes);
506 combined_bytes.extend_from_slice(&source_chain_id_bytes);
507 combined_bytes.extend_from_slice(&sui_address_length_bytes);
508 combined_bytes.extend_from_slice(&sui_address_bytes);
509 combined_bytes.extend_from_slice(&dest_chain_id_bytes);
510 combined_bytes.extend_from_slice(ð_address_length_bytes);
511 combined_bytes.extend_from_slice(ð_address_bytes);
512 combined_bytes.extend_from_slice(&token_id_bytes);
513 combined_bytes.extend_from_slice(&token_amount_bytes);
514
515 assert_eq!(combined_bytes, encoded_bytes);
516
517 assert_eq!(
520 combined_bytes.len(),
521 18 + 1 + 1 + 8 + 1 + 1 + 32 + 1 + 20 + 1 + 1 + 8
522 );
523 Ok(())
524 }
525
526 #[test]
527 fn test_bridge_message_encoding_regression_emitted_sui_to_eth_token_bridge_v1()
528 -> anyhow::Result<()> {
529 telemetry_subscribers::init_for_testing();
530 let registry = Registry::new();
531 mysten_metrics::init_metrics(®istry);
532 let sui_tx_digest = TransactionDigest::random();
533 let sui_tx_event_index = 1u16;
534
535 let nonce = 10u64;
536 let sui_chain_id = BridgeChainId::SuiTestnet;
537 let eth_chain_id = BridgeChainId::EthSepolia;
538 let sui_address = SuiAddress::from_str(
539 "0x0000000000000000000000000000000000000000000000000000000000000064",
540 )
541 .unwrap();
542 let eth_address =
543 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
544 let token_id = TOKEN_ID_USDC;
545 let amount_sui_adjusted = 12345;
546
547 let sui_bridge_event = EmittedSuiToEthTokenBridgeV1 {
548 nonce,
549 sui_chain_id,
550 eth_chain_id,
551 sui_address,
552 eth_address,
553 token_id,
554 amount_sui_adjusted,
555 };
556 let encoded_bytes = BridgeAction::SuiToEthBridgeAction(SuiToEthBridgeAction {
557 sui_tx_digest,
558 sui_tx_event_index,
559 sui_bridge_event,
560 })
561 .to_bytes()?;
562 assert_eq!(
563 encoded_bytes,
564 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a012000000000000000000000000000000000000000000000000000000000000000640b1400000000000000000000000000000000000000c8030000000000003039").unwrap(),
565 );
566
567 let hash = Keccak256::digest(encoded_bytes).digest;
568 assert_eq!(
569 hash.to_vec(),
570 Hex::decode("6ab34c52b6264cbc12fe8c3874f9b08f8481d2e81530d136386646dbe2f8baf4")
571 .unwrap(),
572 );
573 Ok(())
574 }
575
576 #[test]
577 fn test_bridge_message_encoding_blocklist_update_v1() {
578 telemetry_subscribers::init_for_testing();
579 let registry = Registry::new();
580 mysten_metrics::init_metrics(®istry);
581
582 let pub_key_bytes = BridgeAuthorityPublicKeyBytes::from_bytes(
583 &Hex::decode("02321ede33d2c2d7a8a152f275a1484edef2098f034121a602cb7d767d38680aa4")
584 .unwrap(),
585 )
586 .unwrap();
587 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
588 nonce: 129,
589 chain_id: BridgeChainId::SuiCustom,
590 blocklist_type: BlocklistType::Blocklist,
591 members_to_update: vec![pub_key_bytes.clone()],
592 });
593 let bytes = blocklist_action.to_bytes().unwrap();
594 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000008102000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
607
608 let pub_key_bytes_2 = BridgeAuthorityPublicKeyBytes::from_bytes(
609 &Hex::decode("027f1178ff417fc9f5b8290bd8876f0a157a505a6c52db100a8492203ddd1d4279")
610 .unwrap(),
611 )
612 .unwrap();
613 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
615 nonce: 68,
616 chain_id: BridgeChainId::SuiCustom,
617 blocklist_type: BlocklistType::Unblocklist,
618 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
619 });
620 let bytes = blocklist_action.to_bytes().unwrap();
621 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000004402010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
635
636 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
637 nonce: 49,
638 chain_id: BridgeChainId::EthCustom,
639 blocklist_type: BlocklistType::Blocklist,
640 members_to_update: vec![pub_key_bytes.clone()],
641 });
642 let bytes = blocklist_action.to_bytes().unwrap();
643 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d455353414745010100000000000000310c000168b43fd906c0b8f024a18c56e06744f7c6157c65").unwrap());
656
657 let blocklist_action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
658 nonce: 94,
659 chain_id: BridgeChainId::EthSepolia,
660 blocklist_type: BlocklistType::Unblocklist,
661 members_to_update: vec![pub_key_bytes.clone(), pub_key_bytes_2.clone()],
662 });
663 let bytes = blocklist_action.to_bytes().unwrap();
664 assert_eq!(bytes, Hex::decode("5355495f4252494447455f4d4553534147450101000000000000005e0b010268b43fd906c0b8f024a18c56e06744f7c6157c65acaef39832cb995c4e049437a3e2ec6a7bad1ab5").unwrap());
678 }
679
680 #[test]
681 fn test_bridge_message_encoding_emergency_action() {
682 let action = BridgeAction::EmergencyAction(EmergencyAction {
683 nonce: 55,
684 chain_id: BridgeChainId::SuiCustom,
685 action_type: EmergencyActionType::Pause,
686 });
687 let bytes = action.to_bytes().unwrap();
688 assert_eq!(
697 bytes,
698 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000370200").unwrap()
699 );
700
701 let action = BridgeAction::EmergencyAction(EmergencyAction {
702 nonce: 56,
703 chain_id: BridgeChainId::EthSepolia,
704 action_type: EmergencyActionType::Unpause,
705 });
706 let bytes = action.to_bytes().unwrap();
707 assert_eq!(
716 bytes,
717 Hex::decode("5355495f4252494447455f4d455353414745020100000000000000380b01").unwrap()
718 );
719 }
720
721 #[test]
722 fn test_bridge_message_encoding_limit_update_action() {
723 let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
724 nonce: 15,
725 chain_id: BridgeChainId::SuiCustom,
726 sending_chain_id: BridgeChainId::EthCustom,
727 new_usd_limit: 1_000_000 * USD_MULTIPLIER, });
729 let bytes = action.to_bytes().unwrap();
730 assert_eq!(
740 bytes,
741 Hex::decode(
742 "5355495f4252494447455f4d4553534147450301000000000000000f020c00000002540be400"
743 )
744 .unwrap()
745 );
746 }
747
748 #[test]
749 fn test_bridge_message_encoding_asset_price_update_action() {
750 let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
751 nonce: 266,
752 chain_id: BridgeChainId::SuiCustom,
753 token_id: TOKEN_ID_BTC,
754 new_usd_price: 100_000 * USD_MULTIPLIER, });
756 let bytes = action.to_bytes().unwrap();
757 assert_eq!(
767 bytes,
768 Hex::decode(
769 "5355495f4252494447455f4d4553534147450401000000000000010a0201000000003b9aca00"
770 )
771 .unwrap()
772 );
773 }
774
775 #[test]
776 fn test_bridge_message_encoding_evm_contract_upgrade_action() {
777 let function_signature = "initializeV2()";
779 let selector = &Keccak256::digest(function_signature).digest[0..4];
780 let call_data = selector.to_vec();
781 assert_eq!(Hex::encode(call_data.clone()), "5cd8a76b");
782
783 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
784 nonce: 123,
785 chain_id: BridgeChainId::EthCustom,
786 proxy_address: EthAddress::repeat_byte(6),
787 new_impl_address: EthAddress::repeat_byte(9),
788 call_data,
789 });
790 assert_eq!(
804 Hex::encode(action.to_bytes().unwrap().clone()),
805 "5355495f4252494447455f4d4553534147450501000000000000007b0c00000000000000000000000006060606060606060606060606060606060606060000000000000000000000000909090909090909090909090909090909090909000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000045cd8a76b00000000000000000000000000000000000000000000000000000000"
806 );
807
808 let function_signature = "newMockFunction(bool)";
810 let selector = &Keccak256::digest(function_signature).digest[0..4];
811 let mut call_data = selector.to_vec();
812 call_data.extend(ethers::abi::encode(&[ethers::abi::Token::Bool(true)]));
813 assert_eq!(
814 Hex::encode(call_data.clone()),
815 "417795ef0000000000000000000000000000000000000000000000000000000000000001"
816 );
817 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
818 nonce: 123,
819 chain_id: BridgeChainId::EthCustom,
820 proxy_address: EthAddress::repeat_byte(6),
821 new_impl_address: EthAddress::repeat_byte(9),
822 call_data,
823 });
824 assert_eq!(
839 Hex::encode(action.to_bytes().unwrap().clone()),
840 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000024417795ef000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000"
841 );
842
843 let function_signature = "newMockFunction(bool,uint8)";
845 let selector = &Keccak256::digest(function_signature).digest[0..4];
846 let mut call_data = selector.to_vec();
847 call_data.extend(ethers::abi::encode(&[
848 ethers::abi::Token::Bool(true),
849 ethers::abi::Token::Uint(42u8.into()),
850 ]));
851 assert_eq!(
852 Hex::encode(call_data.clone()),
853 "be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a"
854 );
855 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
856 nonce: 123,
857 chain_id: BridgeChainId::EthCustom,
858 proxy_address: EthAddress::repeat_byte(6),
859 new_impl_address: EthAddress::repeat_byte(9),
860 call_data,
861 });
862 assert_eq!(
878 Hex::encode(action.to_bytes().unwrap().clone()),
879 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000044be8fc25d0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000"
880 );
881
882 let action = BridgeAction::EvmContractUpgradeAction(EvmContractUpgradeAction {
884 nonce: 123,
885 chain_id: BridgeChainId::EthCustom,
886 proxy_address: EthAddress::repeat_byte(6),
887 new_impl_address: EthAddress::repeat_byte(9),
888 call_data: vec![],
889 });
890 let data = action.to_bytes().unwrap();
903 assert_eq!(
904 Hex::encode(data.clone()),
905 "5355495f4252494447455f4d4553534147450501000000000000007b0c0000000000000000000000000606060606060606060606060606060606060606000000000000000000000000090909090909090909090909090909090909090900000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000"
906 );
907 let types = vec![ParamType::Address, ParamType::Address, ParamType::Bytes];
908 ethers::abi::decode(&types, &data[29..]).unwrap();
910 }
911
912 #[test]
913 fn test_bridge_message_encoding_regression_eth_to_sui_token_bridge_v1() -> anyhow::Result<()> {
914 telemetry_subscribers::init_for_testing();
915 let registry = Registry::new();
916 mysten_metrics::init_metrics(®istry);
917 let eth_tx_hash = TxHash::random();
918 let eth_event_index = 1u16;
919
920 let nonce = 10u64;
921 let sui_chain_id = BridgeChainId::SuiTestnet;
922 let eth_chain_id = BridgeChainId::EthSepolia;
923 let sui_address = SuiAddress::from_str(
924 "0x0000000000000000000000000000000000000000000000000000000000000064",
925 )
926 .unwrap();
927 let eth_address =
928 EthAddress::from_str("0x00000000000000000000000000000000000000c8").unwrap();
929 let token_id = TOKEN_ID_USDC;
930 let sui_adjusted_amount = 12345;
931
932 let eth_bridge_event = EthToSuiTokenBridgeV1 {
933 nonce,
934 sui_chain_id,
935 eth_chain_id,
936 sui_address,
937 eth_address,
938 token_id,
939 sui_adjusted_amount,
940 };
941 let encoded_bytes = BridgeAction::EthToSuiBridgeAction(EthToSuiBridgeAction {
942 eth_tx_hash,
943 eth_event_index,
944 eth_bridge_event,
945 })
946 .to_bytes()?;
947
948 assert_eq!(
949 encoded_bytes,
950 Hex::decode("5355495f4252494447455f4d4553534147450001000000000000000a0b1400000000000000000000000000000000000000c801200000000000000000000000000000000000000000000000000000000000000064030000000000003039").unwrap(),
951 );
952
953 let hash = Keccak256::digest(encoded_bytes).digest;
954 assert_eq!(
955 hash.to_vec(),
956 Hex::decode("b352508c301a37bb1b68a75dd0fc42b6f692b2650818631c8f8a4d4d3e5bef46")
957 .unwrap(),
958 );
959 Ok(())
960 }
961
962 #[test]
963 fn test_bridge_message_encoding_regression_add_coins_on_sui() -> anyhow::Result<()> {
964 telemetry_subscribers::init_for_testing();
965
966 let action = BridgeAction::AddTokensOnSuiAction(AddTokensOnSuiAction {
967 nonce: 0,
968 chain_id: BridgeChainId::SuiCustom,
969 native: false,
970 token_ids: vec![1, 2, 3, 4],
971 token_type_names: vec![
972 TypeTag::from_str("0x9b5e13bcd0cb23ff25c07698e89d48056c745338d8c9dbd033a4172b87027073::btc::BTC").unwrap(),
973 TypeTag::from_str("0x7970d71c03573f540a7157f0d3970e117effa6ae16cefd50b45c749670b24e6a::eth::ETH").unwrap(),
974 TypeTag::from_str("0x500e429a24478405d5130222b20f8570a746b6bc22423f14b4d4e6a8ea580736::usdc::USDC").unwrap(),
975 TypeTag::from_str("0x46bfe51da1bd9511919a92eb1154149b36c0f4212121808e13e3e5857d607a9c::usdt::USDT").unwrap(),
976 ],
977 token_prices: vec![
978 500_000_000u64,
979 30_000_000u64,
980 1_000u64,
981 1_000u64,
982 ]
983 });
984 let encoded_bytes = action.to_bytes().unwrap();
985
986 assert_eq!(
987 Hex::encode(encoded_bytes),
988 "5355495f4252494447455f4d4553534147450601000000000000000002000401020304044a396235653133626364306362323366663235633037363938653839643438303536633734353333386438633964626430333361343137326238373032373037333a3a6274633a3a4254434a373937306437316330333537336635343061373135376630643339373065313137656666613661653136636566643530623435633734393637306232346536613a3a6574683a3a4554484c353030653432396132343437383430356435313330323232623230663835373061373436623662633232343233663134623464346536613865613538303733363a3a757364633a3a555344434c343662666535316461316264393531313931396139326562313135343134396233366330663432313231323138303865313365336535383537643630376139633a3a757364743a3a55534454040065cd1d0000000080c3c90100000000e803000000000000e803000000000000",
989 );
990 Ok(())
991 }
992
993 #[test]
994 fn test_bridge_message_encoding_regression_add_coins_on_evm() -> anyhow::Result<()> {
995 let action = BridgeAction::AddTokensOnEvmAction(crate::types::AddTokensOnEvmAction {
996 nonce: 0,
997 chain_id: BridgeChainId::EthCustom,
998 native: true,
999 token_ids: vec![99, 100, 101],
1000 token_addresses: vec![
1001 EthAddress::from_str("0x6B175474E89094C44Da98b954EedeAC495271d0F").unwrap(),
1002 EthAddress::from_str("0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84").unwrap(),
1003 EthAddress::from_str("0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72").unwrap(),
1004 ],
1005 token_sui_decimals: vec![5, 6, 7],
1006 token_prices: vec![1_000_000_000, 2_000_000_000, 3_000_000_000],
1007 });
1008 let encoded_bytes = action.to_bytes().unwrap();
1009
1010 assert_eq!(
1011 Hex::encode(encoded_bytes),
1012 "5355495f4252494447455f4d455353414745070100000000000000000c0103636465036b175474e89094c44da98b954eedeac495271d0fae7ab96520de3a18e5e111b5eaab095312d7fe84c18360217d8f7ab5e7c516566761ea12ce7f9d720305060703000000003b9aca00000000007735940000000000b2d05e00",
1013 );
1014 let keys = get_bridge_encoding_regression_test_keys();
1016 for key in keys {
1017 let pub_key = key.public.as_bytes();
1018 println!("pub_key: {:?}", Hex::encode(pub_key));
1019 println!(
1020 "sig: {:?}",
1021 Hex::encode(
1022 BridgeAuthoritySignInfo::new(&action, &key)
1023 .signature
1024 .as_bytes()
1025 )
1026 );
1027 }
1028 Ok(())
1029 }
1030
1031 fn get_bridge_encoding_regression_test_keys() -> Vec<BridgeAuthorityKeyPair> {
1032 vec![
1033 BridgeAuthorityKeyPair::from_bytes(
1034 &Hex::decode("e42c82337ce12d4a7ad6cd65876d91b2ab6594fd50cdab1737c91773ba7451db")
1035 .unwrap(),
1036 )
1037 .unwrap(),
1038 BridgeAuthorityKeyPair::from_bytes(
1039 &Hex::decode("1aacd610da3d0cc691a04b83b01c34c6c65cda0fe8d502df25ff4b3185c85687")
1040 .unwrap(),
1041 )
1042 .unwrap(),
1043 BridgeAuthorityKeyPair::from_bytes(
1044 &Hex::decode("53e7baf8378fbc62692e3056c2e10c6666ef8b5b3a53914830f47636d1678140")
1045 .unwrap(),
1046 )
1047 .unwrap(),
1048 BridgeAuthorityKeyPair::from_bytes(
1049 &Hex::decode("08b5350a091faabd5f25b6e290bfc3f505d43208775b9110dfed5ee6c7a653f0")
1050 .unwrap(),
1051 )
1052 .unwrap(),
1053 ]
1054 }
1055}