moonbeam icon indicating copy to clipboard operation
moonbeam copied to clipboard

Girazoki refactor xcm transactor

Open girazoki opened this issue 2 years ago • 0 comments

What does it do?

This PR goes through a extensive refactor on the XCM-transactor pallet. The main goal of this PR is to allow users and SC to use xcm-transactor and to send Transact messages to other consensus systems without relaying on the pallet storage. Currently if we want to send Transact messages to other chains we need to set in storage

  • How such chain charges for weight in the case of XCM
  • How such chain charges for fee

Although this was done to favour UX, this does not scale well with more and more interactions with other chains being initiated. This PR makes it possible for the user to insert those two values, which presumably would be derived offchain by the user itself.

This PR breaks the existing xcm-trasactor substrate API, but to the best of my knowledge, this pallet was not being used through substrate. It does not break the ethereum (precompile API) Here are the main changes.

Extrinsics Before We currenty have the following extrinsics, which among other things:

  • Do not allow to insert the fee amount to be spent in the destination chain. This is calculated from the DestinationAssetFeePerSecond storage item. This storage item needs to be populated as an "enabler" to be able to Transact in such chain.

  • Do not allow to insert the overall weight to be spent, only the weight to be spent in the transaction (dest_weight). The added cost is automatically added from a storage item called TransactInfoWithWeightLimit.

  • Have a separate extrinsic for transact_through_xxx and transact_through_xxx_multilocation. The only difference between these two is the way the fee payment asset to be used is represented, either as currencyId or as MultiLocation

  • transact_through_derivative_multilocation: Transact through a derivative address of the sovereign account, thanks to the utility.as_derivative function. It requires the index to be registered in the pallet through the register extrinsic. Fees are paid by the sovereign account, hence this extrinsic first burns the same amount of the xc-20 asset representation. Fee payment asset is specified as a multilocation

pub fn transact_through_derivative_multilocation(
			origin: OriginFor<T>,
			dest: T::Transactor,
			index: u16,
			fee_location: Box<VersionedMultiLocation>,
			dest_weight: Weight,
			inner_call: Vec<u8>,
		)
  • transact_through_derivative: Transact through a derivative address of the sovereign account, thanks to the utility.as_derivative function. It requires the index to be registered in the pallet through the register extrinsic. Fees are paid by the sovereign account, hence this extrinsic first burns the same amount of the xc-20 asset representation. Fee payment asset is specified as a currencyId
pub fn transact_through_derivative(
			origin: OriginFor<T>,
			dest: T::Transactor,
			index: u16,
			currency_id: T::CurrencyId,
			dest_weight: Weight,
			inner_call: Vec<u8>,
		) 
  • transact_through_sovereign: Transact through the sovereign account directly. Fees are paid by the sovereign account, hence this extrinsic first burns the same amount of the xc-20 asset representation. Fee payment asset is specified as a multilocation. Only callable by root.
pub fn transact_through_sovereign(
			origin: OriginFor<T>,
			dest: Box<VersionedMultiLocation>,
			fee_payer: T::AccountId,
			fee_location: Box<VersionedMultiLocation>,
			dest_weight: Weight,
			call: Vec<u8>,
			origin_kind: OriginKind,
		)
  • transact_through_signed: Transact through a signed account derived from the multilocation representing the caller of this function in the destination chain. Fees are paid by the derived signed account, and the user should make sure this account has sufficient balance in the destination chain to succeed. Fee payment asset is specified as a currencyId.
pub fn transact_through_signed(
			origin: OriginFor<T>,
			dest: Box<VersionedMultiLocation>,
			fee_currency_id: T::CurrencyId,
			dest_weight: Weight,
			call: Vec<u8>,
		) -> DispatchResult
  • transact_through_signed_multilocation: Transact through a signed account derived from the multilocation representing the caller of this function in the destination chain. Fees are paid by the derived signed account, and the user should make sure this account has sufficient balance in the destination chain to succeed. Fee payment asset is specified as its multilocation.
pub fn transact_through_signed_multilocation(
			origin: OriginFor<T>,
			dest: Box<VersionedMultiLocation>,
			fee_location: Box<VersionedMultiLocation>,
			dest_weight: Weight,
			call: Vec<u8>,
		) -> DispatchResult

Extrinsics After

  • transact_through_derivative and transact_through_derivative_multilocation have been joint into a single extrinsic transact_through_derivative. The difference between the two was just the way to express the fee currency (CurrencyId vs Multilocation). I believe this creates a lot of confusion, and thus I merged into a single extrinsic transact_through_derivative in which the fee currency is specified as an Currency enum, with AsCurrencyId and AsMultiLocation variants.

  • transact_through_derivative: Transact through a derivative address of the sovereign account, thanks to the utility.as_derivative function. It requires the index to be registered in the pallet through the register extrinsic. Fees are paid by the sovereign account, hence this extrinsic first burns the same amount of the xc-20 asset representation. Fee payment asset is specified as a currencyId

