@@ -22,13 +22,18 @@ pub mod weights;
2222pub use weights:: WeightInfo ;
2323
2424use alloc:: vec:: Vec ;
25+ use frame_support:: pallet_prelude:: * ;
2526use frame_support:: traits:: tokens:: { Fortitude , Restriction } ;
26- use frame_support:: { pallet_prelude:: * , traits:: schedule:: DispatchTime } ;
2727use frame_system:: pallet_prelude:: * ;
28+ use qp_scheduler:: { BlockNumberOrTimestamp , DispatchTime , ScheduleNamed } ;
2829use sp_runtime:: traits:: StaticLookup ;
2930
31+ /// Type alias for this config's `BlockNumberOrTimestamp`.
32+ pub type BlockNumberOrTimestampOf < T > =
33+ BlockNumberOrTimestamp < BlockNumberFor < T > , <T as Config >:: Moment > ;
34+
3035/// How to delay transactions
31- /// - `Explicit`: Only delay transactions explicitly using `schedule_transfer`.
36+ /// - `Explicit`: Only delay transactions explicitly using this pallet's `schedule_transfer` extrinsic .
3237/// - `Intercept`: Intercept and delay transactions at the `TransactionExtension` level.
3338///
3439/// For example, for a reversible account with `DelayPolicy::Intercept`, the transaction
@@ -49,11 +54,11 @@ pub enum DelayPolicy {
4954
5055/// Reversible account details
5156#[ derive( Encode , Decode , MaxEncodedLen , Clone , Default , TypeInfo , Debug , PartialEq , Eq ) ]
52- pub struct ReversibleAccountData < AccountId , BlockNumber > {
57+ pub struct ReversibleAccountData < AccountId , Delay > {
5358 /// The account that can reverse the transaction. `None` means the account itself.
5459 pub explicit_reverser : Option < AccountId > ,
5560 /// The delay period for the account
56- pub delay : BlockNumber ,
61+ pub delay : Delay ,
5762 /// The policy for the account
5863 pub policy : DelayPolicy ,
5964}
@@ -78,17 +83,15 @@ type BalanceOf<T> = <T as pallet_balances::Config>::Balance;
7883#[ frame_support:: pallet]
7984pub mod pallet {
8085 use super :: * ;
86+ use crate :: BlockNumberOrTimestampOf ;
8187 use frame_support:: dispatch:: PostDispatchInfo ;
8288 use frame_support:: traits:: fungible:: MutateHold ;
8389 use frame_support:: traits:: tokens:: Precision ;
84- use frame_support:: traits:: { Bounded , CallerTrait , QueryPreimage , StorePreimage } ;
85- use frame_support:: {
86- traits:: schedule:: v3:: { Named , TaskName } ,
87- PalletId ,
88- } ;
89- use sp_runtime:: traits:: AccountIdConversion ;
90- use sp_runtime:: traits:: Hash ;
90+ use frame_support:: traits:: { Bounded , CallerTrait , QueryPreimage , StorePreimage , Time } ;
91+ use frame_support:: { traits:: schedule:: v3:: TaskName , PalletId } ;
92+ use sp_runtime:: traits:: { AccountIdConversion , AtLeast32Bit } ;
9193 use sp_runtime:: traits:: { BlockNumberProvider , Dispatchable } ;
94+ use sp_runtime:: traits:: { Hash , Scale } ;
9295 use sp_runtime:: Saturating ;
9396
9497 #[ pallet:: pallet]
@@ -106,8 +109,9 @@ pub mod pallet {
106109 type RuntimeEvent : From < Event < Self > > + IsType < <Self as frame_system:: Config >:: RuntimeEvent > ;
107110
108111 /// Scheduler for the runtime. We use the Named scheduler for cancellability.
109- type Scheduler : Named <
112+ type Scheduler : ScheduleNamed <
110113 BlockNumberFor < Self > ,
114+ Self :: Moment ,
111115 Self :: RuntimeCall ,
112116 Self :: SchedulerOrigin ,
113117 Hasher = Self :: Hashing ,
@@ -126,12 +130,18 @@ pub mod pallet {
126130 type MaxPendingPerAccount : Get < u32 > ;
127131
128132 /// The default delay period for reversible transactions if none is specified.
133+ ///
134+ /// NOTE: default delay is always in blocks.
135+ #[ pallet:: constant]
136+ type DefaultDelay : Get < BlockNumberOrTimestampOf < Self > > ;
137+
138+ /// The minimum delay period allowed for reversible transactions, in blocks.
129139 #[ pallet:: constant]
130- type DefaultDelay : Get < BlockNumberFor < Self > > ;
140+ type MinDelayPeriodBlocks : Get < BlockNumberFor < Self > > ;
131141
132- /// The minimum delay period allowed for reversible transactions.
142+ /// The minimum delay period allowed for reversible transactions, in milliseconds .
133143 #[ pallet:: constant]
134- type MinDelayPeriod : Get < BlockNumberFor < Self > > ;
144+ type MinDelayPeriodMoment : Get < Self :: Moment > ;
135145
136146 /// Pallet Id
137147 type PalletId : Get < PalletId > ;
@@ -144,6 +154,17 @@ pub mod pallet {
144154
145155 /// Hold reason for the reversible transactions.
146156 type RuntimeHoldReason : From < HoldReason > ;
157+
158+ /// Moment type for scheduling.
159+ type Moment : Saturating
160+ + Copy
161+ + Parameter
162+ + AtLeast32Bit
163+ + Scale < BlockNumberFor < Self > , Output = Self :: Moment >
164+ + MaxEncodedLen ;
165+
166+ /// Time provider for scheduling.
167+ type TimeProvider : Time < Moment = Self :: Moment > ;
147168 }
148169
149170 /// Maps accounts to their chosen reversibility delay period (in milliseconds).
@@ -154,7 +175,7 @@ pub mod pallet {
154175 _ ,
155176 Blake2_128Concat ,
156177 T :: AccountId ,
157- ReversibleAccountData < T :: AccountId , BlockNumberFor < T > > ,
178+ ReversibleAccountData < T :: AccountId , BlockNumberOrTimestampOf < T > > ,
158179 OptionQuery ,
159180 > ;
160181
@@ -184,14 +205,14 @@ pub mod pallet {
184205 /// [who, maybe_delay: None means disabled]
185206 ReversibilitySet {
186207 who : T :: AccountId ,
187- data : ReversibleAccountData < T :: AccountId , BlockNumberFor < T > > ,
208+ data : ReversibleAccountData < T :: AccountId , BlockNumberOrTimestampOf < T > > ,
188209 } ,
189210 /// A transaction has been intercepted and scheduled for delayed execution.
190211 /// [who, tx_id, execute_at_moment]
191212 TransactionScheduled {
192213 who : T :: AccountId ,
193214 tx_id : T :: Hash ,
194- execute_at : DispatchTime < BlockNumberFor < T > > ,
215+ execute_at : DispatchTime < BlockNumberFor < T > , T :: Moment > ,
195216 } ,
196217 /// A scheduled transaction has been successfully cancelled by the owner.
197218 /// [who, tx_id]
@@ -248,24 +269,36 @@ pub mod pallet {
248269 #[ pallet:: weight( <T as Config >:: WeightInfo :: set_reversibility( ) ) ]
249270 pub fn set_reversibility (
250271 origin : OriginFor < T > ,
251- delay : Option < BlockNumberFor < T > > ,
272+ delay : Option < BlockNumberOrTimestampOf < T > > ,
252273 policy : DelayPolicy ,
253274 reverser : Option < T :: AccountId > ,
254275 ) -> DispatchResult {
255276 let who = ensure_signed ( origin) ?;
256277
257- ensure ! (
258- !ReversibleAccounts :: <T >:: contains_key( & who) ,
259- Error :: <T >:: AccountAlreadyReversible
260- ) ;
261278 ensure ! (
262279 reverser != Some ( who. clone( ) ) ,
263280 Error :: <T >:: ExplicitReverserCanNotBeSelf
264281 ) ;
265-
282+ ensure ! (
283+ !ReversibleAccounts :: <T >:: contains_key( & who) ,
284+ Error :: <T >:: AccountAlreadyReversible
285+ ) ;
266286 let delay = delay. unwrap_or ( T :: DefaultDelay :: get ( ) ) ;
267287
268- ensure ! ( delay >= T :: MinDelayPeriod :: get( ) , Error :: <T >:: DelayTooShort ) ;
288+ match delay {
289+ BlockNumberOrTimestamp :: BlockNumber ( x) => {
290+ ensure ! (
291+ x > T :: MinDelayPeriodBlocks :: get( ) ,
292+ Error :: <T >:: DelayTooShort
293+ )
294+ }
295+ BlockNumberOrTimestamp :: Timestamp ( t) => {
296+ ensure ! (
297+ t > T :: MinDelayPeriodMoment :: get( ) ,
298+ Error :: <T >:: DelayTooShort
299+ )
300+ }
301+ }
269302
270303 let reversible_account_data = ReversibleAccountData {
271304 explicit_reverser : reverser,
@@ -327,12 +360,16 @@ pub mod pallet {
327360 impl < T : Config > Hooks < BlockNumberFor < T > > for Pallet < T > {
328361 fn integrity_test ( ) {
329362 assert ! (
330- T :: MinDelayPeriod :: get( ) > Zero :: zero( ) ,
331- "`T::MinDelayPeriod` must be greater than 0"
363+ !T :: MinDelayPeriodBlocks :: get( ) . is_zero( )
364+ && !T :: MinDelayPeriodMoment :: get( ) . is_zero( ) ,
365+ "Minimum delay periods must be greater than 0"
332366 ) ;
367+
368+ // NOTE: default delay is always in blocks
333369 assert ! (
334- T :: MinDelayPeriod :: get( ) <= T :: DefaultDelay :: get( ) ,
335- "`T::MinDelayPeriod` must be less or equal to `T::DefaultDelay`"
370+ BlockNumberOrTimestampOf :: <T >:: BlockNumber ( T :: MinDelayPeriodBlocks :: get( ) )
371+ <= T :: DefaultDelay :: get( ) ,
372+ "Minimum delay periods must be less or equal to `T::DefaultDelay`"
336373 ) ;
337374 }
338375 }
@@ -352,7 +389,7 @@ pub mod pallet {
352389 /// Check if an account has reversibility enabled and return its delay.
353390 pub fn is_reversible (
354391 who : & T :: AccountId ,
355- ) -> Option < ReversibleAccountData < T :: AccountId , BlockNumberFor < T > > > {
392+ ) -> Option < ReversibleAccountData < T :: AccountId , BlockNumberOrTimestampOf < T > > > {
356393 ReversibleAccounts :: < T > :: get ( who)
357394 }
358395
@@ -430,18 +467,8 @@ pub mod pallet {
430467 amount : BalanceOf < T > ,
431468 ) -> DispatchResult {
432469 let who = ensure_signed ( origin) ?;
433- let ReversibleAccountData {
434- delay,
435- explicit_reverser,
436- policy : _,
437- } = Self :: reversible_accounts ( & who) . ok_or ( Error :: < T > :: AccountNotReversible ) ?;
438-
439- match explicit_reverser {
440- Some ( reverser) => {
441- ensure ! ( who != reverser, Error :: <T >:: InvalidReverser ) ;
442- }
443- None => { }
444- } ;
470+ let ReversibleAccountData { delay, .. } =
471+ Self :: reversible_accounts ( & who) . ok_or ( Error :: < T > :: AccountNotReversible ) ?;
445472
446473 let transfer_call: T :: RuntimeCall = pallet_balances:: Call :: < T > :: transfer_keep_alive {
447474 dest : dest. clone ( ) ,
@@ -461,9 +488,16 @@ pub mod pallet {
461488 Ok ( ( ) )
462489 } ) ?;
463490
464- let dispatch_time = DispatchTime :: At (
465- T :: BlockNumberProvider :: current_block_number ( ) . saturating_add ( delay) ,
466- ) ;
491+ let dispatch_time = match delay {
492+ BlockNumberOrTimestamp :: BlockNumber ( blocks) => DispatchTime :: At (
493+ T :: BlockNumberProvider :: current_block_number ( ) . saturating_add ( blocks) ,
494+ ) ,
495+ BlockNumberOrTimestamp :: Timestamp ( millis) => {
496+ DispatchTime :: After ( BlockNumberOrTimestamp :: Timestamp (
497+ T :: TimeProvider :: now ( ) . saturating_add ( millis) ,
498+ ) )
499+ }
500+ } ;
467501
468502 let call = T :: Preimages :: bound ( transfer_call) ?;
469503
@@ -592,6 +626,7 @@ pub mod pallet {
592626 #[ derive( frame_support:: DefaultNoBound ) ]
593627 pub struct GenesisConfig < T : Config > {
594628 /// Configure initial reversible accounts. [AccountId, Delay]
629+ /// NOTE: using `(bool, BlockNumberFor<T>)` where `bool` indicates if the delay is in block numbers
595630 pub initial_reversible_accounts : Vec < ( T :: AccountId , BlockNumberFor < T > ) > ,
596631 }
597632
@@ -600,20 +635,22 @@ pub mod pallet {
600635 fn build ( & self ) {
601636 for ( who, delay) in & self . initial_reversible_accounts {
602637 // Basic validation, ensure delay is reasonable if needed
603- if * delay >= T :: MinDelayPeriod :: get ( ) {
638+ let wrapped_delay = BlockNumberOrTimestampOf :: < T > :: BlockNumber ( * delay) ;
639+
640+ if delay >= & T :: MinDelayPeriodBlocks :: get ( ) {
604641 ReversibleAccounts :: < T > :: insert (
605642 who,
606643 ReversibleAccountData {
607644 explicit_reverser : None ,
608- delay : * delay ,
645+ delay : wrapped_delay ,
609646 policy : DelayPolicy :: Explicit ,
610647 } ,
611648 ) ;
612649 } else {
613650 // Optionally log a warning during genesis build
614651 log:: warn!(
615- "Genesis config for account {:?} has delay {:?} below MinDelayPeriod {:?}, skipping." ,
616- who, delay , T :: MinDelayPeriod :: get( )
652+ "Genesis config for account {:?} has delay {:?} below MinDelayPeriodBlocks {:?}, skipping." ,
653+ who, wrapped_delay , T :: MinDelayPeriodBlocks :: get( )
617654 ) ;
618655 }
619656 }
0 commit comments