ink_sandbox/api/
system_api.rs

1use crate::{
2    EventRecordOf,
3    RuntimeCall,
4    Sandbox,
5};
6use frame_support::sp_runtime::{
7    traits::{
8        Dispatchable,
9        Saturating,
10    },
11    DispatchResultWithInfo,
12};
13use frame_system::pallet_prelude::BlockNumberFor;
14
15/// System API for the sandbox.
16pub trait SystemAPI {
17    /// The runtime system config.
18    type T: frame_system::Config;
19
20    /// Build a new empty block and return the new height.
21    fn build_block(&mut self) -> BlockNumberFor<Self::T>;
22
23    /// Build `n` empty blocks and return the new height.
24    ///
25    /// # Arguments
26    ///
27    /// * `n` - The number of blocks to build.
28    fn build_blocks(&mut self, n: u32) -> BlockNumberFor<Self::T>;
29
30    /// Return the current height of the chain.
31    fn block_number(&mut self) -> BlockNumberFor<Self::T>;
32
33    /// Return the events of the current block so far.
34    fn events(&mut self) -> Vec<EventRecordOf<Self::T>>;
35
36    /// Reset the events of the current block.
37    fn reset_events(&mut self);
38
39    /// Execute a runtime call (dispatchable).
40    ///
41    /// # Arguments
42    ///
43    /// * `call` - The runtime call to execute.
44    /// * `origin` - The origin of the call.
45    fn runtime_call<Origin: Into<<RuntimeCall<Self::T> as Dispatchable>::RuntimeOrigin>>(
46        &mut self,
47        call: RuntimeCall<Self::T>,
48        origin: Origin,
49    ) -> DispatchResultWithInfo<<RuntimeCall<Self::T> as Dispatchable>::PostInfo>;
50}
51
52impl<T> SystemAPI for T
53where
54    T: Sandbox,
55    T::Runtime: frame_system::Config,
56{
57    type T = T::Runtime;
58
59    fn build_block(&mut self) -> BlockNumberFor<Self::T> {
60        self.execute_with(|| {
61            let mut current_block = frame_system::Pallet::<Self::T>::block_number();
62            let block_hash = T::finalize_block(current_block);
63            current_block.saturating_inc();
64            T::initialize_block(current_block, block_hash);
65            current_block
66        })
67    }
68
69    fn build_blocks(&mut self, n: u32) -> BlockNumberFor<Self::T> {
70        let mut last_block = None;
71        for _ in 0..n {
72            last_block = Some(self.build_block());
73        }
74        last_block.unwrap_or_else(|| self.block_number())
75    }
76
77    fn block_number(&mut self) -> BlockNumberFor<Self::T> {
78        self.execute_with(frame_system::Pallet::<Self::T>::block_number)
79    }
80
81    fn events(&mut self) -> Vec<EventRecordOf<Self::T>> {
82        self.execute_with(frame_system::Pallet::<Self::T>::events)
83    }
84
85    fn reset_events(&mut self) {
86        self.execute_with(frame_system::Pallet::<Self::T>::reset_events)
87    }
88
89    fn runtime_call<
90        Origin: Into<<RuntimeCall<Self::T> as Dispatchable>::RuntimeOrigin>,
91    >(
92        &mut self,
93        call: RuntimeCall<Self::T>,
94        origin: Origin,
95    ) -> DispatchResultWithInfo<<RuntimeCall<Self::T> as Dispatchable>::PostInfo> {
96        self.execute_with(|| call.dispatch(origin.into()))
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::{
103        api::prelude::*,
104        DefaultSandbox,
105        RuntimeCall,
106        RuntimeEventOf,
107        RuntimeOf,
108        Sandbox,
109    };
110    use frame_support::sp_runtime::{
111        traits::Dispatchable,
112        AccountId32,
113        DispatchResultWithInfo,
114    };
115
116    fn make_transfer(
117        sandbox: &mut DefaultSandbox,
118        dest: AccountId32,
119        value: u128,
120    ) -> DispatchResultWithInfo<
121        <RuntimeCall<<DefaultSandbox as Sandbox>::Runtime> as Dispatchable>::PostInfo,
122    > {
123        assert_ne!(
124            DefaultSandbox::default_actor(),
125            dest,
126            "make_transfer should send to account different than default_actor"
127        );
128        sandbox.runtime_call(
129            RuntimeCall::<RuntimeOf<DefaultSandbox>>::Balances(pallet_balances::Call::<
130                RuntimeOf<DefaultSandbox>,
131            >::transfer_allow_death {
132                dest: dest.into(),
133                value,
134            }),
135            Some(DefaultSandbox::default_actor()),
136        )
137    }
138
139    #[test]
140    fn dry_run_works() {
141        let mut sandbox = DefaultSandbox::default();
142        let actor = DefaultSandbox::default_actor();
143        let initial_balance = sandbox.free_balance(&actor);
144
145        sandbox.dry_run(|sandbox| {
146            sandbox.mint_into(&actor, 100).unwrap();
147            assert_eq!(sandbox.free_balance(&actor), initial_balance + 100);
148        });
149
150        assert_eq!(sandbox.free_balance(&actor), initial_balance);
151    }
152
153    #[test]
154    fn runtime_call_works() {
155        let mut sandbox = DefaultSandbox::default();
156
157        const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]);
158        let initial_balance = sandbox.free_balance(&RECIPIENT);
159
160        let result = make_transfer(&mut sandbox, RECIPIENT, 100);
161        assert!(result.is_ok());
162
163        let expected_balance = initial_balance + 100;
164        assert_eq!(sandbox.free_balance(&RECIPIENT), expected_balance);
165    }
166
167    #[test]
168    fn current_events() {
169        let mut sandbox = DefaultSandbox::default();
170        const RECIPIENT: AccountId32 = AccountId32::new([2u8; 32]);
171
172        let events_before = sandbox.events();
173        assert!(events_before.is_empty());
174
175        make_transfer(&mut sandbox, RECIPIENT, 1).expect("Failed to make transfer");
176
177        let events_after = sandbox.events();
178        assert!(!events_after.is_empty());
179        assert!(matches!(
180            events_after.last().unwrap().event,
181            RuntimeEventOf::<DefaultSandbox>::Balances(_)
182        ));
183    }
184
185    #[test]
186    fn resetting_events() {
187        let mut sandbox = DefaultSandbox::default();
188        const RECIPIENT: AccountId32 = AccountId32::new([3u8; 32]);
189
190        make_transfer(&mut sandbox, RECIPIENT.clone(), 1)
191            .expect("Failed to make transfer");
192
193        assert!(!sandbox.events().is_empty());
194        sandbox.reset_events();
195        assert!(sandbox.events().is_empty());
196
197        make_transfer(&mut sandbox, RECIPIENT, 1).expect("Failed to make transfer");
198        assert!(!sandbox.events().is_empty());
199    }
200
201    #[test]
202    fn snapshot_works() {
203        let mut sandbox = DefaultSandbox::default();
204
205        // Check state before
206        let block_before = sandbox.block_number();
207        let snapshot_before = sandbox.take_snapshot();
208
209        // Advance some blocks to have some state change
210        let _ = sandbox.build_blocks(5);
211        let block_after = sandbox.block_number();
212
213        // Check block number and state after
214        assert_eq!(block_before + 5, block_after);
215
216        // Restore state
217        sandbox.restore_snapshot(snapshot_before);
218
219        // Check state after restore
220        assert_eq!(block_before, sandbox.block_number());
221    }
222}