ic_test/
lib.rs

1//! # ic-test
2//!
3//! **ic-test** is a command-line tool that helps to set up and manage Rust canister tests on the Internet Computer (IC) using.
4//! It makes it easier to create a test project and includes the basic files and setup needed for both IC canisters and optionally EVM (Ethereum Virtual Machine) smart contracts.
5//!
6//! The tool reads the `dfx.json` (must exist) and the `foundry.toml` (may exist) files in order to build the test environment automatically. It uses `pocket-ic` and `alloy` (Foundry) to run tests.
7//! The generated code and helpers provide:
8//!
9//! - A simple way to start a test project.
10//! - A single, easy-to-use interface for testing both IC and EVM parts.  
11//! - Type checking and auto-completion support.
12//! - Easy functions for deploying and calling canisters or contracts.
13//!
14//! For more information, see the [ic-test Book](https://wasm-forge.github.io/ic-test/).
15//!
16//! ## Overview
17//!
18//! **ic-test** will:
19//!
20//! - Read `dfx.json` to get canister details.  
21//! - Read `foundry.toml` to get contract details.  
22//! - Generate Rust types from Candid (`.did`) files.  
23//! - Generate contract interfaces from Solidity (`.sol`) files.  
24//! - Provide API to work with `.wasm` canisters and `.json` contract files in tests.
25//!
26//! ## Requirements
27//!
28//! - [Rust](https://www.rust-lang.org/tools/install)
29//! - [DFX](https://internetcomputer.org/docs/building-apps/getting-started/install#installing-dfx-via-dfxvm) – to build and locally deploy canisters.
30//! - [Foundry](https://book.getfoundry.sh/getting-started/installation) – optional, if you want to test EVM contract's interaction with canisters.
31//!
32//! ## Installation
33//!
34//! ```bash
35//! cargo install ic-test
36//! ```
37//!
38//! ## Tool usage
39//!
40//! ```bash
41//! ic-test <COMMAND> [OPTIONS]
42//! ```
43//!
44//! Without arguments it starts in interactive mode to create a new test project. If an `ic-test.json` config file exists already, the "update" mode will regenerate the existing test project bindings.
45//!
46//! ### Create a new test project
47//!
48//! ```bash
49//! ic-test new tests
50//! ```
51//!
52//! - Creates a new test project in the `tests` folder.
53//! - Looks for canisters and contracts, generates API bindings and a sample test.
54//! - Generates an `ic-test.json` configuration file.
55//! - Fails if the `tests` folder already exists, the user would need to choose a different name.
56//!
57//! ### Update/regenerate an existing test project
58//!
59//! ```bash
60//! ic-test update
61//! ```
62//!
63//! Regenerates bindings using the configuration in `ic-test.json`.
64//!
65//! ### Examples
66//!
67//! For other examples, see <https://github.com/wasm-forge/ic-test-examples>.
68
69#[cfg(feature = "evm")]
70use icp::http_outcalls::handle_http_outcalls;
71#[cfg(feature = "evm")]
72use std::sync::Arc;
73#[cfg(feature = "evm")]
74use tokio::task;
75
76use candid::{decode_one, encode_one, CandidType};
77
78use serde::Deserialize;
79
80mod icp;
81
82#[cfg(feature = "evm")]
83mod evm;
84#[cfg(feature = "evm")]
85pub use crate::evm::{Evm, EvmUser};
86
87pub use crate::{
88    icp::caller::{CallBuilder, CallError, CallMode, Caller},
89    icp::deployer::{DeployBuilder, DeployError, DeployMode, Deployer},
90    icp::user::IcpUser,
91    icp::Icp,
92};
93
94/// Helper structure combining test environments
95pub struct IcpTest {
96    /// Internet Computer environment for canister interactions.
97    pub icp: Icp,
98
99    /// EVM testing environment for the EVM start contract interactions.
100    #[cfg(feature = "evm")]
101    pub evm: Evm,
102}
103
104impl IcpTest {
105    /// Create a new `IcpTest` instance.
106    ///
107    /// Initializes the IC environment and, if the `evm` feature is enabled,
108    /// also spawns a background task to handle EVM outcalls via Pocket-IC.
109    pub async fn new() -> Self {
110        let result = Self {
111            icp: Icp::new().await,
112            #[cfg(feature = "evm")]
113            evm: Evm::new(),
114        };
115
116        #[cfg(feature = "evm")]
117        {
118            let pic = Arc::downgrade(&result.icp.pic);
119
120            let urls: Vec<String> = vec![
121                "127.0.0.1:8545",
122                "localhost:8545",
123                "eth.llamarpc.com",
124                "sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001",
125                "rpc.holesky.ethpandaops.io",
126                "rpc.hoodi.ethpandaops.io",
127                "mainnet.optimism.io",
128                "sepolia.optimism.io",
129                "arb1.arbitrum.io/rpc",
130                "sepolia-rollup.arbitrum.io/rpc",
131                "nova.arbitrum.io/rpc",
132                "polygon-rpc.com",
133                "rpc-amoy.polygon.technology",
134                "api.avax.network/ext/bc/C/rpc",
135                "api.avax-test.network/ext/bc/C/rpc",
136                "bsc-dataseed1.binance.org",
137                "rpc.ankr.com/bsc_testnet_chapel",
138                "rpc.gnosischain.com",
139                "rpc.api.moonbeam.network",
140                "rpc.api.moonriver.moonbeam.network",
141                "rpc.testnet.moonbeam.network",
142                "sepolia.base.org",
143                "mainnet.base.org",
144                "sepolia.blast.io",
145                "rpc.blast.io",
146                "rpc.ankr.com/fantom",
147                "rpc.ankr.com/fantom_testnet",
148                "rpc.frax.com",
149                "rpc.testnet.frax.com",
150                "bartio.rpc.berachain.com",
151                "flare-api.flare.network/ext/C/rpc",
152                "coston2-api.flare.network/ext/C/rpc",
153                "mode.drpc.org",
154                "sepolia.mode.network",
155                "zora.drpc.org",
156                "sepolia.rpc.zora.energy",
157                "racemainnet.io",
158                "metall2.drpc.org",
159                "testnet.rpc.metall2.com",
160                "rpc.zero.thebinaryholdings.com",
161                "rpc.orderly.network",
162                "testnet-rpc.orderly.org",
163            ]
164            .into_iter()
165            .map(String::from)
166            .collect();
167
168            task::spawn(handle_http_outcalls(pic, result.evm.rpc_url(), urls));
169        }
170
171        result
172    }
173
174    /// Advance both the IC and EVM environments.
175    ///
176    /// - For IC, triggers a single tick cycle (e.g., canister heartbeat and timer).
177    /// - For EVM (if enabled), mines a new block.
178    pub async fn tick(&self) {
179        self.icp.tick().await;
180        #[cfg(feature = "evm")]
181        self.evm.mine_block().await;
182    }
183}
184
185/// Utility function to convert between types via Candid encoding/decoding.
186pub fn convert<F, T>(value: F) -> T
187where
188    F: CandidType,
189    T: for<'a> Deserialize<'a> + CandidType,
190{
191    decode_one(&encode_one(&value).unwrap()).unwrap()
192}