7

The actors in my pallet are identified by the tuple (AccountId, CurrencyId) which requires most extrinsics to specify the CurrencyId enum as a separate argument.

Example:

    #[pallet::weight(<T as Config>::WeightInfo::register())]
    #[transactional]
    pub fn register(
        origin: OriginFor<T>,
        currency_id: T::CurrencyId,
    ) -> DispatchResult {
        let account_id = ensure_signed(origin)?;
        let actor_id = ActorId::new(account_id, currency_id);
        ...
        Ok(())
    }

Is it possible to define a custom Origin such that ensure_signed(origin)? returns the pre-constructed tuple?

1
  • What is the problem with your current approach? Mar 29, 2022 at 16:08

2 Answers 2

8

You can add and create custom origins in pallets.

In your pallet, you define the origin type.

use frame_support::pallet_prelude::*;
use pallet::*;
use sp_runtime::traits::BadOrigin;

#[frame_support::pallet]
pub mod pallet {
    use super::*;

    #[pallet::pallet]
    #[pallet::generate_store(pub(super) trait Store)]
    pub struct Pallet<T>(_);

    #[pallet::config]
    pub trait Config: frame_system::Config {}

    /// My custom origin!
    #[pallet::origin]
    #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
    pub enum Origin {
        /// Custom Origin type.
        CustomOrigin(InternalStruct),
    }
}

#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug, TypeInfo)]
pub struct InternalStruct {
    item: u64,
}

/// This is an analogue of `ensure_signed` for the custom origin type.
pub fn ensure_custom_origin<OuterOrigin>(o: OuterOrigin) -> Result<InternalStruct, BadOrigin>
where
    OuterOrigin: Into<Result<Origin, OuterOrigin>>,
{
    match o.into() {
        Ok(Origin::CustomOrigin(internal)) => Ok(internal),
        _ => Err(BadOrigin),
    }
}

And then you can integrate this in construct_runtime! to add your custom origin type to the overarching runtime origin.

construct_runtime! {
    pub enum Runtime where
        Block = Block,
        NodeBlock = OpaqueBlock,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        // .. other pallets
        CustomOriginPallet: custom_origin_pallet::{Pallet, Origin},
    }
}

Per your question about whether you can create a custom origin which modifies the behavior of ensure_signed, the answer is 'no'. ensure_signed only accepts origins of the form frame_system::RawOrigin::Signed(AccountId).

2

I think your current approach looks right.

While it is possible to create custom origins as Rob as shown, it is probably not very ergonomic to use across your pallets.

Instead, as you have shown in your pseudo code, having the user input the currency_id and doing that actor_id calculation / conversion makes sense to me.

In any case, if you do use a custom origin, users would need to interact with your pallet first to create and re-dispatch the new origin. FRAME core on its own is not modular in this way. You would not be able to override the Signed origin type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.