transact_through_derivative(
			origin: OriginFor<T>,
			// destination to which the message should be sent
			dest: T::Transactor,
			// derivative index to be used
			index: u16,
			// fee to be used
			fee: CurrencyPayment<CurrencyIdOf<T>>,
			// inner call to be executed in destination. This wiol
			// be wrapped into utility.as_derivative
			inner_call: Vec<u8>,
			// weight information to be used
			weight_info: TransactWeights,
	     )
  • transact_through_signed and transact_signed_derivative_multilocation have been joint into a single extrinsic transact_through_signed. The difference between the two was just the way to express the fee currency (CurrencyId vs Multilocation). I believe this creates a lot of confusion, and thus I merged into a single extrinsic transact_through_signed in which the fee currency is specified as an Currency enum, with AsCurrencyId and AsMultiLocation variants.
pub fn transact_through_signed(
			origin: OriginFor<T>,
			// destination to which the message should be sent
			dest: Box<VersionedMultiLocation>,
			// fee to be used
			fee: CurrencyPayment<CurrencyIdOf<T>>,
			// call to be executed in destination
			call: Vec<u8>,
			// weight information to be used
			weight_info: TransactWeights,
		) 
  • transact_through_signed, transact_through_derivative and transact_through_sovereign do not accept just the Currency, but additionally an optional fee_amount in a new struct of the following form:
pub struct CurrencyPayment<CurrencyId> {
		// the currency in which we want to express our payment
		pub currency: Currency<CurrencyId>,
		// indicates whether we want to specify the fee amount to be used
		pub fee_amount: Option<u128>,
	}

fee_amount was before derived from storage, but this required maintenance on how each chain handles fees. In order to avoid this dependency on a storage item that is populated via democracy, we allow the user to customize the amount it wants to use for fee. If fee_amount is None, the amount will try to be derived from storage.

  • transact_through_signed, transact_through_derivative and transact_through_sovereign do not accept just the transact_weight, but additionally an optional overall_weight in a new struct of the following form:
pub struct TransactWeights {
		// the amount of weight the Transact instruction should consume at most
		pub transact_require_weight_at_most: Weight,
		// the overall weight to be used for the whole XCM message execution. If None,
		// then this amount will be tried to be derived from storage.  If the storage item
		// for the chain is not populated, then it fails
		pub overall_weight: Option<Weight>,
	}

overall_weight was before derived from storage, but this required maintenance on how each chain weights XCM message. In order to avoid this dependency on a storage item that is populated via democracy, we allow the user to customize the overall_weight it wants to use. If overall_weight is None, the amount will try to be derived from storage.

Storage No changes to storage

Config No changes to Config

Precompiles New methods have been added, named as before (i.e., transact_through_signed, transact_through_signed_multilocation, transact_through_derivative, transact_through_derivative_multilocation) but with additional paremeters. This is called function overloading. The new precompile functions are:

  • transact_through_derivative_multilocation:
function transact_through_derivative_multilocation(
        uint8 transactor,
        uint16 index,
        Multilocation memory fee_asset,
        uint64 transact_require_weight_at_most,
        bytes memory inner_call,
        uint256 fee_amount,
        uint64 overall_weight
    ) external
  • transact_through_derivative:
function transact_through_derivative(
        uint8 transactor,
        uint16 index,
        address currency_id,
        uint64 transact_require_weight_at_most,
        bytes memory inner_call,
        uint256 fee_amount,
        uint64 overall_weight
    ) external;
  • transact_through_signed_multilocation:
 function transact_through_signed_multilocation(
        Multilocation memory dest,
        Multilocation memory fee_location,
        uint64 transact_require_weight_at_most,
        bytes memory call,
        uint256 fee_amount,
        uint64 overall_weight
    ) external;
  • transact_through_signed:
  function transact_through_signed(
        Multilocation memory dest,
        address fee_location_address,
        uint64 transact_require_weight_at_most,
        bytes memory call,
        uint256 fee_amount,
        uint64 overall_weight
    ) external;

What important points reviewers should know?

Is there something left for follow-up PRs?

Ideally we should add new options to the API to be able to insert a refund address. This can come in a subsequent PR

What alternative implementations were considered?

Are there relevant PRs or issues in other repositories (Substrate, Polkadot, Frontier, Cumulus)?

What value does it bring to the blockchain users?

girazoki avatar Aug 04 '22 10:08 girazoki