diff --git a/Cargo.lock b/Cargo.lock
index 7baf68a..a346fcd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4666,10 +4666,14 @@ dependencies = [
"frame-support",
"frame-system",
"ink_sandbox",
+ "pallet-assets",
+ "pallet-balances",
"pallet-contracts",
+ "pallet-timestamp",
"parity-scale-codec",
"pop-api",
"pop-runtime-devnet",
+ "scale-info",
"sp-io",
]
@@ -6475,9 +6479,9 @@ dependencies = [
[[package]]
name = "subtle"
-version = "2.6.1"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
diff --git a/Cargo.toml b/Cargo.toml
index 1765c11..b2f579c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,15 +1,13 @@
[workspace]
resolver = "2"
members = [
- "crates/ink-sandbox",
- "crates/drink/drink",
- "crates/drink/drink-cli",
- "crates/drink/drink/test-macro",
- "crates/pop-drink",
-]
-exclude = [
- "crates/drink/examples",
+ "crates/ink-sandbox",
+ "crates/drink/drink",
+ "crates/drink/drink-cli",
+ "crates/drink/drink/test-macro",
+ "crates/pop-drink",
]
+exclude = ["crates/drink/examples"]
[workspace.package]
edition = "2021"
@@ -32,7 +30,7 @@ proc-macro2 = { version = "1" }
quote = { version = "1" }
ratatui = { version = "0.21.0" }
scale = { package = "parity-scale-codec", version = "3.6.9", features = [
- "derive",
+ "derive",
] }
scale-info = { version = "2.10.0" }
serde_json = { version = "1.0" }
@@ -56,6 +54,6 @@ sp-runtime-interface = { version = "28.0.0", features = ["std"] }
# Local
drink = { path = "crates/drink/drink" }
ink_sandbox = { path = "crates/ink-sandbox" }
+pop-api = { path = "../pop-node/pop-api" }
pop-drink = { path = "crates/pop-drink" }
pop-runtime-devnet = { path = "../pop-node/runtime/devnet" }
-pop-api = { path = "../pop-node/pop-api" }
diff --git a/README.md b/README.md
index 2e17b48..4116a1a 100644
--- a/README.md
+++ b/README.md
@@ -1,68 +1,85 @@
-
Pop DRink! (forked from DRink!)
- Dechained Ready-to-play ink! playground
+
+
Pop DRink!
-> [!IMPORTANT]
-> This repository is customized and maintained for Pop API contract end-to-end testing.
->
-> Quasi tests must be run with `cargo test --release`.
+
-# Pop Sandbox
+[![Twitter URL](https://img.shields.io/twitter/follow/Pop?style=social)](https://x.com/onpopio/)
+[![Twitter URL](https://img.shields.io/twitter/follow/R0GUE?style=social)](https://twitter.com/gor0gue)
+[![Telegram](https://img.shields.io/badge/Telegram-gray?logo=telegram)](https://t.me/onpopio)
-Implementation of the [`pop_drink::Sandbox`](https://github.com/r0gue-io/pop-drink) struct for the Pop Network runtimes (located in `pop-node/runtime`) required for the quasi testing with `drink`.
+Forked version of [inkdevhub/drink](https://github.com/inkdevhub/drink) for E2E testing of smart contract using the [Pop API](https://github.com/r0gue-io/pop-node/tree/main/pop-api) on Pop Network.
-In the context of quasi-testing with pop-drink, a sandbox refers to an isolated runtime environment that simulates the behavior of a full node, without requiring an actual node. It can emulate key processes (where runtime `pallets` are involved) such as block initialization, execution, and block finalization.
+
+
+## Overview
+
+About the repository folder structure:
+
+- [pop-drink](/crates/pop-drink): Library for testing contracts deployed on Pop Network using drink.
+- [drink](/crates/drink/drink): drink is a toolbox for ink! developers to test contracts in a sandbox environment.
+- [ink-sandbox](/crates/ink-sandbox): Creates a sandbox environment for a given runtime, without having to run a node.
+- [examples](/crates/drink/examples): A collection of example contracts tested with drink.
+- [drink-cli](/crates/drink/drink-cli): Simple command line tool to help you play with your local contracts in a convenient way.
## Getting Started
-### Installation
+Add `pop-drink` crate to your contract `Cargo.toml`:
```toml
-pop_drink = { version = "1.0.0", package = "pop-drink" }
+drink = { version = "1.0.0", package = "pop-drink" }
```
-### Import Sandbox for the specific runtime
-
-- For `devnet` runtime
-
-Implementation of the sandbox runtime environment for `devnet` runtime located in `pop-node/runtime/devnet`
+Set up your pop-drink test environment and write your first test.
```rs
-use pop_sandbox::DevnetSandbox;
-```
+use drink::{
+ devnet::{AccountId, Balance, Runtime},
+ TestExternalities,
+ deploy, call,
+};
-- For `testnet` runtime
-Implementation of the sandbox runtime environment for `testnet` runtime located in `pop-node/runtime/testnet`
+// Builds your contract(s).
+#[drink::contract_bundle_provider]
+enum BundleProvider {}
-```rs
-use pop_sandbox::TestnetSandbox;
-```
+/// Sandbox environment for Pop Devnet Runtime.
+pub struct Pop {
+ ext: TestExternalities,
+}
-- For `mainnet` runtime
+// Initialising genesis state.
+impl Default for Pop {
+ fn default() -> Self {
+ let balances: Vec<(AccountId, Balance)> = vec![(ALICE, 100u128)];
+ let ext = BlockBuilder::::new_ext(balances);
+ Self { ext }
+ }
+}
-Implementation of the sandbox runtime environment for `mainnet` runtime located in `pop-node/runtime/mainnet`
+// Implement core functionalities for the `Pop` sandbox.
+drink::impl_sandbox!(Pop, Runtime, ALICE);
-```rs
-use pop_sandbox::MainnetSandbox;
+// Write your first pop-drink test!
+#[drink::test(sandbox = Pop)]
+fn test(mut session: Session) { ... }
```
-### Setup test environment for your contract
+Important: run your tests with `--release`
+```
+cargo test --release
+```
-Below is an example for the contract testing with `pop_drink` and `pop_sandbox` for `devnet` environment using `DevnetSandbox`.
+## Learn more
-```rs
-use pop_drink::session::Session;
-use pop_sandbox::DevnetSandbox as Sandbox;
+Dive into the ["quick start with drink!"](/crates/drink/examples/quick-start-with-drink/README.md) to learn more about drink. Also check out the other examples provided by drink.
-#[drink::contract_bundle_provider]
-enum BundleProvider {}
+If you are interested in learning more about testing contracts using the Pop API, check out the [example contracts](https://github.com/r0gue-io/pop-node/tree/main/pop-api/examples) tested with pop-drink!
-#[drink::test(sandbox = Sandbox)]
-fn test(mut session: Session) {
- // Your test case
-}
-```
+## Support
+
+Be part of our passionate community of Web3 builders. [Join our Telegram](https://t.me/onpopio)!
-## Examples
+Feel free to contribute to `drink` or `pop-drink` to help improve testing ink! smart contracts!
-Please find more examples of `pop_drink` tests in the [`pop_drink/examples`](https://github.com/r0gue-io/pop-drink/tree/main/examples).
+For any questions related to ink! you can also go to [Polkadot Stack Exchange](https://polkadot.stackexchange.com/) or ask the [ink! community](https://t.me/inkathon/1).
\ No newline at end of file
diff --git a/crates/drink/drink/src/lib.rs b/crates/drink/drink/src/lib.rs
index b029000..8b94a05 100644
--- a/crates/drink/drink/src/lib.rs
+++ b/crates/drink/drink/src/lib.rs
@@ -12,9 +12,9 @@ pub use drink_test_macro::{contract_bundle_provider, test};
pub use errors::Error;
pub use frame_support;
pub use ink_sandbox::{
- self, api as sandbox_api, create_sandbox, impl_sandbox,
- pallet_balances, pallet_contracts, pallet_timestamp, sp_externalities, AccountId32,
- DispatchError, Sandbox, Ss58Codec, Weight,
+ self, api as sandbox_api, create_sandbox, impl_sandbox, pallet_assets, pallet_balances,
+ pallet_contracts, pallet_timestamp, sp_externalities, AccountId32, DispatchError, Sandbox,
+ Ss58Codec, Weight,
};
#[cfg(feature = "session")]
pub use session::mock::{mock_message, ContractMock, MessageMock, MockedCallResult, Selector};
@@ -28,4 +28,4 @@ pub mod minimal {
// create_sandbox!(MinimalSandbox);
create_sandbox!(MinimalSandbox, (), crate::pallet_contracts_debugging::DrinkDebug);
-}
\ No newline at end of file
+}
diff --git a/crates/pop-drink/Cargo.toml b/crates/pop-drink/Cargo.toml
index a70b5fa..5c7f26c 100644
--- a/crates/pop-drink/Cargo.toml
+++ b/crates/pop-drink/Cargo.toml
@@ -6,10 +6,16 @@ edition = "2021"
[dependencies]
drink.workspace = true
ink_sandbox.workspace = true
-pop-runtime-devnet.workspace = true
pallet-contracts.workspace = true
+pop-runtime-devnet.workspace = true
frame-system.workspace = true
frame-support.workspace = true
sp-io.workspace = true
scale.workspace = true
pop-api.workspace = true
+
+[dev-dependencies]
+scale-info = { workspace = true, features = ["derive"] }
+pallet-assets.workspace = true
+pallet-balances.workspace = true
+pallet-timestamp.workspace = true
diff --git a/crates/pop-drink/src/error.rs b/crates/pop-drink/src/error.rs
new file mode 100644
index 0000000..8d2c528
--- /dev/null
+++ b/crates/pop-drink/src/error.rs
@@ -0,0 +1,261 @@
+//! The error type and utilities for testing smart contracts using the Pop API.
+
+use std::fmt::Debug;
+
+pub use drink::{
+ pallet_assets::Error as AssetsError, pallet_balances::Error as BalancesError,
+ pallet_contracts::Error as ContractsError,
+};
+use scale::{Decode, Encode};
+
+/// A simplified error type representing errors from the runtime and its modules.
+///
+/// This type can be used to assert to an error that holds a [status code](https://github.com/r0gue-io/pop-node/blob/main/pop-api/src/lib.rs#L33).
+/// The status code is returned by the Pop API and represents a runtime error.
+///
+/// # Generic Parameters:
+/// - `ApiError`: The pop api error type.
+/// - `ModuleError`: The error type for specific runtime modules.
+/// - `MODULE_INDEX`: Index of the variant `Error::Module`.
+#[derive(Encode, Decode, Debug)]
+pub enum Error
+where
+ ApiError: Decode + Encode + Debug + From + Into,
+ ModuleError: Decode + Encode + Debug,
+{
+ /// An error not related to any specific module.
+ Raw(ApiError),
+ /// An error originating from a runtime module.
+ Module(ModuleError),
+}
+
+impl From>
+ for u32
+where
+ ApiError: Decode + Encode + Debug + From + Into,
+ ModuleError: Decode + Encode + Debug,
+{
+ /// Converts an `Error` to a `u32` status code.
+ fn from(error: Error) -> Self {
+ match error {
+ Error::Raw(error) => decode::(&error.encode()),
+ Error::Module(error) => {
+ let mut encoded = error.encode();
+ encoded.insert(0, MODULE_INDEX);
+ encoded.resize(4, 0);
+ decode::(&encoded)
+ },
+ }
+ .into()
+ }
+}
+
+impl From
+ for Error
+where
+ ApiError: Decode + Encode + Debug + From + Into,
+ ModuleError: Decode + Encode + Debug,
+{
+ /// Converts a `u32` into an `Error`.
+ ///
+ /// If the status code represents a module error it converts it into `Error::Module` in stead
+ /// of `Error::Raw`.
+ fn from(value: u32) -> Self {
+ let error = ApiError::from(value);
+ let encoded = error.encode();
+ if encoded[0] == MODULE_INDEX {
+ let (index, module_error) = (encoded[1], &encoded[2..]);
+ let data = vec![vec![index], module_error.to_vec()].concat();
+ return Error::Module(decode(&data));
+ }
+ Error::Raw(error)
+ }
+}
+
+/// Asserts that a result matches an expected `Error`.
+///
+/// This can be used to assert that a contract execution resulted in a specific runtime error
+/// `Error`. The contract error must be convertible to a `u32` (i.e. the status code received from
+/// the api).
+///
+/// # Example
+///
+/// ## Errors
+///
+/// ```rs
+/// use drink::devnet::{
+/// Assets,
+/// AssetsError::BalanceLow,
+/// v0::{
+/// Arithmetic,
+/// ArithmeticError::Overflow,
+/// BadOrigin
+/// },
+/// };
+/// ```
+///
+/// [`BadOrigin`](https://github.com/r0gue-io/pop-node/blob/main/primitives/src/lib.rs#L36C4-L36C18):
+/// ```rs
+/// Error::Raw(BadOrigin)
+/// ```
+///
+/// [`Arithmetic(Overflow)`](https://github.com/r0gue-io/pop-node/blob/main/primitives/src/lib.rs#L55):
+/// ```rs
+/// Error::Raw(Arithmetic(Overflow))
+/// ```
+///
+/// [`Assets(BalanceLow)`](https://paritytech.github.io/polkadot-sdk/master/pallet_assets/pallet/enum.Error.html#variant.BalanceLow):
+/// ```rs
+/// Error::Module(Assets(BalanceLow))
+/// ```
+///
+/// ## How to use `assert_err` macro.
+///
+/// - Create a custom error type that holds the status code.
+///
+/// ```rs
+/// use pop_api::StatusCode;
+///
+/// /// Custom error in contract.
+/// pub enum CustomError {
+/// ...,
+/// /// Error with status code.
+/// StatusCode(u32),
+/// }
+///
+/// impl From for CustomError {
+/// /// Converts a `StatusCode` (returned by the api) to a `CustomError`.
+/// fn from(value: StatusCode) -> Self {
+/// match value {
+/// ...,
+/// _ => CustomError::StatusCode(value.0),
+/// }
+/// }
+/// }
+///
+/// impl From for u32 {
+/// /// Converts a `CustomError to a `u32`.
+/// //
+/// // Required for the `assert_err` macro to assert to `Error`.
+/// fn from(value: CustomError) -> Self {
+/// match value {
+/// ...,
+/// CustomError::StatusCode(status_code) => status_code,
+/// }
+/// }
+/// }
+///
+/// - Use `assert_err` in a test.
+///
+/// #[drink::test(sandbox = Pop)]
+/// fn test_custom_error(mut session: Session) {
+/// ...
+///
+/// // Call a contract method that returns a `Result<(), CustomError>`.
+/// let result = call::(session, "hello_world", vec![], None);
+///
+/// // Assert the result to the expected error.
+/// assert_err!(result, Error::Raw(BadOrigin)));
+///
+/// // Other assertions:
+/// ...
+/// assert_err!(result, Error::Raw(Arithmetic(Overflow)));
+/// ...
+/// assert_err!(result, Error::Module(Assets(BalanceLow)));
+/// }
+/// ```
+///
+/// # Parameters:
+/// - `result`: The result which contains the custom error type.
+/// - `error`: The expected error.
+#[macro_export]
+macro_rules! assert_err {
+ ($result:expr, $error:expr $(,)?) => {
+ $crate::error::assert_err_inner::<_, _, _>($result, $error);
+ };
+}
+
+#[track_caller]
+#[allow(unused)]
+fn assert_err_inner(result: Result, expected_error: Error)
+where
+ E: Into,
+ Error: From + Into + Debug,
+{
+ let expected_code: u32 = expected_error.into();
+ let expected_error = Error::from(expected_code);
+ if let Err(error) = result {
+ let error_code: u32 = error.into();
+ if error_code != expected_code {
+ panic!(
+ r#"assertion `left == right` failed
+ left: {:?}
+ right: {:?}"#,
+ Error::from(error_code),
+ expected_error
+ );
+ }
+ } else {
+ panic!(
+ r#"assertion `left == right` failed
+ left: Ok()
+ right: {:?}"#,
+ expected_error
+ );
+ }
+}
+
+fn decode(data: &[u8]) -> T {
+ T::decode(&mut &data[..]).expect("Decoding failed")
+}
+
+#[cfg(test)]
+mod test {
+ use pop_api::primitives::v0::Error as ApiError;
+
+ use crate::error::{AssetsError::*, BalancesError::*, *};
+
+ fn test_cases() -> Vec<(Error, ApiError)> {
+ use frame_support::traits::PalletInfoAccess;
+ use pop_api::primitives::{ArithmeticError::*, TokenError::*};
+
+ use crate::mock::RuntimeError::*;
+ vec![
+ (Error::Raw(ApiError::BadOrigin), ApiError::BadOrigin),
+ (Error::Raw(ApiError::Token(BelowMinimum)), ApiError::Token(BelowMinimum)),
+ (Error::Raw(ApiError::Arithmetic(Overflow)), ApiError::Arithmetic(Overflow)),
+ (
+ Error::Module(Assets(BalanceLow)),
+ ApiError::Module { index: crate::mock::Assets::index() as u8, error: [0, 0] },
+ ),
+ (
+ Error::Module(Assets(NoAccount)),
+ ApiError::Module { index: crate::mock::Assets::index() as u8, error: [1, 0] },
+ ),
+ (
+ Error::Module(Balances(VestingBalance)),
+ ApiError::Module { index: crate::mock::Balances::index() as u8, error: [0, 0] },
+ ),
+ (
+ Error::Module(Balances(LiquidityRestrictions)),
+ ApiError::Module { index: crate::mock::Balances::index() as u8, error: [1, 0] },
+ ),
+ ]
+ }
+
+ #[test]
+ fn runtime_error_to_primitives_error_conversion_works() {
+ test_cases().into_iter().for_each(|t| {
+ let runtime_error: u32 = t.0.into();
+ let pop_api_error: u32 = t.1.into();
+ assert_eq!(runtime_error, pop_api_error);
+ });
+ }
+
+ #[test]
+ fn assert_err_works() {
+ test_cases().into_iter().for_each(|t| {
+ crate::assert_err!(Result::<(), pop_api::primitives::v0::Error>::Err(t.1), t.0,);
+ });
+ }
+}
diff --git a/crates/pop-drink/src/lib.rs b/crates/pop-drink/src/lib.rs
index cb7c986..5c20ee4 100644
--- a/crates/pop-drink/src/lib.rs
+++ b/crates/pop-drink/src/lib.rs
@@ -1,95 +1,192 @@
+//! A library for testing smart contracts on Pop Network.
+
pub use drink::*;
+pub use frame_support::{self, assert_ok};
pub use ink_sandbox::api::assets_api::AssetsAPI;
-pub use sp_io::TestExternalities;
-pub use frame_support::{self, sp_runtime::DispatchError, assert_ok};
-pub use session::{error::SessionError, ContractBundle, Session, NO_SALT};
-use scale::Decode;
use ink_sandbox::{AccountIdFor, BalanceFor};
+use scale::Decode;
+pub use session::{error::SessionError, ContractBundle, Session, NO_SALT};
+pub use sp_io::TestExternalities;
+
+/// Error type and utilities for testing contracts using the Pop API.
+pub mod error;
+#[cfg(test)]
+mod mock;
+/// Types and utilities for testing smart contracts interacting with Pop Network Devnet via the pop
+/// api.
pub mod devnet {
- use super::*;
- pub use pop_runtime_devnet::{BuildStorage, Runtime};
+ pub use pop_runtime_devnet::Runtime;
- /// Types used in the pop runtime.
- pub type Balance = BalanceFor;
- pub type AccountId = AccountIdFor;
+ use super::*;
+ pub use crate::error::*;
- /// This is used to resolve type mismatches between the `AccountId` in the quasi testing
- /// environment and the contract environment.
- pub fn account_id_from_slice(s: &AccountId) -> pop_api::primitives::AccountId {
- let account: [u8; 32] = s.clone().into();
- utils::account_id_from_slice(&account)
- }
+ /// Error related utilities for smart contracts using pop api.
+ pub mod error {
+ pub use pop_runtime_devnet::RuntimeError::*;
+
+ pub use crate::error::*;
+
+ pub mod v0 {
+ pub use pop_api::primitives::v0::{self, *};
+
+ /// Error type for writing tests (see `error` module).
+ pub type Error = crate::error::Error;
+ }
+ }
+
+ // Types used in the pop runtime.
+ pub type Balance = BalanceFor;
+ pub type AccountId = AccountIdFor;
+
+ /// Converts an AccountId from Pop's runtime to the account ID used in the contract environment.
+ pub fn account_id_from_slice(s: &AccountId) -> pop_api::primitives::AccountId {
+ let account: [u8; 32] = s.clone().into();
+ super::account_id_from_slice(&account)
+ }
}
-pub mod utils {
- use super::*;
- pub fn deploy
- (
- session: &mut Session,
- bundle: ContractBundle,
- method: &str,
- input: Vec,
- salt: Vec,
- init_value: Option>,
- )
- -> Result, E>
- where
- S: Sandbox,
- S::Runtime: pallet_contracts::Config,
- E: Decode,
- {
- let result = session.deploy_bundle(bundle, method, &input, salt, init_value);
- if result.is_err() {
- let deployment_result = session.record().last_deploy_result().result.clone();
- let error = deployment_result.unwrap().result.data;
- return Err(E::decode(&mut &error[2..]).unwrap());
- }
- Ok(result.unwrap())
- }
+/// Deploy a contract with a given constructor, arguments, salt and an initial value. In
+/// case of success, returns the address of the deployed contract.
+///
+/// # Generic Parameters:
+/// - `S` - Sandbox environment.
+/// - `E` - `Err()` type returned by the contract.
+///
+/// # Parameters:
+/// - `session` - The session for interacting with contracts.
+/// - `bundle` - The contract bundle.
+/// - `method` - The name of the constructor method.
+/// - `input` - The input arguments.
+/// - `salt` - Optional deployment salt.
+/// - `init_value` - Initial balance to transfer during the contract creation. Requires the contract
+/// method to be `payable`.
+///
+/// # Example:
+/// ```rs
+/// #[drink::test(sandbox = Pop)]
+/// fn test_constructor_works(mut session: Session) {
+/// let bundle = BundleProvider::local().unwrap();
+///
+/// // Deploy contract.
+/// //
+/// // `ContractError` is the error type used by the contract.
+/// assert_ok!(deploy(&mut session, bundle, "new", input, salt, init_value));
+/// }
+/// ```
+pub fn deploy(
+ session: &mut Session,
+ bundle: ContractBundle,
+ method: &str,
+ input: Vec,
+ salt: Vec,
+ init_value: Option>,
+) -> Result, E>
+where
+ S: Sandbox,
+ S::Runtime: pallet_contracts::Config,
+ E: Decode,
+{
+ let result = session.deploy_bundle(bundle, method, &input, salt, init_value);
+ if result.is_err() {
+ let deployment_result = session.record().last_deploy_result().result.clone();
+ let error = deployment_result.unwrap().result.data;
+ return Err(E::decode(&mut &error[2..]).unwrap());
+ }
+ Ok(result.unwrap())
+}
- /// Call a contract method and decode the returned data.
- pub fn call(
- session: &mut Session,
- func_name: &str,
- input: Vec,
- endowment: Option>,
- ) -> Result
- where
- S: Sandbox,
- S::Runtime: pallet_contracts::Config,
- O: Decode,
- E: Decode,
- {
- match session.call::(func_name, &input, endowment) {
- // If the call is reverted, decode the error into `PSP22Error`.
- Err(SessionError::CallReverted(error)) =>
- Err(E::decode(&mut &error[2..])
- .unwrap_or_else(|_| panic!("Decoding failed"))),
- // If the call is successful, decode the last returned value.
- Ok(_) => Ok(session
- .record()
- .last_call_return_decoded::()
- .unwrap_or_else(|_| panic!("Expected a return value"))
- .unwrap_or_else(|_| panic!("Decoding failed"))),
- // Catch-all for unexpected results.
- _ => panic!("Expected call to revert or return a value"),
- }
- }
+/// Call a method and decode the returned data.
+///
+/// # Generic Parameters:
+/// - `S` - Sandbox environment.
+/// - `O` - `Ok()` type returned by the contract.
+/// - `E` - `Err()` type returned by the contract.
+///
+/// # Parameters:
+/// - `session` - The session for interacting with contracts.
+/// - `func_name`: The name of the contract method.
+/// - `input` - The input arguments.
+/// - `init_value` - Balance to transfer during the call. Requires the contract method to be
+/// `payable`.
+///
+/// # Example:
+/// ```rs
+/// #[drink::test(sandbox = Pop)]
+/// fn call_works(mut session: Session) {
+/// let bundle = BundleProvider::local().unwrap();
+/// assert_ok!(deploy(&mut session, bundle, "new", input, salt, init_value));
+///
+/// // Call contract.
+/// //
+/// // `()` is the successful result type used by the contract.
+/// // `ContractError` is the error type used by the contract.
+/// call::(
+/// session,
+/// "transfer",
+/// input,
+/// init_value,
+/// )
+/// }
+/// ```
+pub fn call(
+ session: &mut Session,
+ func_name: &str,
+ input: Vec,
+ endowment: Option>,
+) -> Result
+where
+ S: Sandbox,
+ S::Runtime: pallet_contracts::Config,
+ O: Decode,
+ E: Decode,
+{
+ match session.call::(func_name, &input, endowment) {
+ // If the call is reverted, decode the error into the specified error type.
+ Err(SessionError::CallReverted(error)) =>
+ Err(E::decode(&mut &error[2..]).expect("Decoding failed")),
+ // If the call is successful, decode the last returned value.
+ Ok(_) => Ok(session
+ .record()
+ .last_call_return_decoded::()
+ .expect("Expected a return value")
+ .expect("Decoding failed")),
+ // Catch-all for unexpected results.
+ _ => panic!("Expected call to revert or return a value"),
+ }
+}
- /// This is used to resolve type mismatches between the `AccountId` in the quasi testing
- /// environment and the contract environment.
- pub fn account_id_from_slice(s: &[u8; 32]) -> pop_api::primitives::AccountId {
- pop_api::primitives::AccountId::decode(&mut &s[..]).expect("Should be decoded to AccountId")
- }
+/// Get the last contract event.
+///
+/// # Generic Parameters:
+/// - `S` - Sandbox environment.
+///
+/// # Parameters:
+/// - `session` - The session for interacting with contracts.
+///
+/// # Example:
+/// ```rs
+/// use drink::last_contract_event;
+///
+/// assert_eq!(
+/// last_contract_event::(&session).unwrap(),
+/// ContractEvent {
+/// value: 42,
+/// }
+/// .encode()
+/// .as_slice()
+/// );
+/// ```
+pub fn last_contract_event(session: &Session) -> Option>
+where
+ S: Sandbox,
+ S::Runtime: pallet_contracts::Config,
+ ::RuntimeEvent:
+ TryInto>,
+{
+ session.record().last_event_batch().contract_events().last().cloned()
+}
- /// Get the last event from pallet contracts.
- pub fn last_contract_event(session: &Session) -> Option>
- where
- S: Sandbox,
- S::Runtime: pallet_contracts::Config,
- ::RuntimeEvent: TryInto>,
- {
- session.record().last_event_batch().contract_events().last().cloned()
- }
+fn account_id_from_slice(s: &[u8; 32]) -> pop_api::primitives::AccountId {
+ pop_api::primitives::AccountId::decode(&mut &s[..]).expect("Should be decoded to AccountId")
}
diff --git a/crates/pop-drink/src/mock.rs b/crates/pop-drink/src/mock.rs
new file mode 100644
index 0000000..08d0746
--- /dev/null
+++ b/crates/pop-drink/src/mock.rs
@@ -0,0 +1,100 @@
+use frame_support::{
+ derive_impl, parameter_types,
+ traits::{AsEnsureOriginWithArg, ConstU32},
+};
+use frame_system::{pallet_prelude::BlockNumberFor, EnsureRoot, EnsureSigned};
+use pallet_contracts::{
+ config_preludes::{
+ CodeHashLockupDepositPercent, DefaultDepositLimit, DepositPerByte, DepositPerItem,
+ MaxDelegateDependencies,
+ },
+ DefaultAddressGenerator, Frame, Schedule,
+};
+
+type HashOf = ::Hash;
+
+frame_support::construct_runtime!(
+ pub enum Test {
+ System: frame_system,
+ Assets: pallet_assets::,
+ Balances: pallet_balances,
+ Timestamp: pallet_timestamp,
+ Contracts: pallet_contracts,
+ }
+);
+
+#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)]
+impl frame_system::Config for Test {
+ type AccountData = pallet_balances::AccountData;
+ type AccountId = u64;
+ type Block = frame_system::mocking::MockBlock;
+}
+
+#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig as pallet_balances::DefaultConfig)]
+impl pallet_balances::Config for Test {
+ type AccountStore = System;
+ type ReserveIdentifier = [u8; 8];
+}
+
+#[derive_impl(pallet_timestamp::config_preludes::TestDefaultConfig as pallet_timestamp::DefaultConfig)]
+impl pallet_timestamp::Config for Test {}
+
+impl pallet_contracts::Config for Test {
+ type AddressGenerator = DefaultAddressGenerator;
+ type ApiVersion = ();
+ type CallFilter = ();
+ // TestFilter;
+ type CallStack = [Frame; 5];
+ type ChainExtension = ();
+ type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent;
+ type Currency = Balances;
+ type Debug = ();
+ // TestDebug;
+ type DefaultDepositLimit = DefaultDepositLimit;
+ type DepositPerByte = DepositPerByte;
+ type DepositPerItem = DepositPerItem;
+ type Environment = ();
+ type InstantiateOrigin = EnsureSigned;
+ type MaxCodeLen = ConstU32<{ 100 * 1024 }>;
+ type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>;
+ type MaxDelegateDependencies = MaxDelegateDependencies;
+ type MaxStorageKeyLen = ConstU32<128>;
+ type Migrations = ();
+ // crate::migration::codegen::BenchMigrations;
+ type Randomness = Test;
+ type RuntimeCall = RuntimeCall;
+ type RuntimeEvent = RuntimeEvent;
+ type RuntimeHoldReason = RuntimeHoldReason;
+ type Schedule = MySchedule;
+ type Time = Timestamp;
+ type UnsafeUnstableInterface = ();
+ // UnstableInterface;
+ type UploadOrigin = EnsureSigned;
+ type WeightInfo = ();
+ type WeightPrice = ();
+ // Self;
+ type Xcm = ();
+}
+
+type AssetsInstance = pallet_assets::Instance1;
+#[derive_impl(pallet_assets::config_preludes::TestDefaultConfig as pallet_assets::DefaultConfig)]
+impl pallet_assets::Config for Test {
+ type CreateOrigin = AsEnsureOriginWithArg>;
+ type Currency = Balances;
+ type ForceOrigin = EnsureRoot;
+ type Freezer = ();
+ type RuntimeEvent = RuntimeEvent;
+}
+
+parameter_types! {
+ pub MySchedule: Schedule = {
+ let schedule = >::default();
+ schedule
+ };
+}
+
+impl frame_support::traits::Randomness, BlockNumberFor> for Test {
+ fn random(_subject: &[u8]) -> (HashOf, BlockNumberFor) {
+ (Default::default(), Default::default())
+ }
+}