multiversx_sc_modules/
ongoing_operation.rs1multiversx_sc::imports!();
2
3pub const DEFAULT_MIN_GAS_TO_SAVE_PROGRESS: u64 = 1_000_000;
4
5pub type LoopOp = bool;
6pub const CONTINUE_OP: bool = true;
7pub const STOP_OP: bool = false;
8
9#[multiversx_sc::module]
10pub trait OngoingOperationModule {
11    fn run_while_it_has_gas<Process>(
44        &self,
45        min_gas_to_save_progress: u64,
46        mut process: Process,
47    ) -> OperationCompletionStatus
48    where
49        Process: FnMut() -> LoopOp,
50    {
51        let mut gas_per_iteration = 0;
52        let mut gas_before = self.blockchain().get_gas_left();
53        loop {
54            let loop_op = process();
55            if loop_op == STOP_OP {
56                break;
57            }
58
59            let gas_after = self.blockchain().get_gas_left();
60            let current_iteration_cost = gas_before - gas_after;
61            if current_iteration_cost > gas_per_iteration {
62                gas_per_iteration = current_iteration_cost;
63            }
64
65            if !self.can_continue_operation(gas_per_iteration, min_gas_to_save_progress) {
66                return OperationCompletionStatus::InterruptedBeforeOutOfGas;
67            }
68
69            gas_before = gas_after;
70        }
71
72        self.clear_operation();
73
74        OperationCompletionStatus::Completed
75    }
76
77    #[inline]
78    fn can_continue_operation(&self, operation_cost: u64, min_gas_to_save_progress: u64) -> bool {
79        let gas_left = self.blockchain().get_gas_left();
80
81        gas_left > min_gas_to_save_progress + operation_cost
82    }
83
84    fn load_operation<T: TopDecode + Default>(&self) -> T {
87        let raw_buffer = self.current_ongoing_operation().get();
88        if raw_buffer.is_empty() {
89            return T::default();
90        }
91
92        match T::top_decode(raw_buffer) {
93            Result::Ok(op) => op,
94            Result::Err(err) => sc_panic!(err.message_str()),
95        }
96    }
97
98    fn save_progress<T: TopEncode>(&self, op: &T) {
100        let mut encoded_op = ManagedBuffer::new();
101        if let Result::Err(err) = op.top_encode(&mut encoded_op) {
102            sc_panic!(err.message_str());
103        }
104
105        self.current_ongoing_operation().set(&encoded_op);
106    }
107
108    #[inline]
110    fn clear_operation(&self) {
111        self.current_ongoing_operation().clear();
112    }
113
114    #[storage_mapper("ongoing_operation:currentOngoingOperation")]
115    fn current_ongoing_operation(&self) -> SingleValueMapper<ManagedBuffer>;
116}