# Representing pallet's origins as System-like origins

**URL:** <https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313>\
**Category:** Tech Talk\
**Tags:** frame\
**Created:** [October 17, 2023, 9:18pm UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313 "2023-10-17T21:18:23Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![pandres95](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.polkadot.network/pandres95/32/1163_2.png) [@pandres95](https://forum.polkadot.network/u/pandres95)\
**Post date:** [October 17, 2023, 9:18pm UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313/1 "2023-10-17T21:18:23Z")

</div>

This post discusses whether a potential solution should enable granular control over representing/casting/converting a pallet origin into a system-wide origin.

## Problem Statement

### Built-in guards and UX

Traditionally, pallet builders have used built-in checks (like [`ensure_signed`](https://docs.rs/frame-system/latest/frame_system/fn.ensure_signed.html), [`ensure_root`](https://crates.parity.io/frame_system/fn.ensure_root.html), or [`ensure_none`](https://crates.parity.io/frame_system/fn.ensure_none.html)) to implement guards on permissionless methods. The reasons behind this decision are pretty simple to explain:

1. Checks are already there.
2. They are simple, ergonomic, and intuitive. They provide the minimum essential guards without the need to think too much about it.
3. Implementing custom guards ([`EnsureOrigin`](https://docs.rs/frame-support/latest/frame_support/traits/trait.EnsureOrigin.html), custom implementations involving `OuterOrigin`s) are usually not necessary unless privileged calls/pallet origins come into place.

### The need for more flexibility

While this isn’t necessarily problematic for most users (especially when pallet developers would expose their intended public APIs via traits), some edge cases require more flexibility.

One of those edge cases is where an arbitrary call is needed to be executed, either via the [`Proxy`](https://docs.rs/pallet-proxy/latest/pallet_proxy/pallet/struct.Pallet.html#method.proxy) or [`Schedule`](https://docs.rs/frame-support/latest/frame_support/traits/schedule/v3/trait.Anon.html#tymethod.schedule) pallets or directly using [`Dispatchable`s](https://docs.rs/sp-runtime/latest/sp_runtime/traits/trait.Dispatchable.html). An example of this case is what’s implemented on [`Collective`](https://docs.rs/pallet-collective/latest/pallet_collective/pallet/struct.Pallet.html#method.close) or [`Referenda`](https://docs.rs/pallet-referenda/latest/pallet_referenda/pallet/struct.Pallet.html#method.submit) pallets, and more recently on [`Communities`](https://github.com/virto-network/virto-node/issues/125) pallet (currently under development).

Implementing such solutions requires designers to think about clean implementations that are limited to the scope of the pallet they’re building or, at least, to give runtime implementors some degree of manageability on which call origins may be used when dispatching an arbitrary call. This is where workarounds and tricks bloom.

### Workarounds

For example, while `Collective`s is opinionated on which origin to use when executing or proposing a poll and is therefore limited to the ensures that might be implemented as valid origins for some pallets, `Referenda` exposes the [`TracksInfo`](https://docs.rs/pallet-referenda/latest/pallet_referenda/trait.TracksInfo.html#) trait to let runtime implementors define whether an origin is bound to a track. This limits the ability of referenda proposals to what the runtime implementors decide. Also, it is less ergonomic, as it doesn’t allow for default behavior.

### Converting a pallet origin into system origin is not possible (or at least, not that easy)

In the specific case of [`Communities`](https://github.com/virto-network/virto-node/issues/125), the initially designed approach was to let a pallet origin be expressed in terms of [`frame_system`’s `Origin`](https://docs.rs/frame-system/latest/frame_system/pallet/type.Origin.html), as to be signed by the community’s [AccountId](https://docs.rs/frame-system/latest/frame_system/pallet/trait.Config.html#associatedtype.AccountId), without intervention from the runtime end or without needing explicitly stating the origin to dispatching the call of the proposal, once approved, so it’s possible to execute some calls that don’t implement custom ensures (such as `system::remark_with_event`).

However, this is not possible by default due to the current constraints of how _FRAME_ is built, and the _workaround_ was to follow a similar approach to what’s presented on the `Referendum` pallet with `TracksInfo`.

While this is not that bad (in the end, it’s a _workaround_ and _it works_), it poses some challenges:

1. Reduces ergonomy since it forces the users of the pallet to declare the signed origin for their community `AccountId`.
2. If a builder is unaware of these restrictions or how to circumvent them, it increases potential points of failure (or, better, points for attacks — let’s not forget how powerful a dispatchable is).
3. While it is possible to implement a solution where a pallet origin defines a conversion to a system origin on their end, which is considered by the `RuntimeOrigin`, this requires implementing a custom `RuntimeOrigin`, not the one provided by _FRAME_. This implies redoing more boilerplate code, degrading the DX.

## Solution

It is theoretically possible to implement a retro-compatible (thus, no need for a major breaking change) solution on `FRAME`, that enables pallet builders to define a pallet’s origin conversion to system Origin that is considered when building `RuntimeOrigin`.

An example of this potential solution goes like this:

```rs
// Define the structure for the pallet origin

pub struct RawOrigin {}

// Implement a failable conversion to `frame_syetem::Origin`

impl<T> trait TryInto<frame_system::Origin<T>> for RawOrigin 
  where
    T: frame_system::Config
{
  type Error = ();

  fn try_into (self) -> Result<frame_system::Origin<T>, ()> {
    todo!("Implement conversion to system origin here")
  }
},

// Declare the structure as pallet origin

#[pallet::origin(as_system_origin)]
pub type Origin = RawOrigin;

```

This would tell FRAME to consider the origins coming from that pallet as possible candidates for an attempted conversion. An approach like this:

```rs
impl From<RuntimeOrigin> for #scrate::__private::sp_std::result::Result<#system_path::Origin<#runtime>, RuntimeOrigin> {
	/// NOTE: converting to pallet origin loses the origin filter information.
	fn from(val: RuntimeOrigin) -> Self {
		match val.caller {
			OriginCaller::system(l) => Ok(l),
			// for #variant_name in #system_convertible_variants
			OriginCaller::#variant_name(x) => x.try_into().map_err(|_| val),
			// /for #variant_name in #system_convertible_variants
			_ => Err(val)
		}
	}
}

```

## Discussion

Of course, a post like this wouldn’t be complete without an invitation to leave feedback and think of the possibilities for this.

---

<div class="post-metadata">

**Author:** ![shawntabrizi](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.polkadot.network/shawntabrizi/32/14_2.png) [@shawntabrizi](https://forum.polkadot.network/u/shawntabrizi)\
**Post date:** [October 17, 2023, 11:15pm UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313/2 "2023-10-17T23:15:59Z")

</div>

Sorry, but I do not quite get the intended goal here.

You want to be able to create some guarantee that a `RuntimeOrigin` (which is the accumulation of all origins in your whole runtime), can always be converted to a `SystemOrigin`, which is one of: `Signed`, `None`, `Root`.

Is that right?

Would this basically a way to give each Pallet their own account id which they can use to created signed origins?

Or what is the goal you are try to achieve specifically?

Perhaps could you write the most minimal before and after code which your idea will solve?

---

<div class="post-metadata">

**Author:** ![xlc](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.polkadot.network/xlc/32/690_2.png) [@xlc](https://forum.polkadot.network/u/xlc)\
**Post date:** [October 18, 2023, 12:09am UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313/3 "2023-10-18T00:09:14Z")

</div>

I don’t really understand the problem that you are trying to solve. Some specific examples?

---

<div class="post-metadata">

**Author:** ![pandres95](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.polkadot.network/pandres95/32/1163_2.png) [@pandres95](https://forum.polkadot.network/u/pandres95)\
**Post date:** [October 18, 2023, 12:11am UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313/4 "2023-10-18T00:11:19Z")

</div>

> [@shawntabrizi](#):
>
> Would this basically a way to give each Pallet their own account id which they can use to created signed origins?

Not exactly, but something more like it.

The intended goal would be to enable a mechanism where a pallet can dispatch calls using their own origin and that this origin can be interpreted as a system origin (either `Signed`, `Root`, or `None`).

## Sample

My pallet has an origin. The origin follows this structure:

```rs
struct RawOrigin<CommunityId, VoteWeight> {
  community_id: CommunityId,
  body_part: BodyPart<VoteWeight>,
}

```

There’s a method in the pallet that allows executing arbitrary calls on behalf of the community using an instance of this origin. To avoid the complexity of handling weights, we schedule the calls. The scheduling looks something like this:

```rs
T::Scheduler::schedule(
	DispatchTime::After(Zero::zero()),
	None,
	Default::default(),
	proposal.origin,
	proposal.call,
)

```

Where `proposal.origin` is an instance of our pallet’s origin, with a specific `community_id`. The idea is that when scheduling a call that checks for `ensure_signed`, we should implement a conversion that looks like this:

```rs
impl<T: Config> TryInto<frame_system::Origin<T>> for pallet::Origin<T> {
	type Error = ();

	fn try_into(self) -> Result<frame_system::Origin<T>, Self::Error> {
		let community_account_id = Pallet::<T>::get_community_account_id(&self.community_id);
		Ok(frame_system::Origin::Signed(community_account_id))
	}
}

```

And that should be enough for a `ensure_signed` to pass, returning that `community_account_id` (that uses `PalletId::<T>::into_sub_account` internally) as the success value.

The expected behaviour would be that if we call a permissionless method that uses `ensure_signed` (such as `system::remark_with_event`), it executes successfully, and an event is emitted.

What currently happens is that it’s not possible to directly cast the `pallet::Origin` as `frame_system::Origin`: this is because of when `ensure_signed` calls for `origin.into()` internally, what `RuntimeOrigin` does is to check whether there’s an instance of `OriginCaller::system`. Any other variant would be immediately rejected.

So, the example I set in the original post (calling `system::remark_with_event`, would fail to return a `BadOrigin` error for this design.

By applying this change, I propose, this cast should be possible to occur.

So it’s not about implementing a default signed origin for pallet’s origins (as the pallet’s account), but letting pallet builders to implement their own conversion).

---

<div class="post-metadata">

**Author:** ![shawntabrizi](https://dub1.discourse-cdn.com/flex005/user_avatar/forum.polkadot.network/shawntabrizi/32/14_2.png) [@shawntabrizi](https://forum.polkadot.network/u/shawntabrizi)\
**Post date:** [October 18, 2023, 12:20am UTC](https://forum.polkadot.network/t/representing-pallets-origins-as-system-like-origins/4313/5 "2023-10-18T00:20:12Z")

</div>

Why must this overhead be brought into frame system.

If you intend to have the logic which is:

```auto
let community_account_id = Pallet::<T>::get_community_account_id(&self.community_id);
Ok(frame_system::Origin::Signed(community_account_id))

```

What is the concern with just dispatching from this pallet with `Signed(community_account_id)`?

It seems to me all of this could be solved by adding a single extrinsic into your pallet:

```auto
#[pallet::call]
pub fn convert_community_to_signed_call(origin: OriginFor<T>, call: Box<RuntimeCall>) -> DispatchResult {
    let community_account_id = ensure_community_and_get_account(origin)?;
    call.dispatch(frame_system::RawOrigin::Signed(community_account_id)?
}

```

Then users simply can dispatch their `community` origin calls through this function to generate a signed origin call. In this case, there is less “magic” and you are not pushing further requirements or complexity up the FRAME stack.

For example, from a types perspective, it is not obvious to me that most origins should be convertible to a `frame_system::RawOrigin`. This is probably why I didn’t understand your post to begin with. Having the origin represent a community, and that community having an account id seems very specific to certain scenarios, and ones you can support directly in your pallet.
