sui_cluster_test/test_case/
coin_merge_split_test.rs1use crate::{TestCaseImpl, TestContext, helper::ObjectChecker};
5use async_trait::async_trait;
6use jsonrpsee::rpc_params;
7use sui_json_rpc_types::{SuiTransactionBlockEffectsAPI, SuiTransactionBlockResponse};
8use sui_types::base_types::{ObjectID, SuiAddress};
9use sui_types::object::Owner;
10use sui_types::sui_serde::BigInt;
11use tracing::{debug, info};
12
13pub struct CoinMergeSplitTest;
14
15#[async_trait]
16impl TestCaseImpl for CoinMergeSplitTest {
17 fn name(&self) -> &'static str {
18 "CoinMergeSplit"
19 }
20
21 fn description(&self) -> &'static str {
22 "Test merge and split SUI coins"
23 }
24
25 async fn run(&self, ctx: &mut TestContext) -> Result<(), anyhow::Error> {
26 let mut sui_objs = ctx.get_sui_from_faucet(Some(1)).await;
27 let gas_obj = sui_objs.swap_remove(0);
28
29 let signer = ctx.get_wallet_address();
30 let mut sui_objs_2 = ctx.get_sui_from_faucet(Some(1)).await;
31
32 let primary_coin = sui_objs_2.swap_remove(0);
33 let primary_coin_id = *primary_coin.id();
34 let original_value = primary_coin.value();
35
36 info!("Testing coin split.");
38 let amounts = vec![1.into(), ((original_value - 2) / 2).into()];
39
40 let response =
41 Self::split_coin(ctx, signer, *primary_coin.id(), amounts, *gas_obj.id()).await;
42 let tx_digest = response.digest;
43 let new_coins = response.effects.as_ref().unwrap().created();
44
45 ctx.let_fullnode_sync(vec![tx_digest], 5).await;
47
48 let _ = futures::future::join_all(
49 new_coins
50 .iter()
51 .map(|coin_ref| {
52 ObjectChecker::new(coin_ref.reference.object_id)
53 .owner(Owner::AddressOwner(signer))
54 .check_into_gas_coin(ctx.get_fullnode_client())
55 })
56 .collect::<Vec<_>>(),
57 )
58 .await;
59
60 info!("Testing coin merge.");
62 let mut coins_merged = Vec::new();
63 let mut txes = Vec::new();
64 for new_coin in new_coins {
66 let coin_to_merge = new_coin.reference.object_id;
67 debug!(
68 "Merging coin {} back to {}.",
69 coin_to_merge, primary_coin_id
70 );
71 let response =
72 Self::merge_coin(ctx, signer, primary_coin_id, coin_to_merge, *gas_obj.id()).await;
73 debug!("Verifying the merged coin {} is deleted.", coin_to_merge);
74 coins_merged.push(coin_to_merge);
75 txes.push(response.digest);
76 }
77
78 ctx.let_fullnode_sync(txes, 5).await;
80
81 let _ = futures::future::join_all(
82 coins_merged
83 .iter()
84 .map(|obj_id| {
85 ObjectChecker::new(*obj_id)
86 .owner(Owner::AddressOwner(signer))
87 .deleted()
88 .check(ctx.get_fullnode_client())
89 })
90 .collect::<Vec<_>>(),
91 )
92 .await
93 .into_iter()
94 .collect::<Vec<_>>();
95
96 debug!(
98 "Verifying owner still owns the primary coin {}",
99 *primary_coin.id()
100 );
101 let primary_after_merge = ObjectChecker::new(primary_coin_id)
102 .owner(Owner::AddressOwner(ctx.get_wallet_address()))
103 .check_into_gas_coin(ctx.get_fullnode_client())
104 .await;
105 assert_eq!(
106 primary_after_merge.value(),
107 original_value,
108 "Split-then-merge yields unexpected coin value, expect {}, got {}",
109 original_value,
110 primary_after_merge.value(),
111 );
112 Ok(())
113 }
114}
115
116impl CoinMergeSplitTest {
117 async fn merge_coin(
118 ctx: &TestContext,
119 signer: SuiAddress,
120 primary_coin: ObjectID,
121 coin_to_merge: ObjectID,
122 gas_obj_id: ObjectID,
123 ) -> SuiTransactionBlockResponse {
124 let params = rpc_params![
125 signer,
126 primary_coin,
127 coin_to_merge,
128 Some(gas_obj_id),
129 (20_000_000).to_string()
130 ];
131
132 let data = ctx
133 .build_transaction_remotely("unsafe_mergeCoins", params)
134 .await
135 .unwrap();
136
137 ctx.sign_and_execute(data, "coin merge").await
138 }
139
140 async fn split_coin(
141 ctx: &TestContext,
142 signer: SuiAddress,
143 primary_coin: ObjectID,
144 amounts: Vec<BigInt<u64>>,
145 gas_obj_id: ObjectID,
146 ) -> SuiTransactionBlockResponse {
147 let params = rpc_params![
148 signer,
149 primary_coin,
150 amounts,
151 Some(gas_obj_id),
152 (20_000_000).to_string()
153 ];
154
155 let data = ctx
156 .build_transaction_remotely("unsafe_splitCoin", params)
157 .await
158 .unwrap();
159
160 ctx.sign_and_execute(data, "coin merge").await
161 }
162}