diff --git a/pallets/automation-time/src/benchmarking.rs b/pallets/automation-time/src/benchmarking.rs index 6bdc5d2e2..98f41d69f 100644 --- a/pallets/automation-time/src/benchmarking.rs +++ b/pallets/automation-time/src/benchmarking.rs @@ -259,7 +259,7 @@ benchmarks! { let task_id = Pallet::::generate_task_id(caller.clone(), provided_id.clone()); }: schedule_dynamic_dispatch_task(RawOrigin::Signed(caller.clone()), provided_id, schedule, Box::new(call)) verify { - assert_last_event::(Event::TaskScheduled { who: caller, task_id: task_id }.into()) + assert_last_event::(Event::TaskScheduled { who: caller, task_id: task_id, schedule_as: None }.into()) } schedule_dynamic_dispatch_task_full { @@ -285,7 +285,7 @@ benchmarks! { schedule_notify_tasks::(caller.clone(), times, T::MaxTasksPerSlot::get() - 1); }: schedule_dynamic_dispatch_task(RawOrigin::Signed(caller.clone()), provided_id, schedule, Box::new(call)) verify { - assert_last_event::(Event::TaskScheduled { who: caller, task_id: task_id }.into()) + assert_last_event::(Event::TaskScheduled { who: caller, task_id: task_id, schedule_as: None }.into()) } cancel_scheduled_task_full { diff --git a/pallets/automation-time/src/lib.rs b/pallets/automation-time/src/lib.rs index 5c309f39e..3b0877164 100644 --- a/pallets/automation-time/src/lib.rs +++ b/pallets/automation-time/src/lib.rs @@ -267,6 +267,7 @@ pub mod pallet { TaskScheduled { who: AccountOf, task_id: TaskId, + schedule_as: Option>, }, /// Cancelled a task. TaskCancelled { @@ -332,6 +333,7 @@ pub mod pallet { TaskRescheduled { who: AccountOf, task_id: TaskId, + schedule_as: Option>, }, /// A recurring task was not rescheduled TaskNotRescheduled { @@ -1347,12 +1349,12 @@ pub mod pallet { Ok(task_id) })?; - let scheduler = match action { - Action::XCMP { schedule_as, .. } => schedule_as.unwrap_or(owner_id), - _ => owner_id, + let schedule_as = match action { + Action::XCMP { schedule_as, .. } => schedule_as, + _ => None, }; - Self::deposit_event(Event::::TaskScheduled { who: scheduler, task_id }); + Self::deposit_event(Event::::TaskScheduled { who: owner_id, task_id, schedule_as }); Ok(()) } @@ -1370,9 +1372,18 @@ pub mod pallet { AccountTasks::::remove(task.owner_id.clone(), task_id); } else { let owner_id = task.owner_id.clone(); + let action = task.action.clone(); match Self::reschedule_existing_task(task_id, &mut task) { Ok(_) => { - Self::deposit_event(Event::::TaskRescheduled { who: owner_id, task_id }); + let schedule_as = match action { + Action::XCMP { schedule_as, .. } => schedule_as, + _ => None, + }; + Self::deposit_event(Event::::TaskRescheduled { + who: owner_id, + task_id, + schedule_as, + }); }, Err(err) => { Self::deposit_event(Event::::TaskFailedToReschedule { @@ -1401,9 +1412,18 @@ pub mod pallet { })?; let owner_id = task.owner_id.clone(); - AccountTasks::::insert(owner_id.clone(), task_id, task); + AccountTasks::::insert(owner_id.clone(), task_id, task.clone()); - Self::deposit_event(Event::::TaskScheduled { who: owner_id, task_id }); + let schedule_as = match task.action.clone() { + Action::XCMP { schedule_as, .. } => schedule_as.clone(), + _ => None, + }; + + Self::deposit_event(Event::::TaskScheduled { + who: owner_id, + task_id, + schedule_as, + }); }, Schedule::Fixed { .. } => {}, } diff --git a/pallets/automation-time/src/mock.rs b/pallets/automation-time/src/mock.rs index e2249db74..960e75a4c 100644 --- a/pallets/automation-time/src/mock.rs +++ b/pallets/automation-time/src/mock.rs @@ -42,6 +42,9 @@ pub type CurrencyId = u32; pub const ALICE: [u8; 32] = [1u8; 32]; pub const BOB: [u8; 32] = [2u8; 32]; +pub const DELEGATOR_ACCOUNT: [u8; 32] = [3u8; 32]; +pub const PROXY_ACCOUNT: [u8; 32] = [4u8; 32]; + pub const PARA_ID: u32 = 2000; pub const NATIVE: CurrencyId = 0; @@ -351,7 +354,11 @@ impl Convert> for MockTokenIdConvert { pub struct MockEnsureProxy; impl EnsureProxy for MockEnsureProxy { fn ensure_ok(_delegator: AccountId, _delegatee: AccountId) -> Result<(), &'static str> { - Ok(()) + if _delegator == DELEGATOR_ACCOUNT.into() && _delegatee == PROXY_ACCOUNT.into() { + Ok(()) + } else { + Err("proxy error: expected `ProxyType::Any`") + } } } diff --git a/pallets/automation-time/src/tests.rs b/pallets/automation-time/src/tests.rs index ee20147c6..3d250a3c2 100644 --- a/pallets/automation-time/src/tests.rs +++ b/pallets/automation-time/src/tests.rs @@ -310,6 +310,77 @@ fn schedule_xcmp_works() { }) } +#[test] +fn schedule_xcmp_through_proxy_works() { + new_test_ext(START_BLOCK_TIME).execute_with(|| { + let provided_id = vec![50]; + let delegator_account = AccountId32::new(DELEGATOR_ACCOUNT); + let proxy_account = AccountId32::new(PROXY_ACCOUNT); + let call: Vec = vec![2, 4, 5]; + + // Funds including XCM fees + get_xcmp_funds(proxy_account.clone()); + + assert_ok!(AutomationTime::schedule_xcmp_task_through_proxy( + RuntimeOrigin::signed(proxy_account.clone()), + provided_id, + ScheduleParam::Fixed { execution_times: vec![SCHEDULED_TIME] }, + PARA_ID.try_into().unwrap(), + NATIVE, + MultiLocation::new(1, X1(Parachain(PARA_ID.into()))).into(), + call.clone(), + Weight::from_ref_time(100_000), + delegator_account.clone(), + )); + + let tasks = AutomationTime::get_scheduled_tasks(SCHEDULED_TIME); + assert_eq!(tasks.is_some(), true); + + let tasks = tasks.unwrap(); + assert_eq!(tasks.tasks[0].0, proxy_account.clone()); + + // Find the TaskScheduled event in the event list and verify if the who within it is correct. + events() + .into_iter() + .find(|e| match e { + RuntimeEvent::AutomationTime(crate::Event::TaskScheduled { + who, + schedule_as, + .. + }) if *who == proxy_account && *schedule_as == Some(delegator_account.clone()) => true, + _ => false, + }) + .expect("TaskScheduled event should emit with 'who' being proxy_account, and 'schedule_as' being delegator_account."); + }) +} + +#[test] +fn schedule_xcmp_through_proxy_same_as_delegator_account() { + new_test_ext(START_BLOCK_TIME).execute_with(|| { + let provided_id = vec![50]; + let delegator_account = AccountId32::new(ALICE); + let call: Vec = vec![2, 4, 5]; + + // Funds including XCM fees + get_xcmp_funds(delegator_account.clone()); + + assert_noop!( + AutomationTime::schedule_xcmp_task_through_proxy( + RuntimeOrigin::signed(delegator_account.clone()), + provided_id, + ScheduleParam::Fixed { execution_times: vec![SCHEDULED_TIME] }, + PARA_ID.try_into().unwrap(), + NATIVE, + MultiLocation::new(1, X1(Parachain(PARA_ID.into()))).into(), + call.clone(), + Weight::from_ref_time(100_000), + delegator_account.clone(), + ), + sp_runtime::DispatchError::Other("proxy error: expected `ProxyType::Any`"), + ); + }) +} + #[test] fn schedule_xcmp_fails_if_not_enough_funds() { new_test_ext(START_BLOCK_TIME).execute_with(|| { @@ -1024,7 +1095,8 @@ mod extrinsics { last_event(), RuntimeEvent::AutomationTime(crate::Event::TaskScheduled { who: account_id, - task_id + task_id, + schedule_as: None, }) ); }) diff --git a/pallets/automation-time/src/types.rs b/pallets/automation-time/src/types.rs index d16f6bd2c..a67bfafb0 100644 --- a/pallets/automation-time/src/types.rs +++ b/pallets/automation-time/src/types.rs @@ -198,7 +198,7 @@ impl Schedule { } /// The struct that stores all information needed for a task. -#[derive(Debug, Encode, Decode, TypeInfo)] +#[derive(Debug, Encode, Decode, TypeInfo, Clone)] #[scale_info(skip_type_params(MaxExecutionTimes))] pub struct Task { pub owner_id: AccountId,