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