ethcontract_mock/lib.rs
1#![deny(missing_docs, unsafe_code)]
2
3//! This crate allows emulating ethereum node with a limited number
4//! of supported RPC calls, enabling you to mock ethereum contracts.
5//!
6//! Create a new deployment using the [`Mock::deploy`] function.
7//!
8//! Configure contract's behaviour using [`Contract::expect_transaction`]
9//! and [`Contract::expect_call`].
10//!
11//! Finally, create an ethcontract's [`Instance`] by calling [`Contract::instance`],
12//! then use said instance in your tests.
13//!
14//! # Example
15//!
16//! Let's mock [voting contract] from solidity examples.
17//!
18//! First, we create a mock node and deploy a new mocked contract:
19//!
20//! ```
21//! # include!("test/doctest/common.rs");
22//! # #[tokio::main]
23//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
24//! # let abi = voting_abi();
25//! let mock = Mock::new(/* chain_id = */ 1337);
26//! let contract = mock.deploy(abi);
27//! # Ok(())
28//! # }
29//! ```
30//!
31//! Then we set up expectations for method calls:
32//!
33//! ```
34//! # include!("test/doctest/common.rs");
35//! # #[tokio::main]
36//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
37//! # let abi = voting_abi();
38//! # let account = account_for("Alice");
39//! # let mock = Mock::new(1337);
40//! # let contract = mock.deploy(abi);
41//! // We'll need to know method signatures and types.
42//! let vote: Signature<(U256,), ()> = [1, 33, 185, 63].into();
43//! let winning_proposal: Signature<(), U256> = [96, 159, 241, 189].into();
44//!
45//! // We expect some transactions calling the `vote` method.
46//! contract
47//! .expect_transaction(vote);
48//!
49//! // We also expect calls to `winning_proposal` that will return
50//! // a value of `1`.
51//! contract
52//! .expect_call(winning_proposal)
53//! .returns(1.into());
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! Finally, we create a dynamic instance and work with it as usual:
59//!
60//! ```
61//! # include!("test/doctest/common.rs");
62//! # #[tokio::main]
63//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
64//! # let abi = voting_abi();
65//! # let account = account_for("Alice");
66//! # let mock = Mock::new(1337);
67//! # let contract = mock.deploy(abi);
68//! # let vote: Signature<(U256,), ()> = [1, 33, 185, 63].into();
69//! # let winning_proposal: Signature<(), U256> = [96, 159, 241, 189].into();
70//! # contract.expect_transaction(vote);
71//! # contract.expect_call(winning_proposal).returns(1.into());
72//! let instance = contract.instance();
73//!
74//! instance
75//! .method(vote, (1.into(),))?
76//! .from(account)
77//! .send()
78//! .await?;
79//!
80//! let winning_proposal_index = instance
81//! .view_method(winning_proposal, ())?
82//! .call()
83//! .await?;
84//! assert_eq!(winning_proposal_index, 1.into());
85//! # Ok(())
86//! # }
87//! ```
88//!
89//! # Describing expectations
90//!
91//! The mocked contracts have an interface similar to the one
92//! of the [`mockall`] crate.
93//!
94//! For each contract's method that you expect to be called during a test,
95//! call [`Contract::expect_transaction`] or [`Contract::expect_call`]
96//! and set up the created [`Expectation`] with functions such as [`returns`],
97//! [`times`], [`in_sequence`]. For greater flexibility, you can have
98//! multiple expectations attached to the same method.
99//!
100//! See [`Expectation`] for more info and examples.
101//!
102//! # Interacting with mocked contracts
103//!
104//! After contract's behaviour is programmed, you can call
105//! [`Contract::instance`] to create an ethcontract's [`Instance`].
106//!
107//! You can also get contract's address and send RPC calls directly
108//! through [`web3`].
109//!
110//! Specifically, mock node supports `eth_call`, `eth_sendRawTransaction`,
111//! and `eth_getTransactionReceipt`.
112//!
113//! At the moment, mock node can't sign transactions on its own,
114//! so `eth_sendTransaction` is not supported. Also, deploying contracts
115//! via `eth_sendRawTransaction` is not possible yet.
116//!
117//! # Mocking generated contracts
118//!
119//! Overall, generated contracts are similar to the dynamic ones:
120//! they are deployed with [`Mock::deploy`] and configured with
121//! [`Contract::expect_call`] and [`Contract::expect_transaction`].
122//!
123//! You can get generated contract's ABI using the `raw_contract` function.
124//!
125//! Generated [method signatures] are available through the `signatures`
126//! function.
127//!
128//! Finally, type-safe instance can be created using the `at` method.
129//!
130//! Here's an example of mocking an ERC20-compatible contract.
131//!
132//! First, we create a mock node and deploy a new mocked contract:
133//!
134//! ```
135//! # include!("test/doctest/common.rs");
136//! # /*
137//! ethcontract::contract!("ERC20.json");
138//! # */
139//! # ethcontract::contract!(
140//! # "../examples/truffle/build/contracts/IERC20.json",
141//! # contract = IERC20 as ERC20,
142//! # );
143//!
144//! # #[tokio::main]
145//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
146//! let mock = Mock::new(/* chain_id = */ 1337);
147//! let contract = mock.deploy(ERC20::raw_contract().abi.clone());
148//! # Ok(())
149//! # }
150//! ```
151//!
152//! Then we set up expectations using the generated method signatures:
153//!
154//! ```
155//! # include!("test/doctest/common.rs");
156//! # ethcontract::contract!(
157//! # "../examples/truffle/build/contracts/IERC20.json",
158//! # contract = IERC20 as ERC20,
159//! # );
160//! # #[tokio::main]
161//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
162//! # let account = account_for("Alice");
163//! # let recipient = address_for("Bob");
164//! # let mock = Mock::new(1337);
165//! # let contract = mock.deploy(ERC20::raw_contract().abi.clone());
166//! contract
167//! .expect_transaction(ERC20::signatures().transfer())
168//! .once()
169//! .returns(true);
170//! # let instance = ERC20::at(&mock.web3(), contract.address());
171//! # instance.transfer(recipient, 100.into()).from(account).send().await?;
172//! # Ok(())
173//! # }
174//! ```
175//!
176//! Finally, we use mock contract's address to interact with the mock node:
177//!
178//! ```
179//! # include!("test/doctest/common.rs");
180//! # ethcontract::contract!(
181//! # "../examples/truffle/build/contracts/IERC20.json",
182//! # contract = IERC20 as ERC20,
183//! # );
184//! # #[tokio::main]
185//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
186//! # let account = account_for("Alice");
187//! # let recipient = address_for("Bob");
188//! # let mock = Mock::new(1337);
189//! # let contract = mock.deploy(ERC20::raw_contract().abi.clone());
190//! # contract.expect_transaction(ERC20::signatures().transfer());
191//! let instance = ERC20::at(&mock.web3(), contract.address());
192//! instance
193//! .transfer(recipient, 100.into())
194//! .from(account)
195//! .send()
196//! .await?;
197//! # Ok(())
198//! # }
199//! ```
200//!
201//! # Mocking gas and gas estimation
202//!
203//! Mock node allows you to customize value returned from `eth_gasPrice`
204//! RPC call. Use [`Mock::update_gas_price`] to set a new gas price.
205//!
206//! Estimating gas consumption with `eth_estimateGas` is not supported at the
207//! moment. For now, calls to `eth_estimateGas` always return `1`.
208//!
209//! [`web3-rs`]: ethcontract::web3
210//! [`web3`]: ethcontract::web3
211//! [`expect_call`]: Contract::expect_call
212//! [`expect_transaction`]: Contract::expect_transaction
213//! [`returns`]: Expectation::returns
214//! [`times`]: Expectation::times
215//! [`in_sequence`]: Expectation::in_sequence
216//! [`Instance`]: ethcontract::Instance
217//! [voting contract]: https://docs.soliditylang.org/en/v0.8.6/solidity-by-example.html#voting
218//! [method signatures]: Signature
219
220use crate::predicate::TuplePredicate;
221use crate::range::TimesRange;
222use ethcontract::common::hash::H32;
223use ethcontract::common::Abi;
224use ethcontract::dyns::{DynInstance, DynTransport, DynWeb3};
225use ethcontract::tokens::Tokenize;
226use ethcontract::{Address, U256};
227use std::marker::PhantomData;
228use std::sync::Arc;
229
230#[doc(no_inline)]
231pub use ethcontract::contract::Signature;
232
233mod details;
234mod predicate;
235mod range;
236pub mod utils;
237
238#[cfg(test)]
239mod test;
240
241/// Mock ethereum node.
242///
243/// This struct implements a virtual ethereum node with a limited number
244/// of supported RPC calls. You can interact with it via the standard
245/// transport from `web3`.
246///
247/// The main feature of this struct is deploying mocked contracts
248/// and interacting with them. Create new mocked contract with a call
249/// to [`deploy`] function. Then use the returned struct to set up
250/// expectations on contract methods, get deployed contract's address
251/// and [`Instance`] and make actual calls to it.
252///
253/// Deploying contracts with an RPC call is not supported at the moment.
254///
255/// [`deploy`]: Mock::deploy
256/// [`Instance`]: ethcontract::Instance
257#[derive(Clone)]
258pub struct Mock {
259 transport: details::MockTransport,
260}
261
262impl Mock {
263 /// Creates a new mock chain.
264 pub fn new(chain_id: u64) -> Self {
265 Mock {
266 transport: details::MockTransport::new(chain_id),
267 }
268 }
269
270 /// Creates a `Web3` object that can be used to interact with
271 /// the mocked chain.
272 pub fn web3(&self) -> DynWeb3 {
273 DynWeb3::new(self.transport())
274 }
275
276 /// Creates a `Transport` object that can be used to interact with
277 /// the mocked chain.
278 pub fn transport(&self) -> DynTransport {
279 DynTransport::new(self.transport.clone())
280 }
281
282 /// Deploys a new mocked contract and returns an object that allows
283 /// configuring expectations for contract methods.
284 pub fn deploy(&self, abi: Abi) -> Contract {
285 let address = self.transport.deploy(&abi);
286 Contract {
287 transport: self.transport.clone(),
288 address,
289 abi,
290 }
291 }
292
293 /// Deploys a new mocked contract with specified address and returns an object that allows
294 /// configuring expectations for contract methods.
295 pub fn deploy_with_address(&self, abi: Abi, address: Address) -> Contract {
296 self.transport.deploy_with_address(&abi, address);
297 Contract {
298 transport: self.transport.clone(),
299 address,
300 abi,
301 }
302 }
303
304 /// Updates gas price that is returned by RPC call `eth_gasPrice`.
305 ///
306 /// Mock node does not simulate gas consumption, so this value does not
307 /// affect anything if you don't call `eth_gasPrice`.
308 pub fn update_gas_price(&self, gas_price: u64) {
309 self.transport.update_gas_price(gas_price);
310 }
311
312 /// Verifies that all expectations on all contracts have been met,
313 /// then clears all expectations.
314 ///
315 /// Sometimes its useful to validate all expectations mid-test,
316 /// throw them away, and add new ones. That’s what checkpoints do.
317 /// See [mockall documentation] for more info.
318 ///
319 /// Note that all expectations returned from [`Contract::expect`] method
320 /// become invalid after checkpoint. Modifying them will result in panic.
321 ///
322 /// [mockall documentation]: https://docs.rs/mockall/#checkpoints
323 pub fn checkpoint(&self) {
324 self.transport.checkpoint();
325 }
326}
327
328impl std::fmt::Debug for Mock {
329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330 f.write_str("Mock")
331 }
332}
333
334/// A mocked contract deployed by the mock node.
335///
336/// This struct allows setting up expectations on which contract methods
337/// will be called, with what arguments, in what order, etc.
338pub struct Contract {
339 transport: details::MockTransport,
340 address: Address,
341 abi: Abi,
342}
343
344impl Contract {
345 /// Creates a `Web3` object that can be used to interact with
346 /// the mocked chain on which this contract is deployed.
347 pub fn web3(&self) -> DynWeb3 {
348 DynWeb3::new(self.transport())
349 }
350
351 /// Creates a `Transport` object that can be used to interact with
352 /// the mocked chain.
353 pub fn transport(&self) -> DynTransport {
354 DynTransport::new(self.transport.clone())
355 }
356
357 /// Creates a contract `Instance` that can be used to interact with
358 /// this contract.
359 pub fn instance(&self) -> DynInstance {
360 DynInstance::at(self.web3(), Arc::new(self.abi.clone().into()), self.address)
361 }
362
363 /// Consumes this object and transforms it into a contract `Instance`
364 /// that can be used to interact with this contract.
365 pub fn into_instance(self) -> DynInstance {
366 DynInstance::at(self.web3(), Arc::new(self.abi.into()), self.address)
367 }
368
369 /// Returns a reference to the contract's ABI.
370 pub fn abi(&self) -> &Abi {
371 &self.abi
372 }
373
374 /// Returns contract's address.
375 pub fn address(&self) -> Address {
376 self.address
377 }
378
379 /// Adds a new expectation for contract method. See [`Expectation`].
380 ///
381 /// Generic parameters are used to specify which rust types should be used
382 /// to encode and decode method's arguments and return value. If you're
383 /// using generated contracts, they will be inferred automatically.
384 /// If not, you may have to specify them manually.
385 ///
386 /// # Notes
387 ///
388 /// Expectations generated by this method will allow both view calls
389 /// and transactions. This is usually undesired, so prefer using
390 /// [`expect_call`] and [`expect_transaction`] instead.
391 ///
392 /// [`expect_call`]: Contract::expect_call
393 /// [`expect_transaction`]: Contract::expect_transaction
394 pub fn expect<P: Tokenize + Send + 'static, R: Tokenize + Send + 'static>(
395 &self,
396 signature: impl Into<Signature<P, R>>,
397 ) -> Expectation<P, R> {
398 let signature = signature.into().into_inner();
399 let (index, generation) = self.transport.expect::<P, R>(self.address, signature);
400 Expectation {
401 transport: self.transport.clone(),
402 address: self.address,
403 signature,
404 index,
405 generation,
406 _ph: PhantomData,
407 }
408 }
409
410 /// Adds a new expectation for contract method that only matches view calls.
411 ///
412 /// This is an equivalent of [`expect`] followed by [`allow_transactions`]`(false)`.
413 ///
414 /// [`expect`]: Contract::expect
415 /// [`allow_transactions`]: Expectation::allow_transactions
416 pub fn expect_call<P: Tokenize + Send + 'static, R: Tokenize + Send + 'static>(
417 &self,
418 signature: impl Into<Signature<P, R>>,
419 ) -> Expectation<P, R> {
420 self.expect(signature).allow_transactions(false)
421 }
422
423 /// Adds a new expectation for contract method that only matches transactions.
424 ///
425 /// This is an equivalent of [`expect`] followed by [`allow_calls`]`(false)`.
426 ///
427 /// [`expect`]: Contract::expect
428 /// [`allow_calls`]: Expectation::allow_calls
429 pub fn expect_transaction<P: Tokenize + Send + 'static, R: Tokenize + Send + 'static>(
430 &self,
431 signature: impl Into<Signature<P, R>>,
432 ) -> Expectation<P, R> {
433 self.expect(signature).allow_calls(false)
434 }
435
436 /// Verifies that all expectations on this contract have been met,
437 /// then clears all expectations.
438 ///
439 /// Sometimes its useful to validate all expectations mid-test,
440 /// throw them away, and add new ones. That’s what checkpoints do.
441 /// See [mockall documentation] for more info.
442 ///
443 /// Note that all expectations returned from [`expect`] method
444 /// become invalid after checkpoint. Modifying them will result in panic.
445 ///
446 /// [mockall documentation]: https://docs.rs/mockall/#checkpoints
447 /// [`expect`]: Contract::expect
448 pub fn checkpoint(&self) {
449 self.transport.contract_checkpoint(self.address);
450 }
451}
452
453/// Expectation for contract method.
454///
455/// A method could have multiple expectations associated with it.
456/// Each expectation specifies how the method should be called, how many times,
457/// with what arguments, etc.
458///
459/// When a method gets called, mock node determines if the call is expected
460/// or not. It goes through each of the method's expectations in order they
461/// were created, searching for the first expectation that matches the call.
462///
463/// If a suitable expectation is found, it is used to determine method's
464/// return value and other transaction properties. If not, the call
465/// is considered unexpected, and mock node panics.
466///
467/// To determine if a particular expectation should be used for the given call,
468/// mock node uses two of the expectation's properties:
469///
470/// - [`predicate`] checks if method's arguments and transaction properties
471/// match a certain criteria;
472/// - [times limiter] is used to limit number of times a single expectation
473/// can be used.
474///
475/// To determine result of a method call, [`returns`] property is used.
476///
477/// # Notes
478///
479/// Expectations can't be changed after they were used. That is, if you try
480/// to modify an expectation after making any calls to its contract method,
481/// mock node will panic. This happens because modifying an already-used
482/// expectation may break node's internal state. Adding new expectations
483/// at any time is fine, though.
484///
485/// [`predicate`]: Expectation::predicate
486/// [times limiter]: Expectation::times
487/// [`returns`]: Expectation::returns
488pub struct Expectation<P: Tokenize + Send + 'static, R: Tokenize + Send + 'static> {
489 transport: details::MockTransport,
490 address: Address,
491 signature: H32,
492 index: usize,
493 generation: usize,
494 _ph: PhantomData<(P, R)>,
495}
496
497// Allow not requiring `must_use` as `Expectation`s have side effects, but
498// return `Self` as a convenience for chaining operations. Note that this lint
499// is on nighlty only, so we also need to allow `unknown_lints` here to silence
500// warnings on all Rust versions we build for.
501#[allow(unknown_lints)]
502#[allow(clippy::return_self_not_must_use)]
503impl<P: Tokenize + Send + 'static, R: Tokenize + Send + 'static> Expectation<P, R> {
504 /// Specifies how many times this expectation can be called.
505 ///
506 /// By default, each expectation can be called any number of times,
507 /// including zero. This method allows specifying a more precise range.
508 ///
509 /// For example, use `times(1)` to indicate that the expectation
510 /// should be called exactly [`once`]. Or use `times(1..)` to indicate
511 /// that it should be called at least once. Any range syntax is accepted.
512 ///
513 /// If the expectation gets called less that the specified number
514 /// of times, the test panics.
515 ///
516 /// If it gets called enough number of times, expectation is considered
517 /// satisfied. It becomes inactive and is no longer checked when processing
518 /// new method calls.
519 ///
520 /// # Examples
521 ///
522 /// Consider a method with two expectations:
523 ///
524 /// ```
525 /// # include!("test/doctest/common.rs");
526 /// # #[tokio::main]
527 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
528 /// # let contract = contract();
529 /// # let signature = signature();
530 /// contract
531 /// .expect_call(signature)
532 /// .times(1..=2);
533 /// contract
534 /// .expect_call(signature);
535 /// # contract.instance().view_method(signature, (0, 0))?.call().await?;
536 /// # Ok(())
537 /// # }
538 /// ```
539 ///
540 /// The first two calls to this method will be dispatched to the first
541 /// expectation. Then first expectation will become satisfied, and all
542 /// other calls will be dispatched to the second one.
543 ///
544 /// # Notes
545 ///
546 /// When expectation becomes satisfied, previous expectations
547 /// are not altered and may still be unsatisfied. This is important
548 /// when you have expectations with predicates:
549 ///
550 /// ```
551 /// # include!("test/doctest/common.rs");
552 /// # #[tokio::main]
553 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
554 /// # let contract = contract();
555 /// # let signature = signature();
556 /// contract
557 /// .expect_call(signature)
558 /// .predicate_fn(|(a, b)| a == b)
559 /// .times(1..=2);
560 /// contract
561 /// .expect_call(signature)
562 /// .times(1);
563 /// contract
564 /// .expect_call(signature)
565 /// .times(..);
566 /// # contract.instance().view_method(signature, (0, 0))?.call().await?;
567 /// # contract.instance().view_method(signature, (0, 1))?.call().await?;
568 /// # Ok(())
569 /// # }
570 /// ```
571 ///
572 /// Here, first expectation can be called one or two times, second
573 /// expectation can be called exactly once, and third expectation
574 /// can be called arbitrary number of times.
575 ///
576 /// Now, consider the following sequence of calls:
577 ///
578 /// ```
579 /// # include!("test/doctest/common.rs");
580 /// # #[tokio::main]
581 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
582 /// # let contract = contract();
583 /// # let signature = signature();
584 /// # let instance = contract.instance();
585 /// # contract.expect(signature);
586 /// instance
587 /// .method(signature, (1, 1))?
588 /// .call()
589 /// .await?;
590 /// instance
591 /// .method(signature, (2, 3))?
592 /// .call()
593 /// .await?;
594 /// instance
595 /// .method(signature, (5, 5))?
596 /// .call()
597 /// .await?;
598 /// # Ok(())
599 /// # }
600 /// ```
601 ///
602 /// First call gets dispatched to the first expectation. Second call
603 /// can't be dispatched to the first expectation because of its predicate,
604 /// so it gets dispatched to the second one. Now, one may assume that
605 /// the third call will be dispatched to the third expectation. However,
606 /// first expectation can be called one more time, so it is not satisfied
607 /// yet. Because of this, third call gets dispatched
608 /// to the first expectation.
609 ///
610 /// [`once`]: Expectation::once
611 pub fn times(self, times: impl Into<TimesRange>) -> Self {
612 self.transport.times::<P, R>(
613 self.address,
614 self.signature,
615 self.index,
616 self.generation,
617 times.into(),
618 );
619 self
620 }
621
622 /// Indicates that this expectation can be called exactly zero times.
623 ///
624 /// See [`times`] for more info.
625 ///
626 /// [`times`]: Expectation::times
627 pub fn never(self) -> Self {
628 self.times(0)
629 }
630
631 /// Indicates that this expectation can be called exactly one time.
632 ///
633 /// See [`times`] for more info.
634 ///
635 /// [`times`]: Expectation::times
636 pub fn once(self) -> Self {
637 self.times(1)
638 }
639
640 /// Adds this expectation to a sequence.
641 ///
642 /// By default, expectations may be matched in any order. If a stricter
643 /// order is required, you can use sequences. See [mockall documentation]
644 /// for more info.
645 ///
646 /// # Limitations
647 ///
648 /// An expectation can be in one sequence only.
649 ///
650 /// Also, an expectation should have [`times`] limit set to an exact
651 /// number of calls, i.e., [`once`], two times, and so on.
652 ///
653 /// [mockall documentation]: https://docs.rs/mockall/#sequences
654 /// [`times`]: Expectation::times
655 /// [`once`]: Expectation::once
656 pub fn in_sequence(self, sequence: &mut mockall::Sequence) -> Self {
657 self.transport.in_sequence::<P, R>(
658 self.address,
659 self.signature,
660 self.index,
661 self.generation,
662 sequence,
663 );
664 self
665 }
666
667 /// Sets number of blocks that should be mined on top of the transaction
668 /// block. This method can be useful when there are custom transaction
669 /// confirmation settings.
670 pub fn confirmations(self, confirmations: u64) -> Self {
671 self.transport.confirmations::<P, R>(
672 self.address,
673 self.signature,
674 self.index,
675 self.generation,
676 confirmations,
677 );
678 self
679 }
680
681 /// Sets predicate for this expectation.
682 ///
683 /// If method has multiple expectations, they are checked one-by-one,
684 /// in order they were created. First expectation with a predicate that
685 /// matches method's parameters is called.
686 ///
687 /// This method accepts a tuple of predicates, one predicate
688 /// for each parameter.
689 ///
690 /// This method will overwrite any predicate that was set before.
691 ///
692 /// # Examples
693 ///
694 /// ```
695 /// # include!("test/doctest/common.rs");
696 /// # #[tokio::main]
697 /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
698 /// # let contract = contract();
699 /// # let signature = signature();
700 /// contract
701 /// .expect_call(signature)
702 /// .predicate((predicate::eq(1), predicate::eq(1)))
703 /// .returns(1);
704 /// contract
705 /// .expect_call(signature)
706 /// .predicate_fn(|(a, b)| a > b)
707 /// .returns(2);
708 /// contract
709 /// .expect_call(signature)
710 /// .returns(3);
711 /// # Ok(())
712 /// # }
713 /// ```
714 ///
715 /// Here, we have three expectations, resulting in the following behaviour.
716 /// If both arguments are equal to `1`, method returns `1`.
717 /// Otherwise, if the first argument is greater than the second one, method
718 /// returns `2`. Otherwise, it returns `3`.
719 ///
720 /// # Notes
721 ///
722 /// Having multiple predicates shines for complex setups that involve
723 /// [call sequences] and [limiting number of expectation uses].
724 /// For simpler setups like the one above, [`returns_fn`] may be more
725 /// clear and concise, and also more efficient.
726 ///
727 /// [call sequences]: Expectation::in_sequence
728 /// [limiting number of expectation uses]: Expectation::times
729 /// [`returns_fn`]: Expectation::returns_fn
730 pub fn predicate<T>(self, pred: T) -> Self
731 where
732 T: TuplePredicate<P> + Send + 'static,
733 <T as predicate::TuplePredicate<P>>::P: Send,
734 {
735 self.transport.predicate::<P, R>(
736 self.address,
737 self.signature,
738 self.index,
739 self.generation,
740 Box::new(pred.into_predicate()),
741 );
742 self
743 }
744
745 /// Sets predicate function for this expectation. This function accepts
746 /// a tuple of method's arguments and returns `true` if this
747 /// expectation should be called. See [`predicate`] for more info.
748 ///
749 /// This method will overwrite any predicate that was set before.
750 ///
751 /// [`predicate`]: Expectation::predicate
752 pub fn predicate_fn(self, pred: impl Fn(&P) -> bool + Send + 'static) -> Self {
753 self.transport.predicate_fn::<P, R>(
754 self.address,
755 self.signature,
756 self.index,
757 self.generation,
758 Box::new(pred),
759 );
760 self
761 }
762
763 /// Sets predicate function for this expectation. This function accepts
764 /// a [call context] and a tuple of method's arguments and returns `true`
765 /// if this expectation should be called. See [`predicate`] for more info.
766 ///
767 /// This method will overwrite any predicate that was set before.
768 ///
769 /// [call context]: CallContext
770 /// [`predicate`]: Expectation::predicate
771 pub fn predicate_fn_ctx(
772 self,
773 pred: impl Fn(&CallContext, &P) -> bool + Send + 'static,
774 ) -> Self {
775 self.transport.predicate_fn_ctx::<P, R>(
776 self.address,
777 self.signature,
778 self.index,
779 self.generation,
780 Box::new(pred),
781 );
782 self
783 }
784
785 /// Indicates that this expectation only applies to view calls.
786 ///
787 /// This method will not override predicates set by [`predicate`] and
788 /// similar methods.
789 ///
790 /// See also [`Contract::expect_call`].
791 ///
792 /// [`predicate`]: Expectation::predicate
793 pub fn allow_calls(self, allow_calls: bool) -> Self {
794 self.transport.allow_calls::<P, R>(
795 self.address,
796 self.signature,
797 self.index,
798 self.generation,
799 allow_calls,
800 );
801 self
802 }
803
804 /// Indicates that this expectation only applies to transactions.
805 ///
806 /// This method will not override predicates set by [`predicate`] and
807 /// similar methods.
808 ///
809 /// See also [`Contract::expect_transaction`].
810 ///
811 /// [`predicate`]: Expectation::predicate
812 pub fn allow_transactions(self, allow_transactions: bool) -> Self {
813 self.transport.allow_transactions::<P, R>(
814 self.address,
815 self.signature,
816 self.index,
817 self.generation,
818 allow_transactions,
819 );
820 self
821 }
822
823 /// Sets return value of the method.
824 ///
825 /// By default, call to this expectation will result in solidity's default
826 /// value for the method's return type. This method allows specifying
827 /// a custom return value.
828 ///
829 /// This method will overwrite any return value or callback
830 /// that was set before.
831 pub fn returns(self, returns: R) -> Self {
832 self.transport.returns::<P, R>(
833 self.address,
834 self.signature,
835 self.index,
836 self.generation,
837 returns,
838 );
839 self
840 }
841
842 /// Sets callback function that will be used to calculate return value
843 /// of the method. This function accepts a tuple of method's arguments
844 /// and returns method's result or [`Err`] if transaction
845 /// should be reverted.
846 ///
847 /// A callback set by this method will be called even if its return value
848 /// is unused, such as when processing a transaction. This means that
849 /// callback can be used to further check method's parameters, perform
850 /// asserts and invoke other logic.
851 ///
852 /// This method will overwrite any return value or callback
853 /// that was set before.
854 ///
855 /// See [`returns`] for more info.
856 ///
857 /// [`returns`]: Expectation::returns
858 pub fn returns_fn(self, returns: impl Fn(P) -> Result<R, String> + Send + 'static) -> Self {
859 self.transport.returns_fn::<P, R>(
860 self.address,
861 self.signature,
862 self.index,
863 self.generation,
864 Box::new(returns),
865 );
866 self
867 }
868
869 /// Sets callback function that will be used to calculate return value
870 /// of the method. This function accepts a [call context] and a tuple
871 /// of method's arguments and returns method's result or [`Err`]
872 /// if transaction should be reverted.
873 ///
874 /// A callback set by this method will be called even if its return value
875 /// is unused, such as when processing a transaction. This means that
876 /// callback can be used to further check method's parameters, perform
877 /// asserts and invoke other logic.
878 ///
879 /// This method will overwrite any return value or callback
880 /// that was set before.
881 ///
882 /// See [`returns`] for more info.
883 ///
884 /// [call context]: CallContext
885 /// [`returns`]: Expectation::returns
886 pub fn returns_fn_ctx(
887 self,
888 returns: impl Fn(&CallContext, P) -> Result<R, String> + Send + 'static,
889 ) -> Self {
890 self.transport.returns_fn_ctx::<P, R>(
891 self.address,
892 self.signature,
893 self.index,
894 self.generation,
895 Box::new(returns),
896 );
897 self
898 }
899
900 /// Sets return value of the method to an error, meaning that calls to this
901 /// expectation result in reverted transaction.
902 ///
903 /// This method will overwrite any return value or callback
904 /// that was set before.
905 ///
906 /// See [`returns`] for more info.
907 ///
908 /// [`returns`]: Expectation::returns
909 pub fn returns_error(self, error: String) -> Self {
910 self.transport.returns_error::<P, R>(
911 self.address,
912 self.signature,
913 self.index,
914 self.generation,
915 error,
916 );
917 self
918 }
919
920 /// Sets return value of the method to a default value for its solidity type.
921 /// See [`returns`] for more info.
922 ///
923 /// This method will overwrite any return value or callback
924 /// that was set before.
925 ///
926 /// Note that this method doesn't use [`Default`] trait for `R`. Instead,
927 /// it constructs default value according to solidity rules.
928 ///
929 /// [`returns`]: Expectation::returns
930 pub fn returns_default(self) -> Self {
931 self.transport.returns_default::<P, R>(
932 self.address,
933 self.signature,
934 self.index,
935 self.generation,
936 );
937 self
938 }
939}
940
941/// Information about method call that's being processed.
942pub struct CallContext {
943 /// If `true`, this is a view call, otherwise this is a transaction.
944 pub is_view_call: bool,
945
946 /// Account that issued a view call or a transaction.
947 ///
948 /// Can be zero in case of a view call.
949 pub from: Address,
950
951 /// Address of the current contract.
952 pub to: Address,
953
954 /// Current nonce of the account that issued a view call or a transaction.
955 pub nonce: U256,
956
957 /// Maximum gas amount that this operation is allowed to spend.
958 ///
959 /// Mock node does not simulate gas consumption, so this value does not
960 /// affect anything if you don't check it in your test code.
961 pub gas: U256,
962
963 /// Gas price for this view call or transaction.
964 ///
965 /// Mock node does not simulate gas consumption, so this value does not
966 /// affect anything if you don't check it in your test code.
967 pub gas_price: U256,
968
969 /// Amount of ETH that's transferred with the call.
970 ///
971 /// This value is only non-zero if the method is payable.
972 pub value: U256,
973}