Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Marks the `pallet-revive` host function `account_id` stable - [#2578](https://github.com/use-ink/ink/pull/2578)
- Stabilize `is_contract` - [#2654](https://github.com/use-ink/ink/pull/2654)
- Extract `sandbox` from `ink_e2e` into a new `ink_sandbox` crate - [#2659](https://github.com/use-ink/ink/pull/2659)

### Fixed
- Fix decoding of `HostFn::minimum_balance` return value - [#2656](https://github.com/use-ink/ink/pull/2656)
Expand Down
14 changes: 7 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ members = [
"crates/allocator",
"crates/e2e",
"crates/e2e/macro",
"crates/e2e/sandbox",
"crates/engine",
"crates/env",
"crates/ink",
Expand All @@ -15,6 +14,7 @@ members = [
"crates/revive-types",
"crates/prelude",
"crates/primitives",
"crates/sandbox",
"crates/storage",
"crates/storage/traits"
]
Expand Down
18 changes: 0 additions & 18 deletions crates/e2e/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,17 @@ ink = { workspace = true, default-features = true }
ink_env = { workspace = true, default-features = true }
ink_primitives = { workspace = true, default-features = true }
ink_revive_types = { workspace = true, default-features = true }
ink_sandbox = { version = "=6.0.0-alpha.4", path = "./sandbox", optional = true }

cargo_metadata = { workspace = true }
contract-build = { workspace = true }
funty = { workspace = true }
impl-serde = { workspace = true }
jsonrpsee = { workspace = true, features = ["ws-client"] }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tokio = { workspace = true, features = ["rt-multi-thread"] }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
scale = { workspace = true }
subxt = { workspace = true }
subxt-metadata = { workspace = true, optional = true }
subxt-signer = { workspace = true, features = ["subxt", "sr25519", "unstable-eth"] }
thiserror = { workspace = true }
which = { workspace = true }
Expand All @@ -49,35 +45,21 @@ sp-weights = { workspace = true }
regex = "1.11.2"
itertools = "0.14.0"

sp-io = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "a71ec19a94702ea71767ba5ac97603ea6c6305c1", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] }
sp-runtime-interface = { git = "https://github.com/use-ink/polkadot-sdk.git", rev = "a71ec19a94702ea71767ba5ac97603ea6c6305c1", default-features = false, features = ["disable_target_static_assertions"] }

[dev-dependencies]
# Required for the doctest of `MessageBuilder::call`
scale-info = { workspace = true, features = ["derive"] }

[features]
default = [ "std" ]
std = [
"impl-serde/std",
"ink_e2e_macro/std",
"scale-info/std",
"scale/std",
"serde/std",
"sp-core/std",
"sp-keyring/std",
"sp-runtime/std",
"sp-io/std",
"sp-runtime-interface/std",
"sp-weights/std",
"ink_e2e_macro/std",
"ink_revive_types/std",
"ink_sandbox?/std",
"frame-support/std",
]

sandbox = [
"dep:ink_sandbox",
"dep:subxt-metadata",
"ink_e2e_macro/sandbox",
]
5 changes: 2 additions & 3 deletions crates/e2e/macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,16 @@ darling = { workspace = true }
ink_ir = { workspace = true, default-features = true }
derive_more = { workspace = true, features = ["from"] }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
serde_json = { workspace = true }
syn = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
tracing = { workspace = true }

[dev-dependencies]
ink = { path = "../../ink" }
ink_e2e = { path = "../", features = ["sandbox"] }
ink_e2e = { path = "../" }
ink_sandbox = { path = "../../sandbox" }
temp-env = "0.3.6"

[features]
std = ["derive_more/std"]
sandbox = []
16 changes: 10 additions & 6 deletions crates/e2e/macro/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ impl InkE2ETest {
Backend::Node(node_config) => {
build_full_client(&environment, exec_build_contracts, node_config)
}
#[cfg(any(test, feature = "sandbox"))]
Backend::RuntimeOnly(runtime) => {
build_runtime_client(exec_build_contracts, runtime.into())
Backend::RuntimeOnly(args) => {
let runtime: syn::Path = args.runtime_path();
let client: syn::Path = args.client_path();
build_runtime_client(exec_build_contracts, runtime, client)
}
};

Expand Down Expand Up @@ -170,10 +171,13 @@ fn build_full_client(
}
}

#[cfg(any(test, feature = "sandbox"))]
fn build_runtime_client(contracts: TokenStream2, runtime: syn::Path) -> TokenStream2 {
fn build_runtime_client(
contracts: TokenStream2,
runtime: syn::Path,
client: syn::Path,
) -> TokenStream2 {
quote! {
let contracts = #contracts;
let mut client = ::ink_e2e::SandboxClient::<_, #runtime>::new(contracts);
let mut client = #client::<_, #runtime>::new(contracts);
}
}
53 changes: 32 additions & 21 deletions crates/e2e/macro/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ pub enum Backend {
///
/// This runs a runtime emulator within `TestExternalities`
/// the same process as the test.
#[cfg(any(test, feature = "sandbox"))]
RuntimeOnly(RuntimeOnly),
}

Expand Down Expand Up @@ -64,22 +63,21 @@ impl Node {
}

/// The runtime emulator that should be used within `TestExternalities`
#[cfg(any(test, feature = "sandbox"))]
#[derive(Clone, Eq, PartialEq, Debug, darling::FromMeta)]
pub enum RuntimeOnly {
#[darling(word)]
#[darling(skip)]
Default,
Sandbox(syn::Path),
pub struct RuntimeOnly {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a bit tricky: since we don’t want to import ink_sandbox in this crate, we can’t provide it as a default. It always has to be specified explicitly by the user.

Copy link
Collaborator

@davidsemakula davidsemakula Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ink_e2e machinery already has a good look at the manifest (or more specifically cargo metadata) to do stuff like compiling the root contract and it's contract dependencies (if any) (see here and here).
So we can do something there to bring back defaults (after this PR), while also providing user friendly errors when ink_sandbox is missing.
I'm already planning to do something similar for when the user runs cargo test (not ABI aware) instead of cargo contract test (ABI aware), so I can look at this as well while doing that.

/// The sandbox runtime type (e.g., `ink_sandbox::DefaultSandbox`)
pub sandbox: syn::Path,
/// The client type implementing the backend traits (e.g.,
/// `ink_sandbox::SandboxClient`)
pub client: syn::Path,
}

#[cfg(any(test, feature = "sandbox"))]
impl From<RuntimeOnly> for syn::Path {
fn from(value: RuntimeOnly) -> Self {
match value {
RuntimeOnly::Default => syn::parse_quote! { ::ink_e2e::DefaultSandbox },
RuntimeOnly::Sandbox(path) => path,
}
impl RuntimeOnly {
pub fn runtime_path(&self) -> syn::Path {
self.sandbox.clone()
}
pub fn client_path(&self) -> syn::Path {
self.client.clone()
}
}

Expand Down Expand Up @@ -146,7 +144,7 @@ mod tests {
fn config_works_backend_runtime_only() {
let input = quote! {
environment = crate::CustomEnvironment,
backend(runtime_only),
backend(runtime_only(sandbox = ::ink_sandbox::DefaultSandbox, client = ::ink_sandbox::SandboxClient)),
};
let config =
E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();
Expand All @@ -156,7 +154,13 @@ mod tests {
Some(syn::parse_quote! { crate::CustomEnvironment })
);

assert_eq!(config.backend(), Backend::RuntimeOnly(RuntimeOnly::Default));
assert_eq!(
config.backend(),
Backend::RuntimeOnly(RuntimeOnly {
sandbox: syn::parse_quote! { ::ink_sandbox::DefaultSandbox },
client: syn::parse_quote! { ::ink_sandbox::SandboxClient },
})
);
}

#[test]
Expand All @@ -168,22 +172,29 @@ mod tests {
let config =
E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();

assert_eq!(config.backend(), Backend::RuntimeOnly(RuntimeOnly::Default));
assert_eq!(
config.backend(),
Backend::RuntimeOnly(RuntimeOnly {
sandbox: syn::parse_quote! { ::ink_sandbox::DefaultSandbox },
client: syn::parse_quote! { ::ink_sandbox::SandboxClient },
})
);
}

#[test]
fn config_works_runtime_only_with_custom_backend() {
let input = quote! {
backend(runtime_only(sandbox = ::ink_e2e::DefaultSandbox)),
backend(runtime_only(sandbox = ::ink_sandbox::DefaultSandbox, client = ::ink_sandbox::SandboxClient)),
};
let config =
E2EConfig::from_list(&NestedMeta::parse_meta_list(input).unwrap()).unwrap();

assert_eq!(
config.backend(),
Backend::RuntimeOnly(RuntimeOnly::Sandbox(
syn::parse_quote! { ::ink_e2e::DefaultSandbox }
))
Backend::RuntimeOnly(RuntimeOnly {
sandbox: syn::parse_quote! { ::ink_sandbox::DefaultSandbox },
client: syn::parse_quote! { ::ink_sandbox::SandboxClient },
})
);
}

Expand Down
2 changes: 1 addition & 1 deletion crates/e2e/macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ use syn::Result;
/// ```
/// type E2EResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
///
/// #[ink_e2e::test(backend(runtime_only))]
/// #[ink_sandbox::test(backend(runtime_only(sandbox = ink_sandbox::DefaultSandbox, client = ink_sandbox::SandboxClient)))]
/// async fn runtime_call_works() -> E2EResult<()> {
/// // ...
/// }
Expand Down
26 changes: 0 additions & 26 deletions crates/e2e/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,29 +70,3 @@ where
<Self as fmt::Debug>::fmt(self, f)
}
}

/// Dummy error type for sandbox_client
#[derive(Debug, thiserror::Error)]
pub struct SandboxErr {
msg: String,
}

impl SandboxErr {
/// Create a new `SandboxErr` with the given message.
#[allow(dead_code)]
pub fn new(msg: String) -> Self {
Self { msg }
}
}

impl From<String> for SandboxErr {
fn from(msg: String) -> Self {
Self { msg }
}
}

impl fmt::Display for SandboxErr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SandboxErr: {}", self.msg)
}
}
23 changes: 12 additions & 11 deletions crates/e2e/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ mod contract_results;
mod error;
pub mod events;
mod node_proc;
#[cfg(feature = "sandbox")]
mod sandbox_client;
mod subxt_client;
mod xts;

Expand All @@ -44,10 +42,21 @@ pub use backend_calls::{
CallBuilder,
InstantiateBuilder,
};
pub use client_utils::ContractsRegistry;
pub use builders::{
CreateBuilderPartial,
constructor_exec_input,
};
pub use client_utils::{
ContractsRegistry,
code_hash,
salt,
};
pub use contract_results::{
BareInstantiationResult,
CallDryRunResult,
CallResult,
ContractExecResultFor,
ContractResult,
InstantiateDryRunResult,
InstantiationResult,
UploadResult,
Expand All @@ -58,11 +67,6 @@ pub use node_proc::{
TestNodeProcess,
TestNodeProcessBuilder,
};
#[cfg(feature = "sandbox")]
pub use sandbox_client::{
Client as SandboxClient,
preset,
};
pub use sp_keyring::Sr25519Keyring;
pub use subxt::{
self,
Expand All @@ -85,9 +89,6 @@ pub use tokio;
pub use tracing;
pub use tracing_subscriber;

#[cfg(feature = "sandbox")]
pub use ink_sandbox::DefaultSandbox;

use ink::codegen::ContractCallBuilder;
use ink_env::{
ContractEnv,
Expand Down
Loading
Loading