Transaction Fees on the Registry - Requirements, Flows, Plan, and Questions

Hi!

In this past week, we have been researching and discussing a lot about blockchain transaction fees, their support and implementation in Substrate, and how exactly we want to implement them in our Radicle Registry project.

We have read on why blockchain fees are broken, how to doing fees right, and looked at xprl’s model for guidance.

After some good discussions and feature polishing on both our Notion issue and on the GitHub issue, we reached a sweet spot where I’d like to share with you the requirements and flow for the first iteration of this feature.

What

We want our users to bid an amount they are truly willing to pay, using a simplistic fee setup.
That means that every issued transaction must be accompanied by a positive balance that should cover the single base_fee. The remainder is used as a tip, used to gain priority accordingly.

Requirements
For the sake of clarity, requirements are conditions that need to be met to implement this feature the way we envision it.

  • R0: Transactions must be validated pre-dispatch, where the transaction author is verified to have enough funds to cover the respective transaction fees.

  • R1: Allow tx authors to specify the total fee they are willing to pay.
    This bid should cover all internal fees and the remainder be used as a tip to get the tx priority.

  • R2: Be able to control who pays the tx fee
    For org-related transactions, we want the org’s account to be the one paying for the transaction.

  • R3: Only charge base_fee and tip
    In the future, we might include other sub fees, such as weights, but for now, we go-ahead with a
    base_fee and tip.

  • R4: From the withdrawn amount from the transaction payee, burn only a small percentage.

  • R5: Transactions must only run if the fee can be withdrawn successfully

Flow

Consider having tx author, TA , and the registry, R.

  1. TA will issue a transaction, T , to R . In this transaction, TA will indicate a fee amount F that they are willing to pay.

  2. Pre-dispatch, R will validate that TA has enough funds to cover the transaction fees.

  3. R will deduct base_fee from F, and use the remainder as a tip, used to prioritize T accordingly.

  4. R will withdraw F from the end account (see R2).
    In case of success, T will run (see 4.), otherwise, an error will be returned and T is dismissed.

  5. R will burn a small percentage of the withdrawn balance and transfer the rest to an account.
    (I need more information about what account that should be but it’s non-blocking).

Plan

Given R3 and especially R2, I am considering implementing a simple fee-charging system of our own.
Especially R2 since Substrate only runs a transaction if it succeeds to charge the fee beforehand, while
to meet this requirement we need much more control, to be able to figure whether the tx author is a member of the org in question (for org-related transactions, that is).

I will quickly get in touch with Substrate to make sure that’s not supported at the moment, since using their own system also has its advantages, such as being able to add more levels of fees almost for free.

Question

@cloudhead, @lftherios

Since org-related transactions should be withdrawn from the involved org’s account, we must authorize the tx author as a member of that org. Otherwise, it’d be an open door to destroy an org’s funds.

Doing so also means that there’s still an open door to issue transactions that will cause stress on the runtime (checking if the tx author belongs to the involved org). Note that a bad actor could - following the design we have at this point - issue as many transactions involving an org they do not belong to and pay nothing for it.

A solution could be to charge the base_fee as early as possible and only later the tip when these authorizations steps can take place.

Let me have your thoughts!

Nuno
Think Radicle :seedling:

1 Like

The answer to this following question may be obvious to others, but not me at least :sweat_smile: What are the users bidding on and what are they paying for? Let me know if I need to do some reading of the other links to get that context :slight_smile:

@fintohaps, I am also completely new to the topic. That’s a rather fair question that I hope to answer well enough.

The users pay a (total) fee to the network when they issue a transaction in order for that transaction to be processed. Something somewhat similar to highway fees. Now, that total fee can be made up of many sub fees. Substrate documents them pretty well:

final_fee = base_fee + targeted_fee_adjustment(len_fee + weight_fee) + tip;

`base_fee`: This is the minimum amount a user pays for a transaction. 
`len_fee`: This is the amount paid merely to pay for the size of the transaction
`weight_fee`: This amount is computed based on the weight of the transaction. Unlike len_fee this is not input dependent and reflects the complexity of the execution and the time it consumes. `targeted_fee_adjustment`: This is a multiplier that can tune the final fee based on the congestion of the network. 
`tip`: [optional] if included in the transaction, it will be added on top. Only signed transactions can have a tip.

Processing a transaction costs resources: CPU, storage, etc. Having these fees in place helps to avoid spam and DOS, since submitting transactions comes at a cost to the authors.

In our case, in this first iteration, we want the users to specify beforehand how much they are willing to pay to get their transaction processed by the network, where we will only charge a base_fee; The remainder will be used as a tip. Tips are used to prioritise transactions in the queue, like in an action. If user A pays their fee plus tips 10 RADS and user B also pays their fees plus tips 20 RADS, then - especially in times of considerable congestion - the smartest thing to do is to take user B’s transaction first, since it provides the best tip and thus the best value.

Let me know if this explanation helps! For further info, you can also read the Substrate docs on Transaction Fees.

Further questions are more than welcome, especially since they put to test my fresh and baby knowledge on the subject :slight_smile:

1 Like

That makes total sense! Put in the context of these requests using resources it’s clear that we need to create an economy around the use of those resources. I think I always ignore the fact that my card payments incur a fee in the background :sweat_smile: Unless I’m in Berlin that is and they don’t accept my Visa card :joy:

Tipping makes me wonder a bit… It shifts the weight towards someone who can afford more which might be viewed as unfair in some scenarios. Interesting nonetheless.

Thanks for the explanation :heart: Looking forward to seeing more of these posts from the registry :grin:

1 Like

Tipping makes me wonder a bit… It shifts the weight towards someone who can afford more which might be viewed as unfair in some scenarios. Interesting nonetheless.

I wouldn’t necessarily worry so much about one single individual but more about organisations who would, in practical terms, own the network. I have to do some more reading on this.

Thanks for the explanation :heart: Looking forward to seeing more of these posts from the registry :grin:

My pleasure, Fintan :yellow_heart:

Well ya, replace “someone” for “entity”, but the point still applies :smile:

Alternativley we could withdraw the fee from the organization when the author is a member and withdraw the fee from the author account otherwise. This would prevent attacks where the author only pays the base fee while being able to set an arbitrarily high tip. One could also think about this as a reimbursement where the author first pays from their account and later (in the same transaction) gets reimbursed by the org account.

But even in this case we should have a mechanism that prevents org members from specifying arbitrarily large bids. Consider an org member with very limited permissions, say accepting proposal on one project. Without any guards this member would be able to bankrupt the organization.

Does this mean we don’t charge the tip for now?

What is the “withdrawn” amount? From the context it becomes clear but I think it would help stating it clearly.

This is a really cool idea.

Good point. I wonder what’s the best way to handle this. Perhaps it could be something set at the org contract level when we have those. I’m not sure what a good interim solution would be, except maybe having the tip split between the org and the user… I don’t like so much the idea of having a max tip, since it would be hard to set and is inflexible in situations where the network is congested.

Yes, this looks like the right approach to me.

I’m not sure we need an interim solution. At the moment a member can bankrupt an org by sending money and does not need fees for that. So no need to fix this issue now. I just wanted to raise that point so that we keep it in mind.

Does this mean we don’t charge the tip for now?

No. The intent was to clarify that from all the types of transaction costs we could adopt, only a base_fee would be applied, but I understand that it’s misleading. Updated it to include the tip.

What is the “withdrawn” amount? From the context it becomes clear but I think it would help stating it clearly.

Addressed!

For reference, you can find the final implementation here.

Thank you all!