1#[cfg(debug_assertions)]
4use std::collections::HashMap;
5#[cfg(debug_assertions)]
6use std::sync::Mutex;
7
8pub trait SuiRpc: Send + Sync {
10 fn get_object(
12 &self,
13 object_id: [u8; 32],
14 ) -> Result<Option<SuiObject>, Box<dyn std::error::Error + Send + Sync>>;
15
16 fn get_transaction_block(
18 &self,
19 digest: [u8; 32],
20 ) -> Result<Option<SuiTransactionBlock>, Box<dyn std::error::Error + Send + Sync>>;
21
22 fn get_transaction_events(
24 &self,
25 digest: [u8; 32],
26 ) -> Result<Vec<SuiEvent>, Box<dyn std::error::Error + Send + Sync>>;
27
28 fn get_checkpoint(
30 &self,
31 sequence_number: u64,
32 ) -> Result<Option<SuiCheckpoint>, Box<dyn std::error::Error + Send + Sync>>;
33
34 fn get_latest_checkpoint_sequence_number(
36 &self,
37 ) -> Result<u64, Box<dyn std::error::Error + Send + Sync>>;
38
39 fn sender_address(&self) -> Result<[u8; 32], Box<dyn std::error::Error + Send + Sync>>;
41
42 fn get_gas_objects(
44 &self,
45 owner: [u8; 32],
46 ) -> Result<Vec<SuiObject>, Box<dyn std::error::Error + Send + Sync>>;
47
48 fn execute_signed_transaction(
55 &self,
56 tx_bytes: Vec<u8>,
57 signature: Vec<u8>,
58 public_key: Vec<u8>,
59 ) -> Result<[u8; 32], Box<dyn std::error::Error + Send + Sync>>;
60
61 fn wait_for_transaction(
63 &self,
64 digest: [u8; 32],
65 timeout_ms: u64,
66 ) -> Result<Option<SuiTransactionBlock>, Box<dyn std::error::Error + Send + Sync>>;
67
68 fn get_ledger_info(&self) -> Result<SuiLedgerInfo, Box<dyn std::error::Error + Send + Sync>>;
70
71 fn as_any(&self) -> &dyn std::any::Any {
73 unimplemented!("as_any() must be implemented by concrete types")
74 }
75}
76
77#[derive(Clone, Debug)]
79pub struct SuiObject {
80 pub object_id: [u8; 32],
81 pub version: u64,
82 pub owner: Vec<u8>,
83 pub object_type: String,
84 pub has_public_transfer: bool,
85}
86
87#[derive(Clone, Debug)]
89pub struct SuiObjectChange {
90 pub object_id: [u8; 32],
91 pub change_type: String,
92}
93
94#[derive(Clone, Debug, PartialEq)]
96pub enum SuiExecutionStatus {
97 Success,
98 Failure { error: String },
99}
100
101#[derive(Clone, Debug)]
103pub struct SuiTransactionEffects {
104 pub status: SuiExecutionStatus,
105 pub gas_used: u64,
106 pub modified_objects: Vec<SuiObjectChange>,
107}
108
109#[derive(Clone, Debug)]
111pub struct SuiTransactionBlock {
112 pub digest: [u8; 32],
113 pub checkpoint: Option<u64>,
114 pub effects: SuiTransactionEffects,
115}
116
117#[derive(Clone, Debug)]
119pub struct SuiEvent {
120 pub id: String,
121 pub transaction_digest: [u8; 32],
122 pub event_sequence_number: u64,
123 pub type_field: String,
124 pub data: Vec<u8>,
125}
126
127#[derive(Clone, Debug)]
129pub struct SuiCheckpoint {
130 pub sequence_number: u64,
131 pub digest: [u8; 32],
132 pub epoch: u64,
133 pub network_total_transactions: u64,
134 pub certified: bool,
135}
136
137#[derive(Clone, Debug)]
139pub struct SuiLedgerInfo {
140 pub latest_version: u64,
141 pub latest_epoch: u64,
142}
143
144#[cfg(debug_assertions)]
149pub struct MockSuiRpc {
150 objects: Mutex<HashMap<[u8; 32], SuiObject>>,
151 transactions: Mutex<HashMap<[u8; 32], SuiTransactionBlock>>,
152 checkpoints: Mutex<HashMap<u64, SuiCheckpoint>>,
153 latest_checkpoint: u64,
154 mock_address: [u8; 32],
155 tx_counter: std::sync::atomic::AtomicU64,
156}
157
158#[cfg(debug_assertions)]
159impl MockSuiRpc {
160 pub fn new(latest_checkpoint: u64) -> Self {
161 Self {
162 objects: Mutex::new(HashMap::new()),
163 transactions: Mutex::new(HashMap::new()),
164 checkpoints: Mutex::new(HashMap::new()),
165 latest_checkpoint,
166 mock_address: [0x42; 32],
167 tx_counter: std::sync::atomic::AtomicU64::new(0),
168 }
169 }
170
171 pub fn new_with_address(latest_checkpoint: u64, address: [u8; 32]) -> Self {
172 Self {
173 objects: Mutex::new(HashMap::new()),
174 transactions: Mutex::new(HashMap::new()),
175 checkpoints: Mutex::new(HashMap::new()),
176 latest_checkpoint,
177 mock_address: address,
178 tx_counter: std::sync::atomic::AtomicU64::new(0),
179 }
180 }
181
182 pub fn add_object(&self, object: SuiObject) {
183 self.objects
184 .lock()
185 .unwrap()
186 .insert(object.object_id, object);
187 }
188
189 pub fn add_transaction(&self, tx: SuiTransactionBlock) {
190 self.transactions.lock().unwrap().insert(tx.digest, tx);
191 }
192
193 pub fn add_checkpoint(&self, checkpoint: SuiCheckpoint) {
194 self.checkpoints
195 .lock()
196 .unwrap()
197 .insert(checkpoint.sequence_number, checkpoint);
198 }
199}
200
201#[cfg(debug_assertions)]
202impl SuiRpc for MockSuiRpc {
203 fn get_object(
204 &self,
205 object_id: [u8; 32],
206 ) -> Result<Option<SuiObject>, Box<dyn std::error::Error + Send + Sync>> {
207 Ok(self.objects.lock().unwrap().get(&object_id).cloned())
208 }
209
210 fn get_transaction_block(
211 &self,
212 digest: [u8; 32],
213 ) -> Result<Option<SuiTransactionBlock>, Box<dyn std::error::Error + Send + Sync>> {
214 Ok(self.transactions.lock().unwrap().get(&digest).cloned())
215 }
216
217 fn get_transaction_events(
218 &self,
219 _digest: [u8; 32],
220 ) -> Result<Vec<SuiEvent>, Box<dyn std::error::Error + Send + Sync>> {
221 Ok(Vec::new())
222 }
223
224 fn get_checkpoint(
225 &self,
226 sequence_number: u64,
227 ) -> Result<Option<SuiCheckpoint>, Box<dyn std::error::Error + Send + Sync>> {
228 Ok(self
229 .checkpoints
230 .lock()
231 .unwrap()
232 .get(&sequence_number)
233 .cloned())
234 }
235
236 fn get_latest_checkpoint_sequence_number(
237 &self,
238 ) -> Result<u64, Box<dyn std::error::Error + Send + Sync>> {
239 Ok(self.latest_checkpoint)
240 }
241
242 fn sender_address(&self) -> Result<[u8; 32], Box<dyn std::error::Error + Send + Sync>> {
243 Ok(self.mock_address)
244 }
245
246 fn get_gas_objects(
247 &self,
248 _owner: [u8; 32],
249 ) -> Result<Vec<SuiObject>, Box<dyn std::error::Error + Send + Sync>> {
250 Ok(vec![SuiObject {
252 object_id: [0x01; 32],
253 version: 1,
254 owner: self.mock_address.to_vec(),
255 object_type: "0x2::coin::Coin<0x2::sui::SUI>".to_string(),
256 has_public_transfer: true,
257 }])
258 }
259
260 fn execute_signed_transaction(
261 &self,
262 _tx_bytes: Vec<u8>,
263 _signature: Vec<u8>,
264 _public_key: Vec<u8>,
265 ) -> Result<[u8; 32], Box<dyn std::error::Error + Send + Sync>> {
266 let counter = self
268 .tx_counter
269 .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
270 let mut digest = [0u8; 32];
271 digest[..4].copy_from_slice(b"mock");
272 digest[4..12].copy_from_slice(&counter.to_le_bytes());
273 Ok(digest)
274 }
275
276 fn wait_for_transaction(
277 &self,
278 _digest: [u8; 32],
279 _timeout_ms: u64,
280 ) -> Result<Option<SuiTransactionBlock>, Box<dyn std::error::Error + Send + Sync>> {
281 Ok(None)
282 }
283
284 fn get_ledger_info(&self) -> Result<SuiLedgerInfo, Box<dyn std::error::Error + Send + Sync>> {
285 Ok(SuiLedgerInfo {
286 latest_version: self.latest_checkpoint,
287 latest_epoch: 1,
288 })
289 }
290
291 fn as_any(&self) -> &dyn std::any::Any {
292 self
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_mock_object() {
302 let rpc = MockSuiRpc::new(1000);
303 let obj = SuiObject {
304 object_id: [1u8; 32],
305 version: 1,
306 owner: vec![2, 3],
307 object_type: "CSV::Seal".to_string(),
308 has_public_transfer: false,
309 };
310 rpc.add_object(obj.clone());
311
312 let fetched = rpc.get_object([1u8; 32]).unwrap();
313 assert_eq!(fetched.unwrap().version, 1);
314 }
315
316 #[test]
317 fn test_mock_checkpoint() {
318 let rpc = MockSuiRpc::new(1000);
319 let cp = SuiCheckpoint {
320 sequence_number: 500,
321 digest: [1u8; 32],
322 epoch: 1,
323 network_total_transactions: 50000,
324 certified: true,
325 };
326 rpc.add_checkpoint(cp.clone());
327
328 let fetched = rpc.get_checkpoint(500).unwrap();
329 assert!(fetched.unwrap().certified);
330 }
331}