1use fastcrypto::traits::ToFromBytes;
5use move_core_types::ident_str;
6use std::{collections::HashMap, str::FromStr};
7use sui_types::bridge::{
8 BRIDGE_CREATE_ADD_TOKEN_ON_SUI_MESSAGE_FUNCTION_NAME,
9 BRIDGE_EXECUTE_SYSTEM_MESSAGE_FUNCTION_NAME, BRIDGE_MESSAGE_MODULE_NAME, BRIDGE_MODULE_NAME,
10};
11use sui_types::transaction::CallArg;
12use sui_types::{BRIDGE_PACKAGE_ID, Identifier};
13use sui_types::{
14 TypeTag,
15 base_types::{ObjectRef, SuiAddress},
16 programmable_transaction_builder::ProgrammableTransactionBuilder,
17 transaction::{ObjectArg, TransactionData},
18};
19
20use crate::{
21 error::{BridgeError, BridgeResult},
22 types::{BridgeAction, VerifiedCertifiedBridgeAction},
23};
24
25pub fn build_sui_transaction(
26 client_address: SuiAddress,
27 gas_object_ref: &ObjectRef,
28 action: VerifiedCertifiedBridgeAction,
29 bridge_object_arg: ObjectArg,
30 sui_token_type_tags: &HashMap<u8, TypeTag>,
31 rgp: u64,
32) -> BridgeResult<TransactionData> {
33 match action.data() {
35 BridgeAction::EthToSuiBridgeAction(_) => build_token_bridge_approve_transaction(
36 client_address,
37 gas_object_ref,
38 action,
39 true,
40 bridge_object_arg,
41 sui_token_type_tags,
42 rgp,
43 ),
44 BridgeAction::SuiToEthBridgeAction(_) => build_token_bridge_approve_transaction(
45 client_address,
46 gas_object_ref,
47 action,
48 false,
49 bridge_object_arg,
50 sui_token_type_tags,
51 rgp,
52 ),
53 BridgeAction::SuiToEthTokenTransfer(_) => build_token_bridge_approve_transaction(
54 client_address,
55 gas_object_ref,
56 action,
57 false,
58 bridge_object_arg,
59 sui_token_type_tags,
60 rgp,
61 ),
62 BridgeAction::BlocklistCommitteeAction(_) => build_committee_blocklist_approve_transaction(
63 client_address,
64 gas_object_ref,
65 action,
66 bridge_object_arg,
67 rgp,
68 ),
69 BridgeAction::EmergencyAction(_) => build_emergency_op_approve_transaction(
70 client_address,
71 gas_object_ref,
72 action,
73 bridge_object_arg,
74 rgp,
75 ),
76 BridgeAction::LimitUpdateAction(_) => build_limit_update_approve_transaction(
77 client_address,
78 gas_object_ref,
79 action,
80 bridge_object_arg,
81 rgp,
82 ),
83 BridgeAction::AssetPriceUpdateAction(_) => build_asset_price_update_approve_transaction(
84 client_address,
85 gas_object_ref,
86 action,
87 bridge_object_arg,
88 rgp,
89 ),
90 BridgeAction::EvmContractUpgradeAction(_) => {
91 unreachable!()
93 }
94 BridgeAction::AddTokensOnSuiAction(_) => build_add_tokens_on_sui_transaction(
95 client_address,
96 gas_object_ref,
97 action,
98 bridge_object_arg,
99 rgp,
100 ),
101 BridgeAction::AddTokensOnEvmAction(_) => {
102 unreachable!()
104 }
105 }
106}
107
108fn build_token_bridge_approve_transaction(
109 client_address: SuiAddress,
110 gas_object_ref: &ObjectRef,
111 action: VerifiedCertifiedBridgeAction,
112 claim: bool,
113 bridge_object_arg: ObjectArg,
114 sui_token_type_tags: &HashMap<u8, TypeTag>,
115 rgp: u64,
116) -> BridgeResult<TransactionData> {
117 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
118 let mut builder = ProgrammableTransactionBuilder::new();
119
120 let (source_chain, seq_num, sender, target_chain, target, token_type, amount) =
121 match bridge_action {
122 BridgeAction::SuiToEthBridgeAction(a) => {
123 let bridge_event = a.sui_bridge_event;
124 (
125 bridge_event.sui_chain_id,
126 bridge_event.nonce,
127 bridge_event.sui_address.to_vec(),
128 bridge_event.eth_chain_id,
129 bridge_event.eth_address.to_fixed_bytes().to_vec(),
130 bridge_event.token_id,
131 bridge_event.amount_sui_adjusted,
132 )
133 }
134 BridgeAction::SuiToEthTokenTransfer(a) => (
135 a.sui_chain_id,
136 a.nonce,
137 a.sui_address.to_vec(),
138 a.eth_chain_id,
139 a.eth_address.to_fixed_bytes().to_vec(),
140 a.token_id,
141 a.amount_adjusted,
142 ),
143 BridgeAction::EthToSuiBridgeAction(a) => {
144 let bridge_event = a.eth_bridge_event;
145 (
146 bridge_event.eth_chain_id,
147 bridge_event.nonce,
148 bridge_event.eth_address.to_fixed_bytes().to_vec(),
149 bridge_event.sui_chain_id,
150 bridge_event.sui_address.to_vec(),
151 bridge_event.token_id,
152 bridge_event.sui_adjusted_amount,
153 )
154 }
155 _ => unreachable!(),
156 };
157
158 let source_chain = builder.pure(source_chain as u8).unwrap();
159 let seq_num = builder.pure(seq_num).unwrap();
160 let sender = builder.pure(sender.clone()).map_err(|e| {
161 BridgeError::BridgeSerializationError(format!(
162 "Failed to serialize sender: {:?}. Err: {:?}",
163 sender, e
164 ))
165 })?;
166 let target_chain = builder.pure(target_chain as u8).unwrap();
167 let target = builder.pure(target.clone()).map_err(|e| {
168 BridgeError::BridgeSerializationError(format!(
169 "Failed to serialize target: {:?}. Err: {:?}",
170 target, e
171 ))
172 })?;
173 let arg_token_type = builder.pure(token_type).unwrap();
174 let amount = builder.pure(amount).unwrap();
175
176 let arg_msg = builder.programmable_move_call(
177 BRIDGE_PACKAGE_ID,
178 ident_str!("message").to_owned(),
179 ident_str!("create_token_bridge_message").to_owned(),
180 vec![],
181 vec![
182 source_chain,
183 seq_num,
184 sender,
185 target_chain,
186 target,
187 arg_token_type,
188 amount,
189 ],
190 );
191
192 let arg_bridge = builder.obj(bridge_object_arg).unwrap();
194 let arg_clock = builder.input(CallArg::CLOCK_IMM).unwrap();
195
196 let mut sig_bytes = vec![];
197 for (_, sig) in sigs.signatures {
198 sig_bytes.push(sig.as_bytes().to_vec());
199 }
200 let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| {
201 BridgeError::BridgeSerializationError(format!(
202 "Failed to serialize signatures: {:?}. Err: {:?}",
203 sig_bytes, e
204 ))
205 })?;
206
207 builder.programmable_move_call(
208 BRIDGE_PACKAGE_ID,
209 sui_types::bridge::BRIDGE_MODULE_NAME.to_owned(),
210 ident_str!("approve_token_transfer").to_owned(),
211 vec![],
212 vec![arg_bridge, arg_msg, arg_signatures],
213 );
214
215 if claim {
216 builder.programmable_move_call(
217 BRIDGE_PACKAGE_ID,
218 sui_types::bridge::BRIDGE_MODULE_NAME.to_owned(),
219 ident_str!("claim_and_transfer_token").to_owned(),
220 vec![
221 sui_token_type_tags
222 .get(&token_type)
223 .ok_or(BridgeError::UnknownTokenId(token_type))?
224 .clone(),
225 ],
226 vec![arg_bridge, arg_clock, source_chain, seq_num],
227 );
228 }
229
230 let pt = builder.finish();
231
232 Ok(TransactionData::new_programmable(
233 client_address,
234 vec![*gas_object_ref],
235 pt,
236 100_000_000,
237 rgp,
238 ))
239}
240
241fn build_emergency_op_approve_transaction(
242 client_address: SuiAddress,
243 gas_object_ref: &ObjectRef,
244 action: VerifiedCertifiedBridgeAction,
245 bridge_object_arg: ObjectArg,
246 rgp: u64,
247) -> BridgeResult<TransactionData> {
248 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
249
250 let mut builder = ProgrammableTransactionBuilder::new();
251
252 let (source_chain, seq_num, action_type) = match bridge_action {
253 BridgeAction::EmergencyAction(a) => (a.chain_id, a.nonce, a.action_type),
254 _ => unreachable!(),
255 };
256
257 let source_chain = builder.pure(source_chain as u8).unwrap();
259 let seq_num = builder.pure(seq_num).unwrap();
260 let action_type = builder.pure(action_type as u8).unwrap();
261 let arg_bridge = builder.obj(bridge_object_arg).unwrap();
262
263 let arg_msg = builder.programmable_move_call(
264 BRIDGE_PACKAGE_ID,
265 ident_str!("message").to_owned(),
266 ident_str!("create_emergency_op_message").to_owned(),
267 vec![],
268 vec![source_chain, seq_num, action_type],
269 );
270
271 let mut sig_bytes = vec![];
272 for (_, sig) in sigs.signatures {
273 sig_bytes.push(sig.as_bytes().to_vec());
274 }
275 let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| {
276 BridgeError::BridgeSerializationError(format!(
277 "Failed to serialize signatures: {:?}. Err: {:?}",
278 sig_bytes, e
279 ))
280 })?;
281
282 builder.programmable_move_call(
283 BRIDGE_PACKAGE_ID,
284 ident_str!("bridge").to_owned(),
285 ident_str!("execute_system_message").to_owned(),
286 vec![],
287 vec![arg_bridge, arg_msg, arg_signatures],
288 );
289
290 let pt = builder.finish();
291
292 Ok(TransactionData::new_programmable(
293 client_address,
294 vec![*gas_object_ref],
295 pt,
296 100_000_000,
297 rgp,
298 ))
299}
300
301fn build_committee_blocklist_approve_transaction(
302 client_address: SuiAddress,
303 gas_object_ref: &ObjectRef,
304 action: VerifiedCertifiedBridgeAction,
305 bridge_object_arg: ObjectArg,
306 rgp: u64,
307) -> BridgeResult<TransactionData> {
308 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
309
310 let mut builder = ProgrammableTransactionBuilder::new();
311
312 let (source_chain, seq_num, blocklist_type, members_to_update) = match bridge_action {
313 BridgeAction::BlocklistCommitteeAction(a) => {
314 (a.chain_id, a.nonce, a.blocklist_type, a.members_to_update)
315 }
316 _ => unreachable!(),
317 };
318
319 let source_chain = builder.pure(source_chain as u8).unwrap();
321 let seq_num = builder.pure(seq_num).unwrap();
322 let blocklist_type = builder.pure(blocklist_type as u8).unwrap();
323 let members_to_update = members_to_update
324 .into_iter()
325 .map(|m| m.to_eth_address().as_bytes().to_vec())
326 .collect::<Vec<_>>();
327 let members_to_update = builder.pure(members_to_update).unwrap();
328 let arg_bridge = builder.obj(bridge_object_arg).unwrap();
329
330 let arg_msg = builder.programmable_move_call(
331 BRIDGE_PACKAGE_ID,
332 ident_str!("message").to_owned(),
333 ident_str!("create_blocklist_message").to_owned(),
334 vec![],
335 vec![source_chain, seq_num, blocklist_type, members_to_update],
336 );
337
338 let mut sig_bytes = vec![];
339 for (_, sig) in sigs.signatures {
340 sig_bytes.push(sig.as_bytes().to_vec());
341 }
342 let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| {
343 BridgeError::BridgeSerializationError(format!(
344 "Failed to serialize signatures: {:?}. Err: {:?}",
345 sig_bytes, e
346 ))
347 })?;
348
349 builder.programmable_move_call(
350 BRIDGE_PACKAGE_ID,
351 ident_str!("bridge").to_owned(),
352 ident_str!("execute_system_message").to_owned(),
353 vec![],
354 vec![arg_bridge, arg_msg, arg_signatures],
355 );
356
357 let pt = builder.finish();
358
359 Ok(TransactionData::new_programmable(
360 client_address,
361 vec![*gas_object_ref],
362 pt,
363 100_000_000,
364 rgp,
365 ))
366}
367
368fn build_limit_update_approve_transaction(
369 client_address: SuiAddress,
370 gas_object_ref: &ObjectRef,
371 action: VerifiedCertifiedBridgeAction,
372 bridge_object_arg: ObjectArg,
373 rgp: u64,
374) -> BridgeResult<TransactionData> {
375 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
376
377 let mut builder = ProgrammableTransactionBuilder::new();
378
379 let (receiving_chain_id, seq_num, sending_chain_id, new_usd_limit) = match bridge_action {
380 BridgeAction::LimitUpdateAction(a) => {
381 (a.chain_id, a.nonce, a.sending_chain_id, a.new_usd_limit)
382 }
383 _ => unreachable!(),
384 };
385
386 let receiving_chain_id = builder.pure(receiving_chain_id as u8).unwrap();
388 let seq_num = builder.pure(seq_num).unwrap();
389 let sending_chain_id = builder.pure(sending_chain_id as u8).unwrap();
390 let new_usd_limit = builder.pure(new_usd_limit).unwrap();
391 let arg_bridge = builder.obj(bridge_object_arg).unwrap();
392
393 let arg_msg = builder.programmable_move_call(
394 BRIDGE_PACKAGE_ID,
395 ident_str!("message").to_owned(),
396 ident_str!("create_update_bridge_limit_message").to_owned(),
397 vec![],
398 vec![receiving_chain_id, seq_num, sending_chain_id, new_usd_limit],
399 );
400
401 let mut sig_bytes = vec![];
402 for (_, sig) in sigs.signatures {
403 sig_bytes.push(sig.as_bytes().to_vec());
404 }
405 let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| {
406 BridgeError::BridgeSerializationError(format!(
407 "Failed to serialize signatures: {:?}. Err: {:?}",
408 sig_bytes, e
409 ))
410 })?;
411
412 builder.programmable_move_call(
413 BRIDGE_PACKAGE_ID,
414 ident_str!("bridge").to_owned(),
415 ident_str!("execute_system_message").to_owned(),
416 vec![],
417 vec![arg_bridge, arg_msg, arg_signatures],
418 );
419
420 let pt = builder.finish();
421
422 Ok(TransactionData::new_programmable(
423 client_address,
424 vec![*gas_object_ref],
425 pt,
426 100_000_000,
427 rgp,
428 ))
429}
430
431fn build_asset_price_update_approve_transaction(
432 client_address: SuiAddress,
433 gas_object_ref: &ObjectRef,
434 action: VerifiedCertifiedBridgeAction,
435 bridge_object_arg: ObjectArg,
436 rgp: u64,
437) -> BridgeResult<TransactionData> {
438 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
439
440 let mut builder = ProgrammableTransactionBuilder::new();
441
442 let (source_chain, seq_num, token_id, new_usd_price) = match bridge_action {
443 BridgeAction::AssetPriceUpdateAction(a) => {
444 (a.chain_id, a.nonce, a.token_id, a.new_usd_price)
445 }
446 _ => unreachable!(),
447 };
448
449 let source_chain = builder.pure(source_chain as u8).unwrap();
451 let token_id = builder.pure(token_id).unwrap();
452 let seq_num = builder.pure(seq_num).unwrap();
453 let new_price = builder.pure(new_usd_price).unwrap();
454 let arg_bridge = builder.obj(bridge_object_arg).unwrap();
455
456 let arg_msg = builder.programmable_move_call(
457 BRIDGE_PACKAGE_ID,
458 ident_str!("message").to_owned(),
459 ident_str!("create_update_asset_price_message").to_owned(),
460 vec![],
461 vec![token_id, source_chain, seq_num, new_price],
462 );
463
464 let mut sig_bytes = vec![];
465 for (_, sig) in sigs.signatures {
466 sig_bytes.push(sig.as_bytes().to_vec());
467 }
468 let arg_signatures = builder.pure(sig_bytes.clone()).map_err(|e| {
469 BridgeError::BridgeSerializationError(format!(
470 "Failed to serialize signatures: {:?}. Err: {:?}",
471 sig_bytes, e
472 ))
473 })?;
474
475 builder.programmable_move_call(
476 BRIDGE_PACKAGE_ID,
477 ident_str!("bridge").to_owned(),
478 ident_str!("execute_system_message").to_owned(),
479 vec![],
480 vec![arg_bridge, arg_msg, arg_signatures],
481 );
482
483 let pt = builder.finish();
484
485 Ok(TransactionData::new_programmable(
486 client_address,
487 vec![*gas_object_ref],
488 pt,
489 100_000_000,
490 rgp,
491 ))
492}
493
494pub fn build_add_tokens_on_sui_transaction(
495 client_address: SuiAddress,
496 gas_object_ref: &ObjectRef,
497 action: VerifiedCertifiedBridgeAction,
498 bridge_object_arg: ObjectArg,
499 rgp: u64,
500) -> BridgeResult<TransactionData> {
501 let (bridge_action, sigs) = action.into_inner().into_data_and_sig();
502
503 let mut builder = ProgrammableTransactionBuilder::new();
504
505 let (source_chain, seq_num, native, token_ids, token_type_names, token_prices) =
506 match bridge_action {
507 BridgeAction::AddTokensOnSuiAction(a) => (
508 a.chain_id,
509 a.nonce,
510 a.native,
511 a.token_ids,
512 a.token_type_names,
513 a.token_prices,
514 ),
515 _ => unreachable!(),
516 };
517 let token_type_names = token_type_names
518 .iter()
519 .map(|type_name| type_name.to_canonical_string(false))
520 .collect::<Vec<_>>();
521 let source_chain = builder.pure(source_chain as u8).unwrap();
522 let seq_num = builder.pure(seq_num).unwrap();
523 let native_token = builder.pure(native).unwrap();
524 let token_ids = builder.pure(token_ids).unwrap();
525 let token_type_names = builder.pure(token_type_names).unwrap();
526 let token_prices = builder.pure(token_prices).unwrap();
527
528 let message_arg = builder.programmable_move_call(
529 BRIDGE_PACKAGE_ID,
530 BRIDGE_MESSAGE_MODULE_NAME.into(),
531 BRIDGE_CREATE_ADD_TOKEN_ON_SUI_MESSAGE_FUNCTION_NAME.into(),
532 vec![],
533 vec![
534 source_chain,
535 seq_num,
536 native_token,
537 token_ids,
538 token_type_names,
539 token_prices,
540 ],
541 );
542
543 let bridge_arg = builder.obj(bridge_object_arg).unwrap();
544
545 let mut sig_bytes = vec![];
546 for (_, sig) in sigs.signatures {
547 sig_bytes.push(sig.as_bytes().to_vec());
548 }
549 let sigs_arg = builder.pure(sig_bytes.clone()).unwrap();
550
551 builder.programmable_move_call(
552 BRIDGE_PACKAGE_ID,
553 BRIDGE_MODULE_NAME.into(),
554 BRIDGE_EXECUTE_SYSTEM_MESSAGE_FUNCTION_NAME.into(),
555 vec![],
556 vec![bridge_arg, message_arg, sigs_arg],
557 );
558
559 let pt = builder.finish();
560
561 Ok(TransactionData::new_programmable(
562 client_address,
563 vec![*gas_object_ref],
564 pt,
565 100_000_000,
566 rgp,
567 ))
568}
569
570pub fn build_committee_register_transaction(
571 validator_address: SuiAddress,
572 gas_object_ref: &ObjectRef,
573 bridge_object_arg: ObjectArg,
574 bridge_authority_pub_key_bytes: Vec<u8>,
575 bridge_url: &str,
576 ref_gas_price: u64,
577 gas_budget: u64,
578) -> BridgeResult<TransactionData> {
579 let mut builder = ProgrammableTransactionBuilder::new();
580 let system_state = builder.obj(ObjectArg::SUI_SYSTEM_MUT).unwrap();
581 let bridge = builder.obj(bridge_object_arg).unwrap();
582 let bridge_pubkey = builder
583 .input(CallArg::Pure(
584 bcs::to_bytes(&bridge_authority_pub_key_bytes).unwrap(),
585 ))
586 .unwrap();
587 let url = builder
588 .input(CallArg::Pure(bcs::to_bytes(bridge_url.as_bytes()).unwrap()))
589 .unwrap();
590 builder.programmable_move_call(
591 BRIDGE_PACKAGE_ID,
592 BRIDGE_MODULE_NAME.into(),
593 Identifier::from_str("committee_registration").unwrap(),
594 vec![],
595 vec![bridge, system_state, bridge_pubkey, url],
596 );
597 let data = TransactionData::new_programmable(
598 validator_address,
599 vec![*gas_object_ref],
600 builder.finish(),
601 gas_budget,
602 ref_gas_price,
603 );
604 Ok(data)
605}
606
607pub fn build_committee_update_url_transaction(
608 validator_address: SuiAddress,
609 gas_object_ref: &ObjectRef,
610 bridge_object_arg: ObjectArg,
611 bridge_url: &str,
612 ref_gas_price: u64,
613 gas_budget: u64,
614) -> BridgeResult<TransactionData> {
615 let mut builder = ProgrammableTransactionBuilder::new();
616 let bridge = builder.obj(bridge_object_arg).unwrap();
617 let url = builder
618 .input(CallArg::Pure(bcs::to_bytes(bridge_url.as_bytes()).unwrap()))
619 .unwrap();
620 builder.programmable_move_call(
621 BRIDGE_PACKAGE_ID,
622 BRIDGE_MODULE_NAME.into(),
623 Identifier::from_str("update_node_url").unwrap(),
624 vec![],
625 vec![bridge, url],
626 );
627 let data = TransactionData::new_programmable(
628 validator_address,
629 vec![*gas_object_ref],
630 builder.finish(),
631 gas_budget,
632 ref_gas_price,
633 );
634 Ok(data)
635}
636
637#[cfg(test)]
638mod tests {
639 use crate::crypto::BridgeAuthorityKeyPair;
640 use crate::e2e_tests::test_utils::TestClusterWrapperBuilder;
641 use crate::metrics::BridgeMetrics;
642 use crate::sui_client::SuiClient;
643 use crate::types::BridgeAction;
644 use crate::types::EmergencyAction;
645 use crate::types::EmergencyActionType;
646 use crate::types::*;
647 use crate::{
648 crypto::BridgeAuthorityPublicKeyBytes,
649 test_utils::{
650 approve_action_with_validator_secrets, bridge_token, get_test_eth_to_sui_bridge_action,
651 get_test_sui_to_eth_bridge_action,
652 },
653 };
654 use ethers::types::Address as EthAddress;
655 use std::collections::HashMap;
656 use std::sync::Arc;
657 use sui_types::bridge::{BridgeChainId, TOKEN_ID_BTC, TOKEN_ID_USDC};
658 use sui_types::crypto::ToFromBytes;
659 use sui_types::crypto::get_key_pair;
660
661 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
662 async fn test_build_sui_transaction_for_token_transfer() {
663 telemetry_subscribers::init_for_testing();
664 let mut bridge_keys = vec![];
665 for _ in 0..=3 {
666 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
667 bridge_keys.push(kp);
668 }
669 let mut test_cluster = TestClusterWrapperBuilder::new()
670 .with_bridge_authority_keys(bridge_keys)
671 .with_deploy_tokens(true)
672 .build()
673 .await;
674
675 let metrics = Arc::new(BridgeMetrics::new_for_testing());
676 let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics)
677 .await
678 .unwrap();
679 let bridge_authority_keys = test_cluster.authority_keys_clone();
680
681 test_cluster
684 .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized()
685 .await;
686 let context = &mut test_cluster.inner.wallet;
687 let sender = context.active_address().unwrap();
688 let usdc_amount = 5000000;
689 let bridge_object_arg = sui_client
690 .get_mutable_bridge_object_arg_must_succeed()
691 .await;
692 let id_token_map = sui_client.get_token_id_map().await.unwrap();
693
694 let action = get_test_eth_to_sui_bridge_action(None, Some(usdc_amount), Some(sender), None);
696 let usdc_object_ref = approve_action_with_validator_secrets(
698 context,
699 bridge_object_arg,
700 action.clone(),
701 &bridge_authority_keys,
702 Some(sender),
703 &id_token_map,
704 )
705 .await
706 .unwrap();
707
708 let bridge_event = bridge_token(
710 context,
711 EthAddress::random(),
712 usdc_object_ref,
713 id_token_map.get(&TOKEN_ID_USDC).unwrap().clone(),
714 bridge_object_arg,
715 )
716 .await;
717
718 let action = get_test_sui_to_eth_bridge_action(
719 None,
720 None,
721 Some(bridge_event.nonce),
722 Some(bridge_event.amount_sui_adjusted),
723 Some(bridge_event.sui_address),
724 Some(bridge_event.eth_address),
725 Some(TOKEN_ID_USDC),
726 );
727 approve_action_with_validator_secrets(
729 context,
730 bridge_object_arg,
731 action.clone(),
732 &bridge_authority_keys,
733 None,
734 &id_token_map,
735 )
736 .await;
737 }
738
739 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
740 async fn test_build_sui_transaction_for_emergency_op() {
741 telemetry_subscribers::init_for_testing();
742 let num_valdiator = 2;
743 let mut bridge_keys = vec![];
744 for _ in 0..num_valdiator {
745 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
746 bridge_keys.push(kp);
747 }
748 let mut test_cluster = TestClusterWrapperBuilder::new()
749 .with_bridge_authority_keys(bridge_keys)
750 .with_deploy_tokens(true)
751 .build()
752 .await;
753 let metrics = Arc::new(BridgeMetrics::new_for_testing());
754 let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics)
755 .await
756 .unwrap();
757 let bridge_authority_keys = test_cluster.authority_keys_clone();
758
759 test_cluster
761 .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized()
762 .await;
763 let summary = sui_client.get_bridge_summary().await.unwrap();
764 assert!(!summary.is_frozen);
765
766 let context = &mut test_cluster.inner.wallet;
767 let bridge_object_arg = sui_client
768 .get_mutable_bridge_object_arg_must_succeed()
769 .await;
770 let id_token_map = sui_client.get_token_id_map().await.unwrap();
771
772 let action = BridgeAction::EmergencyAction(EmergencyAction {
774 nonce: 0,
775 chain_id: BridgeChainId::SuiCustom,
776 action_type: EmergencyActionType::Pause,
777 });
778 approve_action_with_validator_secrets(
780 context,
781 bridge_object_arg,
782 action.clone(),
783 &bridge_authority_keys,
784 None,
785 &id_token_map,
786 )
787 .await;
788 let summary = sui_client.get_bridge_summary().await.unwrap();
789 assert!(summary.is_frozen);
790
791 let action = BridgeAction::EmergencyAction(EmergencyAction {
793 nonce: 1,
794 chain_id: BridgeChainId::SuiCustom,
795 action_type: EmergencyActionType::Unpause,
796 });
797 approve_action_with_validator_secrets(
799 context,
800 bridge_object_arg,
801 action.clone(),
802 &bridge_authority_keys,
803 None,
804 &id_token_map,
805 )
806 .await;
807 let summary = sui_client.get_bridge_summary().await.unwrap();
808 assert!(!summary.is_frozen);
809 }
810
811 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
812 async fn test_build_sui_transaction_for_committee_blocklist() {
813 telemetry_subscribers::init_for_testing();
814 let mut bridge_keys = vec![];
815 for _ in 0..=3 {
816 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
817 bridge_keys.push(kp);
818 }
819 let mut test_cluster = TestClusterWrapperBuilder::new()
820 .with_bridge_authority_keys(bridge_keys)
821 .with_deploy_tokens(true)
822 .build()
823 .await;
824 let metrics = Arc::new(BridgeMetrics::new_for_testing());
825 let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics)
826 .await
827 .unwrap();
828 let bridge_authority_keys = test_cluster.authority_keys_clone();
829
830 test_cluster
832 .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized()
833 .await;
834 let committee = sui_client.get_bridge_summary().await.unwrap().committee;
835 let victim = committee.members.first().unwrap().clone().1;
836 for member in committee.members {
837 assert!(!member.1.blocklisted);
838 }
839
840 let context = &mut test_cluster.inner.wallet;
841 let bridge_object_arg = sui_client
842 .get_mutable_bridge_object_arg_must_succeed()
843 .await;
844 let id_token_map = sui_client.get_token_id_map().await.unwrap();
845
846 let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
848 nonce: 0,
849 chain_id: BridgeChainId::SuiCustom,
850 blocklist_type: BlocklistType::Blocklist,
851 members_to_update: vec![
852 BridgeAuthorityPublicKeyBytes::from_bytes(&victim.bridge_pubkey_bytes).unwrap(),
853 ],
854 });
855 approve_action_with_validator_secrets(
857 context,
858 bridge_object_arg,
859 action.clone(),
860 &bridge_authority_keys,
861 None,
862 &id_token_map,
863 )
864 .await;
865 let committee = sui_client.get_bridge_summary().await.unwrap().committee;
866 for member in committee.members {
867 if member.1.bridge_pubkey_bytes == victim.bridge_pubkey_bytes {
868 assert!(member.1.blocklisted);
869 } else {
870 assert!(!member.1.blocklisted);
871 }
872 }
873
874 let action = BridgeAction::BlocklistCommitteeAction(BlocklistCommitteeAction {
876 nonce: 1,
877 chain_id: BridgeChainId::SuiCustom,
878 blocklist_type: BlocklistType::Unblocklist,
879 members_to_update: vec![
880 BridgeAuthorityPublicKeyBytes::from_bytes(&victim.bridge_pubkey_bytes).unwrap(),
881 ],
882 });
883 approve_action_with_validator_secrets(
885 context,
886 bridge_object_arg,
887 action.clone(),
888 &bridge_authority_keys,
889 None,
890 &id_token_map,
891 )
892 .await;
893 let committee = sui_client.get_bridge_summary().await.unwrap().committee;
894 for member in committee.members {
895 assert!(!member.1.blocklisted);
896 }
897 }
898
899 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
900 async fn test_build_sui_transaction_for_limit_update() {
901 telemetry_subscribers::init_for_testing();
902 let mut bridge_keys = vec![];
903 for _ in 0..=3 {
904 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
905 bridge_keys.push(kp);
906 }
907 let mut test_cluster = TestClusterWrapperBuilder::new()
908 .with_bridge_authority_keys(bridge_keys)
909 .with_deploy_tokens(true)
910 .build()
911 .await;
912 let metrics = Arc::new(BridgeMetrics::new_for_testing());
913 let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics)
914 .await
915 .unwrap();
916 let bridge_authority_keys = test_cluster.authority_keys_clone();
917
918 test_cluster
920 .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized()
921 .await;
922 let transfer_limit = sui_client
923 .get_bridge_summary()
924 .await
925 .unwrap()
926 .limiter
927 .transfer_limit
928 .into_iter()
929 .map(|(s, d, l)| ((s, d), l))
930 .collect::<HashMap<_, _>>();
931
932 let context = &mut test_cluster.inner.wallet;
933 let bridge_object_arg = sui_client
934 .get_mutable_bridge_object_arg_must_succeed()
935 .await;
936 let id_token_map = sui_client.get_token_id_map().await.unwrap();
937
938 let action = BridgeAction::LimitUpdateAction(LimitUpdateAction {
940 nonce: 0,
941 chain_id: BridgeChainId::SuiCustom,
942 sending_chain_id: BridgeChainId::EthCustom,
943 new_usd_limit: 6_666_666 * USD_MULTIPLIER, });
945 approve_action_with_validator_secrets(
947 context,
948 bridge_object_arg,
949 action.clone(),
950 &bridge_authority_keys,
951 None,
952 &id_token_map,
953 )
954 .await;
955 let new_transfer_limit = sui_client
956 .get_bridge_summary()
957 .await
958 .unwrap()
959 .limiter
960 .transfer_limit;
961 for limit in new_transfer_limit {
962 if limit.0 == BridgeChainId::EthCustom && limit.1 == BridgeChainId::SuiCustom {
963 assert_eq!(limit.2, 6_666_666 * USD_MULTIPLIER);
964 } else {
965 assert_eq!(limit.2, *transfer_limit.get(&(limit.0, limit.1)).unwrap());
966 }
967 }
968 }
969
970 #[tokio::test(flavor = "multi_thread", worker_threads = 8)]
971 async fn test_build_sui_transaction_for_price_update() {
972 telemetry_subscribers::init_for_testing();
973 let mut bridge_keys = vec![];
974 for _ in 0..=3 {
975 let (_, kp): (_, BridgeAuthorityKeyPair) = get_key_pair();
976 bridge_keys.push(kp);
977 }
978 let mut test_cluster = TestClusterWrapperBuilder::new()
979 .with_bridge_authority_keys(bridge_keys)
980 .with_deploy_tokens(true)
981 .build()
982 .await;
983 let metrics = Arc::new(BridgeMetrics::new_for_testing());
984 let sui_client = SuiClient::new(&test_cluster.inner.fullnode_handle.rpc_url, metrics)
985 .await
986 .unwrap();
987 let bridge_authority_keys = test_cluster.authority_keys_clone();
988
989 test_cluster
992 .trigger_reconfiguration_if_not_yet_and_assert_bridge_committee_initialized()
993 .await;
994 let notional_values = sui_client.get_notional_values().await.unwrap();
995 assert_ne!(notional_values[&TOKEN_ID_USDC], 69_000 * USD_MULTIPLIER);
996
997 let context = &mut test_cluster.inner.wallet;
998 let bridge_object_arg = sui_client
999 .get_mutable_bridge_object_arg_must_succeed()
1000 .await;
1001 let id_token_map = sui_client.get_token_id_map().await.unwrap();
1002
1003 let action = BridgeAction::AssetPriceUpdateAction(AssetPriceUpdateAction {
1005 nonce: 0,
1006 chain_id: BridgeChainId::SuiCustom,
1007 token_id: TOKEN_ID_BTC,
1008 new_usd_price: 69_000 * USD_MULTIPLIER, });
1010 approve_action_with_validator_secrets(
1012 context,
1013 bridge_object_arg,
1014 action.clone(),
1015 &bridge_authority_keys,
1016 None,
1017 &id_token_map,
1018 )
1019 .await;
1020 let new_notional_values = sui_client.get_notional_values().await.unwrap();
1021 for (token_id, price) in new_notional_values {
1022 if token_id == TOKEN_ID_BTC {
1023 assert_eq!(price, 69_000 * USD_MULTIPLIER);
1024 } else {
1025 assert_eq!(price, *notional_values.get(&token_id).unwrap());
1026 }
1027 }
1028 }
1029}