diff --git a/drink-cli/src/app_state/print.rs b/drink-cli/src/app_state/print.rs index 95f69c2..a1b26e5 100644 --- a/drink-cli/src/app_state/print.rs +++ b/drink-cli/src/app_state/print.rs @@ -1,4 +1,4 @@ -use drink::contract_api::decode_debug_buffer; +use drink::contracts_api::decode_debug_buffer; use pallet_contracts::ContractResult; use ratatui::{ style::{Color, Modifier, Style}, diff --git a/drink/src/runtime.rs b/drink/src/runtime.rs index 7291381..2ba1039 100644 --- a/drink/src/runtime.rs +++ b/drink/src/runtime.rs @@ -2,9 +2,9 @@ //! `drink` with any runtime that implements the `Runtime` trait. pub mod minimal; +pub mod pallet_contracts_debugging; pub use frame_metadata::RuntimeMetadataPrefixed; pub use minimal::MinimalRuntime; -pub mod pallet_contracts_debugging; /// The type of an account identifier. pub type AccountIdFor = ::AccountId; diff --git a/drink/src/sandbox.rs b/drink/src/sandbox.rs index 882598d..b9f8b5b 100644 --- a/drink/src/sandbox.rs +++ b/drink/src/sandbox.rs @@ -3,7 +3,7 @@ mod sandbox_config; pub use sandbox_config::SandboxConfig; pub mod balance_api; -pub mod contract_api; +pub mod contracts_api; pub mod runtime_api; pub mod system_api; pub mod timestamp_api; diff --git a/drink/src/sandbox/balance_api.rs b/drink/src/sandbox/balance_api.rs index 104c151..9740abf 100644 --- a/drink/src/sandbox/balance_api.rs +++ b/drink/src/sandbox/balance_api.rs @@ -2,11 +2,11 @@ use frame_support::{sp_runtime::DispatchError, traits::fungible::Mutate}; use super::Sandbox; -use crate::{runtime::AccountIdFor, BalanceOf}; +use crate::{runtime::AccountIdFor, BalanceOf, SandboxConfig}; -impl Sandbox +impl Sandbox where - Config::Runtime: crate::pallet_balances::Config, + Config::Runtime: pallet_balances::Config, { /// Mint tokens to an account. /// diff --git a/drink/src/sandbox/contract_api.rs b/drink/src/sandbox/contract_api.rs deleted file mode 100644 index 86426a3..0000000 --- a/drink/src/sandbox/contract_api.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Contracts API for the sandbox. -use std::ops::Not; - -use frame_support::{traits::fungible::Inspect, weights::Weight}; -use frame_system::Config as SysConfig; -use pallet_contracts::{ - Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, - DebugInfo, Determinism, -}; -use parity_scale_codec::Decode as _; - -use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; - -type BalanceOf = - <::Currency as Inspect>>::Balance; - -impl Sandbox -where - Config::Runtime: pallet_contracts::Config, -{ - /// Interface for `bare_instantiate` contract call with a simultaneous upload. - /// - /// # Arguments - /// - /// * `contract_bytes` - The contract code. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including constructor name). - /// * `salt` - The salt to be used for contract address derivation. - /// * `origin` - The sender of the contract call. - /// * `gas_limit` - The gas limit for the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::type_complexity, clippy::too_many_arguments)] - pub fn deploy_contract( - &mut self, - contract_bytes: Vec, - value: BalanceOf, - data: Vec, - salt: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult< - AccountIdFor, - BalanceOf, - EventRecordOf, - > { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( - origin, - value, - gas_limit, - storage_deposit_limit, - Code::Upload(contract_bytes), - data, - salt, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - ) - }) - } - - /// Interface for `bare_instantiate` contract call for a previously uploaded contract. - /// - /// # Arguments - /// - /// * `code_hash` - The code hash of the contract to instantiate. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including constructor name). - /// * `salt` - The salt to be used for contract address derivation. - /// * `origin` - The sender of the contract call. - /// * `gas_limit` - The gas limit for the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::type_complexity, clippy::too_many_arguments)] - pub fn instantiate_contract( - &mut self, - code_hash: Vec, - value: BalanceOf, - data: Vec, - salt: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult< - AccountIdFor, - BalanceOf, - EventRecordOf, - > { - let mut code_hash = &code_hash[..]; - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( - origin, - value, - gas_limit, - storage_deposit_limit, - Code::Existing( - ::Hash::decode(&mut code_hash) - .expect("Invalid code hash"), - ), - data, - salt, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - ) - }) - } - - /// Interface for `bare_upload_code` contract call. - /// - /// # Arguments - /// - /// * `contract_bytes` - The contract code. - /// * `origin` - The sender of the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - pub fn upload_contract( - &mut self, - contract_bytes: Vec, - origin: AccountIdFor, - storage_deposit_limit: Option>, - determinism: Determinism, - ) -> CodeUploadResult<::Hash, BalanceOf> - { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_upload_code( - origin, - contract_bytes, - storage_deposit_limit, - determinism, - ) - }) - } - - /// Interface for `bare_call` contract call. - /// - /// # Arguments - /// - /// * `address` - The address of the contract to be called. - /// * `value` - The number of tokens to be transferred to the contract. - /// * `data` - The input data to be passed to the contract (including message name). - /// * `origin` - The sender of the contract call. - /// * `gas_limit` - The gas limit for the contract call. - /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::too_many_arguments)] - pub fn call_contract( - &mut self, - address: AccountIdFor, - value: BalanceOf, - data: Vec, - origin: AccountIdFor, - gas_limit: Weight, - storage_deposit_limit: Option>, - determinism: Determinism, - ) -> ContractExecResult, EventRecordOf> { - self.externalities.execute_with(|| { - pallet_contracts::Pallet::::bare_call( - origin, - address, - value, - gas_limit, - storage_deposit_limit, - data, - DebugInfo::UnsafeDebug, - CollectEvents::UnsafeCollect, - determinism, - ) - }) - } -} - -/// Converts bytes to a '\n'-split string, ignoring empty lines. -pub fn decode_debug_buffer(buffer: &[u8]) -> Vec { - let decoded = buffer.iter().map(|b| *b as char).collect::(); - decoded - .split('\n') - .filter_map(|s| s.is_empty().not().then_some(s.to_string())) - .collect() -} - -#[cfg(test)] -mod tests { - use frame_support::sp_runtime::traits::Hash; - use pallet_contracts::Origin; - - use super::*; - use crate::{ - minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, - DEFAULT_GAS_LIMIT, - }; - - fn compile_module(contract_name: &str) -> Vec { - let path = [ - std::env::var("CARGO_MANIFEST_DIR") - .as_deref() - .unwrap_or("drink"), - "/test-resources/", - contract_name, - ".wat", - ] - .concat(); - wat::parse_file(path).expect("Failed to parse wat file") - } - - #[test] - fn can_upload_code() { - let mut sandbox = Sandbox::::new().unwrap(); - let wasm_binary = compile_module("dummy"); - let hash = <::Hashing>::hash(&wasm_binary); - - let result = sandbox.upload_contract( - wasm_binary, - MinimalRuntime::default_actor(), - None, - Determinism::Enforced, - ); - - assert!(result.is_ok()); - assert_eq!(hash, result.unwrap().code_hash); - } - - #[test] - fn can_deploy_contract() { - let mut sandbox = Sandbox::::new().unwrap(); - let wasm_binary = compile_module("dummy"); - - let events_before = sandbox.events(); - assert!(events_before.is_empty()); - - let result = sandbox.deploy_contract( - wasm_binary, - 0, - vec![], - NO_SALT, - MinimalRuntime::default_actor(), - DEFAULT_GAS_LIMIT, - None, - ); - assert!(result.result.is_ok()); - assert!(!result.result.unwrap().result.did_revert()); - - let events = result.events.expect("Drink should collect events"); - let event_count = events.len(); - let instantiation_event = events[event_count - 2].clone(); - assert!(matches!( - instantiation_event.event, - RuntimeEvent::Contracts(pallet_contracts::Event::::Instantiated { .. }) - )); - let deposit_event = events[event_count - 1].clone(); - assert!(matches!( - deposit_event.event, - RuntimeEvent::Contracts( - pallet_contracts::Event::::StorageDepositTransferredAndHeld { .. } - ) - )); - } - - #[test] - fn can_call_contract() { - let mut sandbox = Sandbox::::new().unwrap(); - let actor = MinimalRuntime::default_actor(); - let wasm_binary = compile_module("dummy"); - - let result = sandbox.deploy_contract( - wasm_binary, - 0, - vec![], - NO_SALT, - actor.clone(), - DEFAULT_GAS_LIMIT, - None, - ); - - let contract_address = result - .result - .expect("Contract should be deployed") - .account_id; - - sandbox.reset_events(); - - let result = sandbox.call_contract( - contract_address.clone(), - 0, - vec![], - actor.clone(), - DEFAULT_GAS_LIMIT, - None, - Determinism::Enforced, - ); - assert!(result.result.is_ok()); - assert!(!result.result.unwrap().did_revert()); - - let events = result.events.expect("Drink should collect events"); - assert_eq!(events.len(), 2); - - assert_eq!( - events[0].event, - RuntimeEvent::Contracts(pallet_contracts::Event::::ContractEmitted { - contract: contract_address.clone(), - data: vec![0, 0, 0, 0], - }) - ); - - assert_eq!( - events[1].event, - RuntimeEvent::Contracts(pallet_contracts::Event::::Called { - contract: contract_address, - caller: Origin::Signed(actor), - }), - ); - } -} diff --git a/drink/src/sandbox/contracts_api.rs b/drink/src/sandbox/contracts_api.rs index 73c4285..86426a3 100644 --- a/drink/src/sandbox/contracts_api.rs +++ b/drink/src/sandbox/contracts_api.rs @@ -2,19 +2,22 @@ use std::ops::Not; use frame_support::{traits::fungible::Inspect, weights::Weight}; -use frame_system::Config; +use frame_system::Config as SysConfig; use pallet_contracts::{ Code, CodeUploadResult, CollectEvents, ContractExecResult, ContractInstantiateResult, DebugInfo, Determinism, }; use parity_scale_codec::Decode as _; -use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox, SandboxApi}; +use crate::{runtime::AccountIdFor, EventRecordOf, Sandbox}; type BalanceOf = <::Currency as Inspect>>::Balance; -pub trait ContractsApi: SandboxApi { +impl Sandbox +where + Config::Runtime: pallet_contracts::Config, +{ /// Interface for `bare_instantiate` contract call with a simultaneous upload. /// /// # Arguments @@ -26,19 +29,23 @@ pub trait ContractsApi: SandboxApi { /// * `origin` - The sender of the contract call. /// * `gas_limit` - The gas limit for the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::too_many_arguments)] - fn deploy_contract( + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + pub fn deploy_contract( &mut self, contract_bytes: Vec, - value: BalanceOf, + value: BalanceOf, data: Vec, salt: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> { - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, @@ -63,26 +70,31 @@ pub trait ContractsApi: SandboxApi { /// * `origin` - The sender of the contract call. /// * `gas_limit` - The gas limit for the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - #[allow(clippy::too_many_arguments)] - fn instantiate_contract( + #[allow(clippy::type_complexity, clippy::too_many_arguments)] + pub fn instantiate_contract( &mut self, code_hash: Vec, - value: BalanceOf, + value: BalanceOf, data: Vec, salt: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, - ) -> ContractInstantiateResult, BalanceOf, EventRecordOf> { + storage_deposit_limit: Option>, + ) -> ContractInstantiateResult< + AccountIdFor, + BalanceOf, + EventRecordOf, + > { let mut code_hash = &code_hash[..]; - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_instantiate( + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_instantiate( origin, value, gas_limit, storage_deposit_limit, Code::Existing( - ::Hash::decode(&mut code_hash).expect("Invalid code hash"), + ::Hash::decode(&mut code_hash) + .expect("Invalid code hash"), ), data, salt, @@ -99,15 +111,16 @@ pub trait ContractsApi: SandboxApi { /// * `contract_bytes` - The contract code. /// * `origin` - The sender of the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. - fn upload_contract( + pub fn upload_contract( &mut self, contract_bytes: Vec, - origin: AccountIdFor, - storage_deposit_limit: Option>, + origin: AccountIdFor, + storage_deposit_limit: Option>, determinism: Determinism, - ) -> CodeUploadResult<::Hash, BalanceOf> { - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_upload_code( + ) -> CodeUploadResult<::Hash, BalanceOf> + { + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_upload_code( origin, contract_bytes, storage_deposit_limit, @@ -127,18 +140,18 @@ pub trait ContractsApi: SandboxApi { /// * `gas_limit` - The gas limit for the contract call. /// * `storage_deposit_limit` - The storage deposit limit for the contract call. #[allow(clippy::too_many_arguments)] - fn call_contract( + pub fn call_contract( &mut self, - address: AccountIdFor, - value: BalanceOf, + address: AccountIdFor, + value: BalanceOf, data: Vec, - origin: AccountIdFor, + origin: AccountIdFor, gas_limit: Weight, - storage_deposit_limit: Option>, + storage_deposit_limit: Option>, determinism: Determinism, - ) -> ContractExecResult, EventRecordOf> { - self.externalities().execute_with(|| { - pallet_contracts::Pallet::::bare_call( + ) -> ContractExecResult, EventRecordOf> { + self.externalities.execute_with(|| { + pallet_contracts::Pallet::::bare_call( origin, address, value, @@ -169,7 +182,7 @@ mod tests { use super::*; use crate::{ - minimal::RuntimeEvent, runtime::Runtime, session::NO_SALT, MinimalRuntime, + minimal::RuntimeEvent, sandbox::SandboxConfig, session::NO_SALT, MinimalRuntime, DEFAULT_GAS_LIMIT, }; diff --git a/examples/quick-start-with-drink/lib.rs b/examples/quick-start-with-drink/lib.rs index b15e6b8..557fa25 100644 --- a/examples/quick-start-with-drink/lib.rs +++ b/examples/quick-start-with-drink/lib.rs @@ -43,7 +43,7 @@ mod flipper { #[cfg(test)] mod tests { use drink::{ - contract_api::decode_debug_buffer, + contracts_api::decode_debug_buffer, session::{Session, NO_ARGS, NO_ENDOWMENT, NO_SALT}, };