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`. +R0GUE Logo -# 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()) + } +}