Replies: 11 comments 34 replies
-
|
To add to limitations:
Overall, I think the limitations are quite significant (especially the need to double the note count for network notes), but what I really like about this approach is that If we could achieve the same property with a single-note design, that would be awesome - but I don't have a good proposal on how to do this yet. |
Beta Was this translation helpful? Give feedback.
-
|
Just leaving some thoughts below: I like this idea, but I think we have a classic protocol design tension :) cleaner abstractions on one side (#2968) vs simpler implementation and stronger user guarantees on the other (#2901). Honestly, #2968 is more elegant on paper. Fee logic isn't handled inside network notes themselves, and you get nice composability like the USDT -> USDC swap handled by the batch builder as @bobbinth mentioned. But pulling it off relies on node functionality that seems like it's being removed, not added, and it weakens the guarantee that a user's submitted transaction will actually get committed.
This concern is fair in principle, but the actual change in #2901 is pretty minimal imho. A note script gets one extra line, That's not really mixing concerns in the bad sense. It's closer to a calling convention: when you call into a network account, the first action (or last) is paying the fee. Compare it to The one place #2901 does cause friction is the I don't think either option is "perfect", but I think #2968 is worse in practice (sorry @mmagician 😅). It trades that mild awkwardness for substantial costs this thread already surfaced:
TLDR: #2901 isn't perfect, but the tradeoffs feel reasonable to me. It's the more pragmatic option, and it keeps the fee logic at the protocol layer instead of pushing complexity down to the node. |
Beta Was this translation helpful? Give feedback.
-
|
For the
My understanding is that the fees will be paid when we execute the sponsorship note. So, the task of the sponsorship note script would be:
The above would ensure that we can't consume sponsorship notes unless the feature notes are already in the transaction. But I guess there is a potential attack vector here: someone can create another sponsorship note for the same feature note with intentionally low fees, and this will block consumption of the feature note (assuming the batch builder will try to put all sponsorship notes for a given feature note into a single transaction). A potential workaround for this is for the Overall, I think we need to analyze the issue of "someone intentionally creates a sponsorship note that makes some specific feature note un-consumable" a bit more. |
Beta Was this translation helpful? Give feedback.
-
Separating business and fee logic for network accounts — refined proposal
SummaryNetwork-account developers should never have to think about fee estimation. The note that expresses business intent should avoid fee-routing logic, and the network account should not need to expose or maintain any fee-pricing logic of its own. The mechanism here achieves that with a network account that is a pure pass-through for fees: its only fee-related job is to move the assets it was sponsored with into a builder-facing FeeNote. It does not:
Fee sufficiency is enforced implicitly — an underpaying (network) transaction is simply never included in the batch, so the user is naturally incentivized to pay enough, and the entire sponsored amount flows straight to the batch builder. Terminology
The pass-through procedureThe network account exposes a single, simple procedure. Because it runs within the network account's context, the emitted The user is incentivized to fund the End-to-end flowAssume the network account is already deployed.
Phasing: ship without the mempool firstThe fully atomic version routes everything through the network-transaction-builder mempool using unauthenticated notes, so that note creation, network-account consumption, and builder settlement all land in the same batch with no net notes committed onchain. That dependency is the riskiest part of the design — see Open Questions — so it is not required for v1. Phase 1 (no ntx builder mempool): commit everything onchain. Notes commit across two blocks; we lose atomicity (the The tricky part of splitting execution across blocks. Without the mempool, we commit the
Either way we've committed a Phase 2: add atomicity. With routing the transaction first through the ntx builder mempool, the ntx builder can take the This gives a clean migration path: correctness and the developer-experience benefit now, the footprint/atomicity optimization later. Anti-spam on the network transaction builderEven in the atomic version, a user can flood the ntx builder with transactions that underpay: they will never be included (so the user never pays), yet the ntx builder still spends resources on them. Mitigation: the ntx builder executes but does not prove first. It plainly executes the transaction, checks whether the emitted This removes proving cost from the spam surface. Residual cost remains when the ntx builder still executes each transaction to make the decision. If we could have a way for users to pay the ntx builder even w/o their tx being included onchain, that'd be ideal. This might be achieved through a |
Beta Was this translation helpful? Give feedback.
-
|
A bit of a tangent; but since the primary problem with having network accounts handle fees seems to be complexity for the authors, could we not create some basic standards for this instead? We could start simple e.g. just a constant $$$, maybe add rough scaling with estimates etc. And aspiring authors could optimize this over time? I imagine network accounts could also execute some examples before they deploy to get a good feel for what could happen? Many use cases would be basic notes so costs should be fall within a narrow band fairly deterministically for those iiuc? So we get out the starting blocks with KISS, but leave room for evolution and use case specific optimisations? Authors that dgaf can use the standards and settle for slightly suboptimal, which is what we end up with in alternate implementations regardless I think? |
Beta Was this translation helpful? Give feedback.
-
|
I think the chained-transaction case @PhilippGackstatter raised is the deciding factor for which route we take; we already have this pattern with: B2AGG -> bridge -> BURN -> faucet. Single-note version: the network sponsorship fee would be in the note and represented in the The ntx note chain then composes like this:
The general idea: a network account's fee for a note = its own cost + the fees of the network notes it will create (priced via FPI The sponsorship-note design hits its worst case on this exact chain: every hop needs a fresh sponsorship note, which means either two extra notes per hop or the bidirectional-dependency problems Philipp described. I'd still keep the best part of the pass-through proposal: app devs never write fee logic. If there are no strong objections I'd take this back to #2901 and start on the first PR so we can evaluate it against the agglayer flow in |
Beta Was this translation helpful? Give feedback.
-
Sponsorship note (dry-run, separation of fees)The mechanism of this approach is described in #2968 (comment). It is quite elegant, but there are still some open questions about it in my mind: Chained transactionsHow exactly would "chained transactions" work? Enforcing atomic transactions across more than a single "leg" of the chain is likely not going to be practical. So, in the case of
Currency for application fee paymentsOne of the goals of the fee mechanism is to make sure we can avoid the situation where the user has token The proposal makes payment of protocol fees in multiple tokens trivial - but since the application fees are treated separately, we still have a problem of the user not having the tokens required by some contract. This would bring us back to:
None of these look too attractive - but maybe there is an approach I'm not thinking about. Other potential complications
|
Beta Was this translation helpful? Give feedback.
-
Same note (target account estimates, single fee)I sketched out this approach in #2968 (reply in thread) and some of the following comments, but to describe it fully: The fess are included in the "feature" note. To indicate which asset is supposed to be the fee, we'd create a new attachment - or maybe just bundle this info into the To make the consumption of the notes ergonomic, we'd need to introduce the concept of note prologue/epilogue, and network accounts would be able to remove the fee from the note in the prologue (i.e., right before a note is executed). This would require a new kernel procedure (e.g., The main reason for the above is that we don't need to create different versions of various notes - e.g., The main downsides of this approach are: Requires
|
Beta Was this translation helpful? Give feedback.
-
Sponsorship note (target account estimates, single fee)This is a kind of combination of the two - we still have two separate notes: a feature note and a sponsorship note, but the sponsorship note carries both protocol and application fee. And the way the user gets the fee is by calling something like This removes some of the problems - specifically, chained transactions are not an issue, and token conversions become much simpler (e.g., the batch builder would consume a sponsorship not with token
As I mentioned in the previous comment, I'm not too worried about 2 - but I worries about 1. On the other hand, it does also enable some new capabilities. For example, if a feature not is "stuck" in the mempool because it doesn't have a corresponding feature note with sufficient fees, the user could send another note with more fees and make the feature note consumable. |
Beta Was this translation helpful? Give feedback.
-
Fee network accountI thought a bit more about approach B from here. Having a single global fee account has risks in terms of contention, but in return it enables a network fee approach with very nice properties for the developers, as they don't need to think about protocol fees at all. It also commits no fee-related notes on chain and requires no dry-running or fee estimation. This approach doesn't quite fit neatly into the properties that @bobbinth described, but to spell out the differences:
ApproachCore idea:
To illustrate, consider the
A couple of notes:
The properties of this approach that I see are the following.
Cons:
I want to explore possible solutions to the primary cons in the rest of this post. Multiple NTX buildersThis is not a concern now, but the solution should of course work long-term. The simplest solution is to say that NTX building stays as a centralized standard. Problem solved 😅. Probably not the most scalable solution. Fee Account per NTX builder To rule out fee-related conflicts we could assign one fee account to a ntx builder. This could work if the number of ntx builders is relatively small. The main burden is put on the user, as they need to top up their balance in all of these accounts. So, not that great. Dedicated Fee builder Another way is to have a dedicated ntx builder just for mutating the global fee network account. So, all regular ntx builders build their sets of transactions and directly send the set of One concern is that this builder would need to process (very) large transactions (one There is also the risk of two ntx builders successfully checking the balance of a user independently, executing a transaction but then the cumulative balance of the user isn't sufficient to cover both txs. This can be addressed by requiring a large enough minimum balance before an NTX builder executes a tx for a user. Worst case, one batch of network transactions would have to be dropped to make any progress at all. There is probably a lot more to be discussed about this, but I think directionally it could work. Debit AuthorizationWe want to avoid making feature notes fee-aware, but also have them somehow carry the "fee debit authorization" through to where it's needed. The core idea is to use note attachments, which can be independently added to a note without making the feature logic itself fee-aware. However, it requires extra protocol machinery. Specifically, it would require implementing note and attachment "finalizers". The former was previously and briefly discussed here. To summarize, the main idea is:
These would be useful independently of this fee approach, but are essential for this authorization use case. As a third feature, we'd need the tx kernel to track which note created another note, if any. This, for one, would be a pretty simple addition. Then, we define a new attachment and the Tracking note creatorsThe tx kernel keeps track of this only for the duration of the transaction, and makes it accessible via Example: FeeAuth attachmentWe define a pub struct FeeAuth {
originator: AccountId,
remaining_budget: AssetAmount,
}This attachment's finalizer asserts that one of the following is true:
This results in the following guarantees:
FeeDebit noteWe define the pub struct FeeDebit {
originator: AccountId,
amount: AssetAmount,
}
Budget Conservation We need to ensure that the Define a
AuthNetworkAccountIt estimates the fee of the current transaction using the For the set of unique originators across all input notes (to deduplicate multiple notes from the same creator), it creates a It also iterates the set of output notes and adds a Applying itTo make it more concrete, let's apply this to the B2AGG/BURN transaction chain:
This way, only the notes that originate from Note that in all cases, adding or copying the A malicious network note or network account can drain Bob's balance by at most ConclusionI think that this approach optimizes for this hierarchy of needs:
For the user, it is pretty simple. Top up once. Transactions should be fast. They should basically never fail for fee-related reasons, as long as the balance in the account is large enough. It pushes most of the hard things to the protocol/standards, which is where they should be. I think this approach solves most of the issues of other approaches:
It selects different trade-offs to do this - nothing's for free. It comes at the main cost of additional, significant protocol changes. I deliberately didn't go into details of how this could be implemented for now. These would be useful independently, so it is a net win, from my POV. The other potential cost is the centralized fee account - but I don't see this as a major issue as discussed. |
Beta Was this translation helpful? Give feedback.
-
|
First of all, thanks for the comprehensive discussion, it reads very well thought. And it sounds indeed like a very interesting problem to solve. A couple of thoughts from the product and maybe project-perspective:
So reading the different approaches, I really liked @PhilippGackstatter's proposal. However, in my understanding, we need to change quite a bit of things to make it work. My 2 cents, and maybe I am oversimplifying: Narrowing to two build-fast optionsI think the design space collapses to two candidates, given our actual constraints: (1) we launch centralized (so all protocol fees accrue to us), and Both my options deliberately defer decentralization and treat it as a later migration, not a v1 requirement. Option 1 - fee on the note itself (minimal-change single note)This is the #2901 direction in its lightest form, closest to the EIP-1559-style sketch above.
Cost we accept: fee logic is mildly "infectious" at the asset level. Because the fee asset shares the note vault, any code doing Why it's fast: only new surface is a small accessor to expose the base fee to account code, plus the Option 2 - (might be radical) dedicated fee account with off-chain payment-channel intentsInstead of putting fees in notes, give every user a dedicated fee account holding a prepaid balance, and bill postpaid via user-signed vouchers. Fees never touch business notes at all - full separation of concerns. Instead of @PhilippGackstatter's global account, every user has its own fee account. This is, in my understanding, how institutions want to pay fees, this is how AWS, Cloudflare, etc do it. Predictable fees on a debit account.
Costs we accept:
Why it's fast: no fee logic in any note, no estimation, no mempool work. New code is one How I'd compare them
Both are centralized-first and both avoid the unauthenticated-note mempool propagation that the node is moving away from. My lean is Option 2 for the DevEx and billing story, with Option 1 as the lower-risk fallback if we want to stay entirely within the note/batch-fee model. |
Beta Was this translation helpful? Give feedback.

Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Note: this is WIP
Summary
Proposal #2901 assumes that the note being consumed carries the asset that funds fee payment on behalf of the network account, and that a
FeeManagercomponent can extract that asset from the active note.I want to explore an alternative design where we keep the "feature" note unchanged (no bundling of fee asset) and instead separate network account sponsorship into its own standard note.
To make the terminology precise, I will use:
FeatureNote: the note that encodes the actual user intent, e.g.BURN,P2ID,CLAIM, etc.NetworkSponsorshipNote: a separate note whose only job is to sponsor execution against a specific network account.BuilderFeeNote: the builder-facing note from the batch-level fees discussion in Batch-level fees #2899. I am renaming it here from FeeNote only to avoid ambiguity.The high-level idea is:
FeatureNoteremains as it is today, without bundling a fee asset into it;NetworkSponsorshipNotetargeted to the network account;The main motivation is to avoid overloading the feature note with fee-routing semantics.
The
FeatureNoteexpresses intent, theNetworkSponsorshipNotesponsors network-account execution, and theBuilderFeeNotecompensates the batch builder for inclusion.Background
The current
FeeManagerdirection in #2901 works for some notes, but it also couples the feature note format to fee payment.That creates awkward cases:
BURN: the note is conceptually "burn this asset", but now it also needs to carry the asset that funds fee payment.P2IDto a network account: the note is conceptually "transfer these assets to this account", but it must also embed a fee asset to cover the tx costs.In other words, the current shape mixes two concerns:
I think those should be separate.
Proposed mechanism
Feature notes only encode business logic
FeatureNoteremains focused on the user-visible action:It does not need to carry a separate fee asset just because the consuming account happens to be a network account.
Separate network sponsorship
We introduce a standardized
NetworkSponsorshipNote.This note is explicitly about sponsoring execution against a network account. It is a "fee note", but conceptually different than the batch builder
BuilderFeeNotediscussed in #2899.A
NetworkSponsorshipNoteshould be only consumable by the network account. I think this doesn't require any special provisions outside of what theFeatureNotewould mandate w.r.t. who can consume.Account-level validation
Rather than patching every note script as IIUC, #2901 mandates, the preferred validation point is the network account itself.
Concretely, the network account validates:
End-to-end flow
FeatureNoteexactly as they do today.NetworkSponsorshipNotefor the target network account.Limitations
nothing's for free :(
Input-note form has overhead
If the
NetworkSponsorshipNoteis an extra input note, then it adds an extra note / nullifier footprint.Output-note form has timing constraints
Depending on the order of processing the notes within the transaction, we need to be careful about where validation happens.
I'm not fully convinced yet that our tx kernel can support the flow described in this proposal.
Variant pricing still needs a carrier
Issue #2901 distinguishes different variants of the same note script, e.g. via
(note_script_root, variant_tag).If the
FeatureNoteremains unchanged, we still need to account for different costs of different execution paths.TBD
Beta Was this translation helpful? Give feedback.
All reactions