(source)

Table of Contents

RFC-0105: XCM improved fee mechanism

Start Date23 July 2024
DescriptionAllow multiple types of fees to be paid
AuthorsFrancisco Aguirre

Summary

XCM already handles execution fees in an effective and efficient manner using the BuyExecution instruction. However, other types of fees are not handled as effectively -- for example, delivery fees. Fees exist that can't be measured using Weight -- as execution fees can -- so a new method should be thought up for those cases. This RFC proposes making the fee handling system simpler and more general, by doing two things:

  • Adding a fees register
  • Deprecating BuyExecution and adding a new instruction PayFees with new semantics to ultimately replace it.

Motivation

Execution fees are handled correctly by XCM right now. However, the addition of extra fees, like for message delivery, result in awkward ways of integrating them into the XCVM implementation. This is because these types of fees are not included in the language. The standard should have a way to correctly deal with these implementation specific fees, that might not exist in every system that uses XCM. The new instruction moves the specified amount of fees from the holding register to a dedicated fees register that the XCVM can use in flexible ways depending on its implementation. The XCVM implementation is free to use these fees to pay for execution fees, transport fees, or any other type of fee that might be necessary. This moves the specifics of fees further away from the XCM standard, and more into the actual underlying XCVM implementation, which is a good thing.

Stakeholders

  • Runtime Users
  • Runtime Devs
  • Wallets
  • dApps

Explanation

The new instruction that will replace BuyExecution is a much simpler and general version: PayFees. This instruction takes one Asset, takes it from the holding register, and puts it into a new fees register. The XCVM implementation can now use this Asset to make sure every necessary fee is paid for, this includes execution fees, delivery fees, and any other type of fee necessary for the program to execute successfully.

#![allow(unused)]
fn main() {
PayFees { asset: Asset }
}

This new instruction will reserve the entirety of the asset operand for fee payment. There is not concept of returning the leftover fees to the holding register, to allow for the implementation to charge fees at different points during execution. Because of this, the asset passed in can't be used for anything else during the entirety of the program. This is different from the current semantics of BuyExecution.

If not all Asset in the fees register is used when the execution ends, then we trap them alongside any possible leftover assets from the holding register. RefundSurplus can be used to move all leftover fees from the fees register to the holding register. Care must be taken that this is used only after all possible instructions which might charge fees, else execution will fail.

Examples

Most XCM programs that pay for execution are written like so:

#![allow(unused)]
fn main() {
// Instruction that loads the holding register
BuyExecution { asset, weight_limit }
// ...rest
}

With this RFC, the structure would be the same, but using the new instruction, that has different semantics:

#![allow(unused)]
fn main() {
// Instruction that loads the holding register
PayFees { asset }
// ...rest
}

Drawbacks

There needs to be an explicit change from BuyExecution to PayFees, most often accompanied by a reduction in the assets passed in.

Testing, Security, and Privacy

It might become a security concern if leftover fees are trapped, since a lot of them are expected.

Performance, Ergonomics, and Compatibility

Performance

There should be no performance downsides to this approach. The fees register is a simplification that may actually result in better performance, in the case an implementation is doing a workaround to achieve what this RFC proposes.

Ergonomics

The interface is going to be very similar to the already existing one. Even simpler since PayFees will only receive one asset. That asset will allow users to limit the amount of fees they are willing to pay.

Compatibility

This RFC can't just change the semantics of the BuyExecution instruction since that instruction accepts any funds, uses what it needs and returns the rest immediately. The new proposed instruction, PayFees, doesn't return the leftover immediately, it keeps it in the fees register. In practice, the deprecated BuyExecution needs to be slowly rolled out in favour of PayFees.

Prior Art and References

The closed RFC PR on the xcm-format repository, before XCM RFCs got moved to fellowship RFCs: https://github.com/polkadot-fellows/xcm-format/pull/53.

Unresolved Questions

None

This proposal would greatly benefit from an improved asset trapping system.

CustomAssetClaimer is also related, as it directly improves the ergonomics of this proposal.

LeftoverAssetsDestination execution hint would also similarly improve the ergonomics.

Removal of JIT fees is also related, they are useless with this proposal.