diff --git a/pallets/automation-price/src/lib.rs b/pallets/automation-price/src/lib.rs index 90ea752b..8c515d0b 100644 --- a/pallets/automation-price/src/lib.rs +++ b/pallets/automation-price/src/lib.rs @@ -362,6 +362,27 @@ pub mod pallet { who: AccountOf, task_id: TaskId, }, + // an event when we're about to run the task + TaskTriggered { + who: AccountOf, + task_id: TaskId, + }, + // An event when the task ran succesfully + TaskExecuted { + who: AccountOf, + task_id: TaskId, + }, + // An event when the task is trigger, ran but result in an error + TaskExecutionFailed { + who: AccountOf, + task_id: TaskId, + error: DispatchError, + }, + // An event when the task is completed and removed from all of the queue + TaskCompleted { + who: AccountOf, + task_id: TaskId, + }, TaskCancelled { who: AccountOf, task_id: TaskId, @@ -708,6 +729,8 @@ pub mod pallet { // Task Registry // SortedTasksIndex // AccountTasks + // Task Queue: if the task is already on the queue but haven't got run yet, + // we will attemppt to remove it #[pallet::call_index(6)] #[pallet::weight(::WeightInfo::cancel_task_extrinsic())] #[transactional] @@ -724,6 +747,10 @@ pub mod pallet { let key = (&task.chain, &task.exchange, &task.asset_pair, &task.trigger_function); SortedTasksIndex::::remove(&key); Self::remove_task_from_account(&task); + Self::deposit_event(Event::TaskCancelled { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + }); }; Ok(()) @@ -957,7 +984,12 @@ pub mod pallet { ::WeightInfo::emit_event() }, Some(task) => { - let task_action_weight = match task.action.clone() { + Self::deposit_event(Event::TaskTriggered { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + }); + + let (task_action_weight, task_dispatch_error) = match task.action.clone() { // TODO: Run actual task later to return weight // not just return weight for test to pass Action::XCMP { @@ -969,25 +1001,40 @@ pub mod pallet { overall_weight, instruction_sequence, .. - } => { - let (w, _err) = Self::run_xcmp_task( - destination, - schedule_as.unwrap_or(task.owner_id.clone()), - execution_fee, - encoded_call, - encoded_call_weight, - overall_weight, - instruction_sequence, - ); - w - }, + } => Self::run_xcmp_task( + destination, + schedule_as.unwrap_or(task.owner_id.clone()), + execution_fee, + encoded_call, + encoded_call_weight, + overall_weight, + instruction_sequence, + ), }; Tasks::::remove(task_id); + if let Some(err) = task_dispatch_error { + Self::deposit_event(Event::::TaskExecutionFailed { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + error: err, + }); + } else { + Self::deposit_event(Event::::TaskExecuted { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + }); + } + // TODO: add this weight Self::remove_task_from_account(&task); + Self::deposit_event(Event::::TaskCompleted { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + }); + task_action_weight .saturating_add(T::DbWeight::get().writes(1u64)) .saturating_add(T::DbWeight::get().reads(1u64)) diff --git a/pallets/automation-price/src/mock.rs b/pallets/automation-price/src/mock.rs index ab04b4e7..586cb379 100644 --- a/pallets/automation-price/src/mock.rs +++ b/pallets/automation-price/src/mock.rs @@ -498,6 +498,7 @@ pub fn get_fee_per_second(location: &MultiLocation) -> Option { } } +// setup a sample default asset to support test pub fn setup_asset(sender: &AccountId32, chain: Vec) { AutomationPrice::initialize_asset( RawOrigin::Root.into(), @@ -510,6 +511,7 @@ pub fn setup_asset(sender: &AccountId32, chain: Vec) { ); } +// setup a few sample assets, initialize it with sane default vale and set a price to support test cases pub fn setup_prices(sender: &AccountId32) { AutomationPrice::initialize_asset( RawOrigin::Root.into(), diff --git a/pallets/automation-price/src/tests.rs b/pallets/automation-price/src/tests.rs index b4b9a532..cb37df40 100644 --- a/pallets/automation-price/src/tests.rs +++ b/pallets/automation-price/src/tests.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::{mock::*, AssetPayment, Config, Error, TaskIdList}; +use crate::{mock::*, Action, AssetPayment, Config, Error, Task, TaskIdList}; use pallet_xcmp_handler::InstructionSequence; use frame_support::{ @@ -588,3 +588,105 @@ fn test_shift_tasks_movement_through_price_changes() { ); }) } + +#[test] +fn test_emit_event_when_execute_tasks() { + new_test_ext(START_BLOCK_TIME).execute_with(|| { + let creator = AccountId32::new(ALICE); + let para_id: u32 = 1000; + + let destination = MultiLocation::new(1, X1(Parachain(para_id))); + let schedule_fee = MultiLocation::default(); + let execution_fee = AssetPayment { + asset_location: MultiLocation::new(1, X1(Parachain(para_id))).into(), + amount: 0, + }; + let encoded_call_weight = Weight::from_ref_time(100_000); + let overall_weight = Weight::from_ref_time(200_000); + + let task = Task:: { + owner_id: creator.into(), + task_id: "123-0-1".as_bytes().to_vec(), + chain: chain1.to_vec(), + exchange: exchange1.to_vec(), + asset_pair: (asset1.to_vec(), asset2.to_vec()), + expired_at: 123_u128, + trigger_function: "gt".as_bytes().to_vec(), + trigger_params: vec![123], + action: Action::XCMP { + destination, + schedule_fee, + execution_fee, + encoded_call: vec![1, 2, 3], + encoded_call_weight, + overall_weight, + schedule_as: None, + instruction_sequence: InstructionSequence::PayThroughRemoteDerivativeAccount, + }, + }; + + AutomationPrice::validate_and_schedule_task(task.clone()); + + AutomationPrice::run_tasks(vec![task.task_id.clone()], 100_000_000_000.into()); + + assert_has_event(RuntimeEvent::AutomationPrice(crate::Event::TaskTriggered { + who: task.owner_id.clone(), + task_id: task.task_id.clone(), + })); + + assert_has_event(RuntimeEvent::AutomationPrice(crate::Event::TaskExecuted { + who: task.owner_id.clone(), + task_id: task.task_id, + })); + }) +} + +// When canceling, task is removed from 3 places: +#[test] +fn test_cancel_task_works() { + new_test_ext(START_BLOCK_TIME).execute_with(|| { + let creator = AccountId32::new(ALICE); + let para_id: u32 = 1000; + + let destination = MultiLocation::new(1, X1(Parachain(para_id))); + let schedule_fee = MultiLocation::default(); + let execution_fee = AssetPayment { + asset_location: MultiLocation::new(1, X1(Parachain(para_id))).into(), + amount: 0, + }; + let encoded_call_weight = Weight::from_ref_time(100_000); + let overall_weight = Weight::from_ref_time(200_000); + + let task = Task:: { + owner_id: creator.into(), + task_id: "123-0-1".as_bytes().to_vec(), + chain: chain1.to_vec(), + exchange: exchange1.to_vec(), + asset_pair: (asset1.to_vec(), asset2.to_vec()), + expired_at: 123_u128, + trigger_function: "gt".as_bytes().to_vec(), + trigger_params: vec![123], + action: Action::XCMP { + destination, + schedule_fee, + execution_fee, + encoded_call: vec![1, 2, 3], + encoded_call_weight, + overall_weight, + schedule_as: None, + instruction_sequence: InstructionSequence::PayThroughRemoteDerivativeAccount, + }, + }; + AutomationPrice::validate_and_schedule_task(task.clone()); + + AutomationPrice::cancel_task( + RuntimeOrigin::signed(task.owner_id.clone()), + task.task_id.clone(), + ); + + assert_has_event(RuntimeEvent::AutomationPrice(crate::Event::TaskCancelled { + who: task.owner_id.clone(), + task_id: task.task_id, + })); + }) +}