CIP: 0104
  Layer: Canton, Daml
  Title: Traffic-Based App Rewards
  Author:
    Simon Meier
  License: CC0-1.0
  Status: Proposed Amendment
  Approved:
  Type: Tokenomics
  Created: 2026-01-29
  Post-History: https://lists.sync.global/g/cip-discuss/topic/cip_0104_switching_to/117528378,
                https://lists.sync.global/g/cip-discuss/topic/cip_0104_amendment_featured/120047885
# CIP 0104: Traffic-Based App Rewards ## Abstract This CIP proposes to improve the quality of the app reward incentives by removing featured app markers and instead basing an app’s rewards on the actual traffic spent on (sub-)transactions that change the state managed by the app. This is achieved by measuring traffic spent directly on the Global Synchronizer using only sequencer and mediator data. This removes the need for app builders to create `FeaturedAppActivityMarker`s in all the right places, and instead measures the *actual burn* contributed by *all activity* of an app provider party by default. The computations for app rewards implement the [Featured App Guidance v2.0b](https://docs.google.com/document/d/1pLDUen2wpEcfS2pdPtF8u8DNWVHtgb6GIDpgAeiuWGE/edit?tab=t.0#heading=h.22k6ymijl3pg). To enable validator operators to manage the traffic costs incurred by validating (as opposed to submitting) transactions and to foster decentralization of apps and wallets, this CIP also proposes to make protocol-conformant confirmation responses free. Thus validator nodes only pay for the submission of transactions by their users, which is an action that validators can explicitly gate, and charge for, if required. This CIP further proposes to implement these changes in a way that improves the throughput of the Global Synchronizer, and reduces the transaction cost of app transactions. Concretely it proposes to not use the `DSO` party by default in app transactions for the purpose of recording app activity. Thereby reducing the size of app transactions, and avoiding the need for the SV nodes to validate and confirm extra sub-transactions concerning the `DSO` party. The option for creating app activity records using the `DSO` party's authority remains available as an extension point for governance or operational processes (e.g., the integration of [dedicated synchronizers](https://lists.sync.global/g/cip-discuss/topic/cip_0129_universal_burn_and/121668399) with app rewards). ## Specification ### Adjust Featured App Reward Cap Adjust the featured app reward cap to allow minting at most 79 cents for $1 of burn: - set `issuanceCurve.initialValue.featuredAppRewardCap` in `AmuletConfig` to `0.79` ### Introduce Featured App Metadata Extend featured app rights with three kinds of metadata relevant for the computation of app activity records: - **app category**: one of **basic featured app**, **featured asset-moving app**, or **featured asset** as explained in [Rationale](#rationale). Default: basic featured app - **app operations parties**: extra parties in addition to the featured app party itself that are used by the app to submit transactions in its name. Default: empty list - **app composition weight**: the weight of the app used when [splitting extra burn among featured assets](#splitting-extra-burn-among-assets). Default: `1.0` This metadata is managed through SV voting. The SV app checks on SV votes that the operations parties of featured apps are distinct. The ability for apps to specify extra operations parties provides greater flexibility for organizing their operations. For example, the app may delegate certain operations to special parties to enhance security. This is commonly done to implement decentralized applications (e.g. cBTC), which decentralize the featured app party and use delegation to operator parties to submit transactions on behalf of the app. ### Extend Amulet Configuration Add the following parameters to the Amulet configuration: * `extraFeaturedAssetsActivityWeight : Decimal` with default `0.2` * `maxExtraActivityWeightPerAsset : Decimal` with default `0.1` * `dsoAppActivityWeight : Decimal` with default `0.1` The `extraFeaturedAssetsActivityWeight` configures the extra activity weight shared among featured assets when they are used in transactions submitted by featured asset-moving apps. The `maxExtraActivityWeightPerAsset` sets an upper limit on the extra activity weight that any single featured asset can receive. The `dsoAppActivityWeight` configures the weight applied to the DSO party when [splitting extra burn among featured assets](#splitting-extra-burn-among-assets). ### Record App Activity Based on Sequencer and Mediator Data Note that the confirmers of a view are the parties whose validator nodes must validate the view and send a positive confirmation response for the (sub-)transaction contained in the view to be committed. This CIP proposes to change the app activity recording such that the traffic cost of a successful confirmation request is granted to the app provider parties proportional to the envelope sizes of the envelopes on which they appear as confirmers. The app provider parties are computed deterministically by reading `FeaturedAppRight`s as of the time that the mining round opens. These records are ingested by the SV app based on the mediator verdicts from the mediator scan API, and from the sequencer using a new sequencer traffic scan API. They replace the use of `FeaturedAppActivityMarker`s and `AppRewardCoupon`s as activity records. The Daml code for `splice-amulet` is changed such that neither `FeaturedAppActivityMarker` nor `AppRewardCoupon` contracts are created anymore. #### Round attribution Recall that mining rounds are preannounced and there are always at least two rounds that are open for activity recording, as shown in the following diagram copied from [Page 19 of the Canton Coin whitepaper](https://www.canton.network/hubfs/Canton%20Network%20Files/Documents%20\(whitepapers,%20etc...\)/Canton%20Coin_%20A%20Canton-Network-native%20payment%20application.pdf). ![Overview diagram of minting rounds and their phases](cc_minting_rounds.png) This CIP proposes to assign the activity records resulting from a confirmation request to the earliest round that is open at the time the confirmation request was sequenced. That round is always guaranteed to be open for activity recording, and closes the soonest. ### App and Asset Activity Record Computation We record two kinds of activity: featured app activity and featured asset activity. The latter is used for recording usage of featured assets within featured asset movers. Let `totalBurn` be the amount of traffic bytes consumed by the confirmation request. At the top level, the app and asset activity record computation works as follows: 1. Determine whether the transaction was submitted by a user or a featured app. 2. If it is user-submitted, then split total burn among the featured app confirmers proportional to the sizes of the envelopes they confirm. The burn is recorded as featured app activity independently of the qualification of the involved apps. 3. If it is featured-app submitted, then 1. attribute the `totalBurn` as app activity to the submitting app and 2. record extra **asset activity** if the submitting app is a featured asset mover by splitting `totalBurn * extraFeaturedAssetsActivityWeight` among the involved featured assets proportional to the sizes of the envelopes they confirm and apply special provisions so that: - no asset receives more than `totalBurn * maxExtraActivityWeightPerAsset` - the excess activity from capped assets is redistributed among the remaining assets in proportion to their uncapped activity - app composition weights are applied to the resulting asset activity records - Canton Coin is considered a featured asset whose asset activity is assigned to the submitting app We explain the details of the computation steps in the three sections below. #### Determining app-submitted transactions A transaction submitted by the list of parties `submitters` is considered to be **submitted by the featured app `X`** if these three conditions and the tie-breaker criterion below are met: 1. `X` is a confirmer. 2. `X` is a featured app at the start time of the round associated to the transaction. 3. `X` or one of `X`'s operational parties is included in `submitters`. Ties are broken by first selecting the app whose submitter appears earliest in the list of `submitters` and then by selecting the featured app with the lexicographically smallest identifier. Note that these tie breaking rules are technical provisions that should never trigger. The SVs ensure that there are no duplicate featured app rights and that the operations parties of different rights are distinct. #### Splitting Total User Burn For user-submitted transactions, the `totalBurn` is split among the featured app confirmers as follows: 1. Compute the confirmers of an envelope as the union of the confirmers of the views in that envelope. Use the view hashes visible to the sequencer on the confirmation request to determine the views contained in the envelope. 2. Determine the app confirmers as the parties for which a `FeaturedAppRight` is active at round start time. 3. Determine `appEnvelopeBurn[A]` for every app confirmer `A` by summing up `envelopeBurn[E] / numAppConfirmers[E]` for every envelope `E` with `A` as an app confirmer. 4. Determine `totalAppBurn[A]` for every app confirmer `A` as the sum of `appEnvelopeBurn[A]`. 5. Determine `appActivity[A]` for every app confirmer `A` as `(totalBurn * appEnvelopeBurn[A]) / totalAppBurn[A]` and store the resulting app activity for each app confirmer. Note that the computation in Step 5 takes care of distributing the traffic cost not attributed to app envelopes in a weighted fashion among the app envelopes. See the [Example: Views and Envelopes for DvP settlement](#example-views-and-envelopes-for-dvp-settlement) section for concrete calculation examples. The computation is performed using integer arithmetic for efficiency reasons. For confirmation requests whose traffic size is below 100 MB, they can be performed with 64-bit signed integers without risk of overflow. #### Splitting Extra Activity Records Among Assets Note that transactions submitted by a featured app always attribute `totalBurn` of app activity to the submitting featured app. If the submitting app `X` has a `FeaturedAppRight` marking it as a featured asset mover at round start, then an extra amount of `totalBurn * extraFeaturedAssetsActivityWeight` of app activity is split among the involved featured assets as follows: 1. Determine the asset confirmers as the parties which are either the `dso` party or whose `FeaturedAppRight` marks them as featured asset providers at round start time. 2. Determine `assetEnvelopeBurn[A]` for party `A` by summing up `envelopeBurn[E] / numAssetConfirmers[E]` for every envelope `E` with `A` as an asset confirmer. 3. Determine `totalAssetBurn` as the sum of `assetEnvelopeBurn[A]` for every asset confirmer `A`. 4. Determine `assetActivityUncapped[A]` for every asset confirmer `A` as `(floor(totalBurn * extraFeaturedAssetsActivityWeight) * assetEnvelopeBurn[A]) / totalAssetBurn`. 5. Determine `assetActivityCap` as `floor(maxExtraActivityWeightPerAsset * totalBurn)`. 6. Determine `assetActivityCapped[A]` for every asset confirmer `A` as `min(assetActivityUncapped[A], assetActivityCap)`. 7. Determine `totalCappedActivity` as the sum of `assetActivityUncapped[A] - assetActivityCapped[A]` for every asset confirmer `A` and redistribute it among the assets with uncapped activity proportional to their uncapped activity: 1. Determine `totalUncappedActivity` as the sum of `assetActivityUncapped[A]` for every asset confirmer `A` with `assetActivityUncapped[A] < assetActivityCap`. 2. Determine `cappedActivityShare[A]` for every asset confirmer `A` with `assetActivityUncapped[A] < assetActivityCap` as `(totalCappedActivity * assetActivityUncapped[A]) / totalUncappedActivity`. 3. Update `assetActivityCapped[A]` for every asset confirmer `A` with `assetActivityUncapped[A] < assetActivityCap` as `min(assetActivityUncapped[A] + cappedActivityShare[A], assetActivityCap)`. 4. Note that for simplicity, redistribution is not iterated and any remaining excess activity is effectively lost. However, this cannot happen as long as `maxExtraActivityWeightPerAsset >= extraFeaturedAssetsActivityWeight / 2` 8. For every asset confirmer `A != dso`, record asset activity `floor(assetActivityCapped[A] * appRight[X].appCompositionWeight * appRight[A].appCompositionWeight)`. 9. Record asset activity `floor(assetActivityCapped[dso] * appRight[X].appCompositionWeight * amuletConfig.dsoAppActivityWeight)` for the featured asset-moving app `X`. The computations are by default performed using Int64 arithmetic except for the multiplications with the `Decimal` configuration parameters. These are performed using Daml's `Decimal` arithmetic. `floor` is applied to avoid over-issuance due to rounding up. The computations in Step 7 ensure that in a DvP of two assets, both asset providers always receive equal shares of the `totalBurn` independently of the relative sizes of the envelopes affecting them. For batch settlements involving multiple assets with different DvP envelope sizes, some excess activity may in theory remain undistributed, but only if the parameters were changed such that `maxExtraActivityWeightPerAsset <= extraFeaturedAssetsActivityWeight / 2`. Nevertheless, asset activity is approximately distributed relative to the amount of change to each asset's ownership register. See the [calculation examples](#example-views-and-envelopes-for-dvp-settlement) for more details. ### App Activity Recording Extension Hook This extension hook allows future CIPs to define additional mechanisms for generating app and asset activity without having to do any modifications to the core activity recording logic. We expect for example that the integration of [dedicated synchronizers](https://lists.sync.global/g/cip-discuss/topic/cip_0129_universal_burn_and/121668399) with app rewards will make use of this hook. It requires the following changes. Add a dedicated template and choice along the following lines: ``` -- | Template for recording activities via special governance or -- operational processes (e.g., the reporting of app activity on a dSync). template ActivityRecorder with dso : Party where signatory dso nonconsuming choice ActivityRecorder_DsoRecordActivity : () with appActivity : [(Party, Int)] -- ^ App activities to record for the given featured app parties, denominated in traffic bytes. assetActivity : [(Party, Int)] -- ^ Asset activities to record for the given featured asset parties, denominated in traffic bytes. controller dso do pure () ``` Note that the choice is DSO-controlled. It is meant to be called by templates that implement operational processes that need to report app activity that is not directly visible from the mediator and sequencer. The Scan app ingests all calls to the `ActivityRecorder_DsoRecordActivity` choice. It uses the record time of the transaction containing the call to determine their associated round, and creates: - Extra app activity records for the `appActivity` of all parties that are featured app providers at round start. - Extra asset activity records for the `assetActivity` of all parties that are featured asset providers at round start. The [reward computation](#app-reward-computation) takes these extra activity records into account. ### App Reward Computation The computation is structured such that the available minting allowance for a round is first used to reward featured apps and, if any minting allowance remains, it is used to reward featured assets. Thereby making the rewards for featured app submitted transactions more predictable. The CIP proposes to use the following computation to compute the minting allowances for app provider parties in a round: 1. Retrieve the `round` contract associated with the current round and compute from it the `roundMintingAllowanceCC`. 2. Retrieve the `amuletConfig` from the `AmuletRules` contract at round start time and compute from it the `traffic_price_in_CC_perMB`. 3. Compute `totalAppActivity` and `totalAssetActivity` by summing up the sequencer-derived app and asset activity records for the round. 4. Compute `totalExtraAppActivity` and `totalExtraAssetActivity` by summing up the app and asset activity values from calls to `ActivityRecorder_DsoRecordActivity` associated with this round. 5. Compute the following intermediate values: - `totalAppBurnCC = ((totalAppActivity + totalExtraAppActivity) / 1e6) * traffic_price_in_CC_per_MB` - `totalAssetBurnCC = ((totalAssetActivity + totalExtraAssetActivity) / 1e6) * traffic_price_in_CC_per_MB` - `appMintingAllowanceCC = min(roundMintingAllowanceCC, round.featuredAppRewardCap * totalAppBurnCC)` - `assetMintingAllowanceCC = min(roundMintingAllowanceCC - appMintingAllowanceCC, round.featuredAppRewardCap * totalAssetBurnCC)` - `issuancePerAppBurnCC = appMintingAllowanceCC / totalAppBurnCC` if `totalAppBurnCC > 0.0` else `0.0` - `issuancePerAssetBurnCC = assetMintingAllowanceCC / totalAssetBurnCC` if `totalAssetBurnCC > 0.0` else `0.0` 6. Compute the `appMintingAllowance[A]` for every party `A` as: - `appBurnCC[A] = ((totalAppActivity[A] + totalExtraAppActivity[A]) / 1e6) * traffic_price_in_CC_per_MB` - `assetBurnCC[A] = ((totalAssetActivity[A] + totalExtraAssetActivity[A]) / 1e6) * traffic_price_in_CC_per_MB` - `appMintingAllowance0[A] = appBurnCC[A] * issuancePerAppBurnCC + assetBurnCC[A] * issuancePerAssetBurnCC` - `appMintingAllowance[A] = if appMintingAllowance0[A] < appRewardCouponThresholdCC then 0.0 else appMintingAllowance0[A]` Featured asset rewards are capped by `round.featuredAppRewardCap`, just like featured app rewards. With the default `maxExtraActivityWeightPerAsset` of `0.1` and a cap of `0.79`, an asset can receive at most `0.1` activity record per $1 of burn, corresponding to at most $0.079 in app rewards. All computations that involve CC are performed using decimals with the same precision and rounding mode as Daml Decimals. ### App Reward Accounting This CIP proposes to change the accounting of the minting allowance of app provider parties away from using *many* `FeaturedAppActivityMarker` and `AppRewardCoupon` contracts per party per round, and instead have the DSO create exactly one coupon per round for every party whose app rewards for the round are equal to or greater than the `appRewardCouponThresholdCC` (new Amulet config parameter; default, $0.5). The coupons are valid for `appRewardCouponLifetime` (new Amulet config parameter; default, 24h) measured from the time they are created. This allows app providers to batch their minting across multiple rounds and thus lower their traffic cost, and the load on the DSO party. The coupon amounts are computed as follows: 1. All SV apps ingest the app activity records using the data from their associated mediator and sequencer to [compute the activity records](#app-and-asset-activity-record-computation). 2. All SV apps compute the per-party app rewards for the round as [described in the “App Reward Computation”](#app-reward-computation) section below. 3. All SV apps agree on them with a super-majority of the other SV apps, using an on-ledger workflow. 4. All SV apps run decentralized automation that creates exactly one app reward coupon for every party with a non-zero minting allowance. App rewards for parties below the threshold are burned. Note that the minting workflows for SV rewards, validator rewards, and the ecosystem development fund from [CIP-82](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0082/cip-0082.md) remain unchanged as part of this CIP. Only the workflow for app rewards is changed. #### Beneficiary Party Support The implementation of featured app activity markers supports fine-grained, per-transaction activity attribution to multiple beneficiary parties. For scalability reasons, this CIP proposes to replace per-transaction attribution of beneficiary parties with the same construction that is used to manage beneficiaries of SV rewards: as part of minting, the app provider party can specify a list of beneficiaries and the amount of CC they should receive out of the minted allowance. The `MintingDelegations` introduced in [CIP-73](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0073/cip-0073.md#daml) will support minting app reward coupons from the adjusted workflow for the app provider. Automating the specification of beneficiaries and their amounts is however out of scope for the `MintingDelegations` from [CIP-73](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0073/cip-0073.md#daml). We expect such automation to be built as part of the apps themselves. ### Expose Traffic Cost and Activity Records in Scan App The CIP proposes to extend the Scan API to make both the data used by the app activity record computation and the activity records themselves available. Concretely it proposes to extend the `/v2/events` endpoint so that it includes: 1. Traffic summaries for confirmation requests. 2. App and asset activity records for successful confirmation requests. 3. The submitting featured app party if there is any. ### Free Protocol-Conformant Confirmation Responses The Canton Protocol is changed so that the confirmation responses expected to be sent by a confirming participant node become de facto free. We expect to implement this using the following two changes: 1. Change the sequencer to not charge traffic for confirmation responses. 2. Merge the sequencer and mediator processes into a single process. The latter is required so that the sequencer can then reject confirmation responses that are not expected by the mediator before ordering. #### Heuristic Implementation Merging the sequencer and mediator processes is a complex large-scale refactoring that will take time. We therefore propose to first introduce a heuristic implementation that does not charge traffic for confirmation responses and rate limits confirmation responses to prevent abuse. Concretely in Version 34 of the Canton Protocol, which is the version used on MainNet at the time of writing, the confirmation responses are the only messages that are addressed to exactly one mediator group. This CIP proposes to exploit this fact to expedite the change to make confirmation responses free as follows: 1. Add a boolean dynamic synchronizer flag to the topology state. 2. Change the traffic accounting in sequencers such that no traffic is deducted for messages that have only a single mediator group as their recipients. 3. Have the SV apps set the flag once all of the SV nodes have upgraded their sequencer to support the flag. 4. Rate limit confirmation responses per validator node to avoid abuse. ### Roll-Out Plan This CIP proposes to roll-out the changes in multiple increments according to the following plan: | Target for MainNet deployment | Increment | | --- | --- | | August 2026 | Make confirmation responses free using the heuristic implementation | | December 2026 | Enable dry-run mode, configure metadata for all FA rights, and make traffic-based app activity records observable on Scan | | February 2027 | **Contingent on Tokenomics committee approval**: switch to traffic-based app reward computation | Enabling the dry-run mode on MainNet allows to get a preview on the app rewards computed under the new traffic-based reward mechanism. Based on this preview the Tokenomics committee will make a final go-live decision. ## Motivation The Global Synchronizer for Canton Network incentivizes app builders via an app reward denominated in Canton Coin. This reward was initially granted only to applications that transferred Canton Coin as part of their activity, but this was found to be too limiting: the Super Validators wanted to also incentivize applications that moved assets other than Canton Coin. As a result the Super Validators introduced featured application activity markers (“featured app markers”) in [CIP-47](https://github.com/global-synchronizer-foundation/cips/blob/main/cip-0047/cip-0047.md), which allowed a featured application to receive rewards for any economically meaningful activity. This approach also had shortcomings, however: the reward weight generated was not directly correlated to the amount of traffic burned. At the time of writing, roughly 150% of weight is claimed via markers compared to actual traffic burned. The work to assess and govern these markers has become complex and time-consuming, and produces pressure to centralize governance of application behavior. App markers are also computationally expensive for the Super Validators, decreasing the amount of throughput that can be devoted to economically useful activity. We need a better method for incentivizing app builders that incentivizes economically useful behavior, and improves governance decentralization while decreasing the requirement for human oversight, so that applications are economically motivated to add the highest possible value in each transaction. ## Rationale ### Motivation for App Activity and Reward Computations The computations proposed by this CIP implement the [Featured App Guidance v2.0b](https://docs.google.com/document/d/1pLDUen2wpEcfS2pdPtF8u8DNWVHtgb6GIDpgAeiuWGE/edit?tab=t.0#heading=h.22k6ymijl3pg) [approved by the Tokenomics commitee on 2026-10-22](https://lists.sync.global/g/tokenomics-announce/message/397). See the guidance for the rationale. ### Alternatives Considered #### Distribute app activity weights among the app informees This CIP proposes to distribute app activity weights only among the confirming app providers of a view instead of sharing them among all featured app providers that are informees of the view. Practically, the main difference is that only the signatories of a contract creation node and the input contract signatories and actors of an exercise node receive app rewards. Parties that are only contract observers or choice observers do not receive app rewards. The rationale for this is two-fold: 1. The confirming app providers are the ones that actively participate in the validation and thus increase the integrity of the network. 2. Not considering contract observers and choice observers allows more flexibility for apps to optimize their Daml workflows without impacting their app rewards. For example, the ability to add extra choice observers is important to [avoid creating one view per item in a batch of actions](https://docs.canton.network/appdev/deep-dives/performance-optimization#reduce-the-number-of-views), and thus reduce traffic cost. #### Ignore Envelope Structure for Activity Recording Instead of attributing the traffic spend of each envelope to the app confirmers of the envelope, one could consider attributing the traffic spend of the whole confirmation request in equal shares across all confirmers of the whole transaction. We propose not to do so, as that would not work well with composed transactions. The goal is that app providers get the same rewards for the composed transaction as they would get if the user acted on each of the apps individually. This ensures that the users’ “vote of app value” is independent of how their use of the app came about. #### Rewards for Unfeatured Apps The [Canton Coin whitepaper](https://www.digitalasset.com/hubfs/Canton%20Network%20Files/Documents%20\(whitepapers%2c%20etc...\)/Canton%20Coin_%20A%20Canton-Network-native%20payment%20application.pdf?__hstc=169870847.16854726061d8b28be85af48c17588c4.1750870506327.1750870506327.1750870506327.1&__hssc=169870847.1.1750870506328&__hsfp=1243925796&_gl=1*1fkzmuj*_gcl_au*MTkyOTE1NjAyNC4xNzUwODcwNTA2*_ga*NDU1NzM2NzgyLjE3NTA4NzA1MDY.*_ga_GVK9ZHZSMR*czE3NTA4NzA1MDUkbzEkZzAkdDE3NTA4NzA1MDUkajYwJGwwJGgw) supports both unfeatured and featured applications. The difference between them being the amount of rewards they are allowed to earn per dollar of fees spent by their users. The mechanisms introduced by this CIP are compatible with supporting both featured and unfeatured applications *provided* there is a scalable means to identify app provider parties ahead of activity record creation. Thus what is missing to support unfeatured applications is the design and implementation for identifying unfeatured apps. A strawman candidate is to compute the set of unfeatured app providers daily, and require that an unfeatured app provider was involved in sub-transactions costing more than a threshold amount of traffic on the previous day. Designing and implementing a scalable implementation of this idea would however significantly delay the delivery of this CIP, which is why it is not included. However adding support for rewarding unfeatured apps in a future CIP is very much in line with the spirit of this CIP. #### Per-Transaction Beneficiaries Defined by App Provider One additional proposal for app rewards is to allow an app provider to specify per-transaction weights for how to split the app’s activity record among a set of beneficiaries, who would then ultimately be able to mint Canton Coin rewards for this activity. The current proposal does not address this feature. The main motivation for not supporting per-transaction beneficiaries is to avoid scalability issues. In particular the following two issues seem important to avoid: 1. **Avoid tracking minting allowances for millions of parties at the DSO level:** unnetted, direct reward sharing with app users would encourage apps to share rewards in fine-grained increments, which in turn creates unwanted scalability pressure on the SV app’s implementation of the minting allowance accounting that must run for every round. 2. **Avoid involving the DSO party in most transactions:** transactions involving the DSO party are validated and confirmed by all SV validator nodes. They thus carry a high cost, and should be used sparingly. Supporting per-transaction beneficiaries would require communicating information about different sub-transactions to the SV nodes at scale at the Daml-level. Thus creating a scalability issue by making the DSO party a confirmer of almost every transaction on the Global Synchronizer. App providers are however free to perform their own accounting to determine how to split the rewards they receive among their beneficiaries. They can retrieve the [necessary data from Scan](#expose-traffic-cost-and-activity-records-in-scan-app) and they can communicate the [minting allowances of their beneficiaries to the DSO on-ledger](#beneficiary-party-support). They may either perform the accounting themselves or use a third-party data provider that performs the accounting on their behalf. #### Weighting Beneficiaries Via Daml Model A given transaction might compose activity from multiple applications. In some cases, a given app provider could argue that their application provides a larger fraction of the overall value generated by that transaction, and therefore their application should receive a larger share of the total rewards generated by that transaction, even if the provider party for that application is not included in a comparable portion of the sub-transactions within the overall transaction. In this conceptual approach, the Daml model defining the app’s behavior would also dictate the weighting to be assigned to that app’s participation in any composed transaction involving that Daml model. However, due to Canton’s privacy, there is no way for the sequencer and mediator to know which Daml models are used in which (sub-)transactions, so value splits defined in a Daml model cannot be evaluated by the DSO. It *may* be possible to control the split of rewards *within a given sub-transaction*, but that would introduce significant complexity to the scope of this proposal. As a result, this CIP does not address weighting beneficiaries via Daml models, due to the significant increase in complexity this would introduce. We leave it to the community to consider this as a future enhancement. #### Load-Smoothing Round Attribution The certainty about the app rewards gained on traffic spent changes over the duration of a round. It is lowest at its start and highest towards the end of the round. Thus one can expect that rounds with low traffic will see a peak of transactions from elastic demand towards the end of the round. The exact height of these peaks is unclear. If these load peaks become a problem, then a future CIP can propose adopting an idea like the following deterministically randomized round assignment to smoothen them: 1. Round start and end times are assigned such that at any point in time exactly two rounds overlap; and thus there are two candidate rounds for every activity record. 2. Every activity record is assigned to one of the candidate rounds in a deterministically randomized fashion using the hash of their record time. 3. The probability of the assignment to the later round is managed such that it linearly increases from zero at the round start time to one at the round end time of the prior round. Thus the later an activity is recorded in a round the more likely it is for that activity to be attributed to the next round, for which the expected reward is more uncertain. Thereby lowering the fluctuation of reward certainty over the duration of a round, which in turn lowers the peaks from elastic demand. ### Technical Background See the [Canton Network whitepaper](https://www.canton.network/hubfs/Canton/Canton%20Network%20-%20White%20Paper.pdf) and the [Canton Blockchain Protocol whitepaper](https://www.canton.network/hubfs/Canton/canton-whitepaper.pdf) for more information about confirmation requests, views, and responses. Canton protocol messages are distributed via the sequencer as a batch of envelopes ([proto](https://github.com/digital-asset/canton/blob/be5399bd5509cd24fdbb1e7d2ed095c532c6dff1/community/base/src/main/protobuf/com/digitalasset/canton/protocol/v30/sequencing.proto#L88-L92)). An envelope consists of a list of recipients, a list of signatures, and a payload ([proto](https://github.com/digital-asset/canton/blob/be5399bd5509cd24fdbb1e7d2ed095c532c6dff1/community/base/src/main/protobuf/com/digitalasset/canton/protocol/v30/sequencing.proto#L76-L86)). The envelope payload used to distribute views is called an `EncryptedViewMessage` ([proto](https://github.com/digital-asset/canton/blob/be5399bd5509cd24fdbb1e7d2ed095c532c6dff1/community/base/src/main/protobuf/com/digitalasset/canton/protocol/v30/participant_transaction.proto#L141-L154)). The view itself is only stored in encrypted form, but the view hash is available in plaintext for the sequencer. ### Example: Views and Envelopes for DvP settlement We use the DvP example from the [Canton ledger model documentation](https://docs.canton.network/overview/reference/ledger-model-detailed). See that documentation for diagram notation, context on the example, and formal definitions of concepts like action, transaction, node, informees, and witnesses. ![Example of a DvP settlement transaction](settlement_tx_example.png) The diagram shows the confirmers of an action in green. As explained in the [Canton performance optimization documentation](https://docs.canton.network/appdev/deep-dives/performance-optimization#reduce-the-number-of-views), Canton 3.4 creates a view for every action node in the transaction tree if the validator nodes that host their informees are not a subset of their parent view’s informees' validators. Assuming that all parties in the example are hosted on their own validator nodes, the example DvP transaction results in five views, which are each encoded in their own envelope. For efficiency, envelopes only encode the root node of the view, as the subtree below the root node can be recomputed using its associated Daml code. Below we show calculation examples to illustrate the [activity record computation](#app-and-asset-activity-record-computation). They all assume the following envelope sizes for the envelopes containing the node’s views: * Node 1: 12 kB * Node 2: 4 kB * Node 3: 2 kB * Node 4: 5 kB * Node 5: 2 kB * `totalBurn`: 25 kB Note that we set `totalBurn` to the sum of all envelopes for the sake of this example. In practice, a confirmation request always contains two envelopes without associated views (one for the mediator view tree, and one or the root view hashes). #### Calculation Examples for User-Submitted Transactions Recall that for user-submitted transactions it only matters which parties have a `FeaturedAppRight` and are thus considered featured apps. Neither their app category, app composition weight, nor their operations parties influence the split of activity records. ##### App Activity Calculation Example 1.1: Bank1 is the only featured app provider Note that Bank1 is a confirmer of Node 2 and 3\. Thus * `totalAppBurn` \= 6 kB * Bank1's `appActivity` contribution from Node 2 is: `(envelopeBurn[Node 2] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 2]) = (4 * 25) / (6 * 1) = 16.666 kB` * Bank1's `appActivity` contribution from Node 3 is: `(envelopeBurn[Node 3] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 3]) = (2 * 25) / (6 * 1) = 8.333 kB` Thus Bank1 total activity record for this confirmation request is `24.999 kB`, which is equal to total confirmation request traffic of 25 kB when ignoring rounding errors. No other party gets credit for the traffic spent on this confirmation request. ##### App Activity Calculation Example 1.2: Bank1 and Bank2 are featured app providers Note that Bank1 is a confirmer of Node 2 and 3; and Bank2 is a confirmer of Node 4 and 5\. Thus * `totalAppBurn` \= 13 kB * Bank1's `appActivity` contribution from Node 2 is: `(envelopeBurn[Node 2] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 2]) = (4 * 25) / (13 * 1) = 7.692 kB` * Bank1's `appActivity` contribution from Node 3 is: `(envelopeBurn[Node 3] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 3]) = (2 * 25) / (13 * 1) = 3.846 kB` * Bank2's `appActivity` contribution from Node 4 is: `(envelopeBurn[Node 4] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 4]) = (5 * 25) / (13 * 1) = 9.615 kB` * Bank2's `appActivity` contribution from Node 5 is: `(envelopeBurn[Node 5] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 5]) = (2 * 25) / (13 * 1) = 3.846 kB` Thus Bank1’s total activity record for this confirmation request is `11.538 kB` and Bank2’s total is `13.461 kB`. They sum up to 24.999 kB, and their ratio is `13.461 / 11.538 = 1.166`, which matches the ratio of the size of their envelopes `7 / 6`. Note that both Bank1 and Bank2 still got full attribution for the envelopes involving only themselves. The difference to the first example is that Bank1 no longer gets any extra credit for the traffic spent by their users on the envelopes for nodes 1, 4, and 5\. ##### App Activity Calculation Example 1.3: Alice, Bank1, and Bank2 are featured app providers Intuition: Alice is a featured app because they act as an automated market maker. Note that Bank1 is a confirmer of Node 2 and 3; Bank2 is a confirmer of Node 4 and 5; and Alice is a confirmer of Node 1 and 2\. Thus * `totalAppBurn` \= 25 kB * Alice's `appActivity` contribution from Node 1 is: `(envelopeBurn[Node 1] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 1]) = (12 * 25) / (25 * 1) = 12 kB` * Bank1 and Alice each receive an `appActivity` contribution from Node 2 of: `(envelopeBurn[Node 2] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 2]) = (4 * 25) / (25 * 2) = 2 kB` That is they share the credit for Node 2 in equal shares. * Bank1's `appActivity` contribution from Node 3 is: `(envelopeBurn[Node 3] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 3]) = (2 * 25) / (25 * 1) = 2 kB` * Bank2's `appActivity` contribution from Node 4 is: `(envelopeBurn[Node 4] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 4]) = (5 * 25) / (25 * 1) = 5 kB` * Bank2's `appActivity` contribution from Node 5 is: `(envelopeBurn[Node 5] * totalBurn) / (totalAppBurn * numAppConfirmers[Node 5]) = (2 * 25) / (25 * 1) = 2 kB` Thus the totals are as follows: * Alice: 14 kB * Bank1: 4 kB * Bank2: 7 kB #### Calculation Examples for Featured-App Submitted Transactions Recall that for transactions that are submitted by a featured app, the `totalBurn` is credited as app activity to the submitting featured app. For example, if `Bob` is a featured app, then `appActivity[Bob] = totalBurn`. If `Bob` is qualified as an asset-moving featured app, then featured asset providers get credited with featured asset activity. We illustrate the asset activity calculation in the following examples. Unless stated otherwise, these examples assume that - the `RewardConfig` uses the default values - `Bob` is the submitting featured asset-moving app - all featured app composition weights are `1.0`, - there is no DSO-confirmed envelope in the transaction ##### Asset Activity Calculation Example 2.1: Bank1 is the only featured asset provider Bank1 is an asset confirmer of Nodes 2 and 3. Thus: * `assetEnvelopeBurn[Bank1]` = `4 + 2` = 6 kB * `totalAssetBurn` = 6 kB * `floor(totalBurn * extraFeaturedAssetsActivityWeight)` = `floor(25 * 0.2)` = 5 kB * `assetActivityUncapped[Bank1]` = `(5 * 6) / 6` = 5 kB * `assetActivityCap` = `floor(25 * 0.1)` = 2.5 kB * `assetActivityCapped[Bank1]` = `min(5, 2.5)` = 2.5 kB The capped excess is 2.5 kB, but there are no other asset confirmers with uncapped activity to receive it. Bank1 therefore receives 2.5 kB of featured asset activity after applying the composition weights. ##### Asset Activity Calculation Example 2.2: Bank1 and Bank2 are featured asset providers Bank1 is an asset confirmer of Nodes 2 and 3, and Bank2 is an asset confirmer of Nodes 4 and 5. Thus: * `assetEnvelopeBurn[Bank1]` = `4 + 2` = 6 kB * `assetEnvelopeBurn[Bank2]` = `5 + 2` = 7 kB * `totalAssetBurn` = 13 kB * `floor(totalBurn * extraFeaturedAssetsActivityWeight)` = 5 kB * `assetActivityUncapped[Bank1]` = `(5 * 6) / 13` = 2.308 kB * `assetActivityUncapped[Bank2]` = `(5 * 7) / 13` = 2.692 kB * `assetActivityCap` = 2.5 kB Bank2 is capped at 2.5 kB, leaving 0.192 kB of `totalCappedActivity`. Bank1 is the only asset confirmer with uncapped activity, so it receives all of that excess: * `totalUncappedActivity` = 2.308 kB * `cappedActivityShare[Bank1]` = `(0.192 * 2.308) / 2.308` = 0.192 kB * Updated `assetActivityCapped[Bank1]` = `min(2.308 + 0.192, 2.5)` = 2.5 kB After applying the composition weights, Bank1 and Bank2 each receive 2.5 kB of featured asset activity. This shows how the capped redistribution ensures that for a DvP transaction both sides receive 10% of the total burn as featured asset activity. ##### Asset Activity Calculation Example 2.3: Bank1, Bank2, and Bank3 in a batch settlement Consider a batch settlement of two `SimpleDvP` settlements between the same counterparties, A and B, involving different assets. The first settlement uses the running example's Nodes 1-5. The second settlement adds Nodes 6-10 with the same sizes: Node 6 (12 kB), Node 7 (4 kB), Node 8 (2 kB), Node 9 (5 kB), and Node 10 (2 kB). Bank1 confirms Nodes 2 and 3 in the first settlement and Nodes 7 and 8 in the second; Bank2 confirms Nodes 4 and 5 in the first; Bank3 replaces Bank2 in the second settlement and confirms Nodes 9 and 10. Thus `totalBurn` is `25 + 25` = 50 kB, and: * `assetEnvelopeBurn[Bank1]` = `(4 + 2) + (4 + 2)` = 12 kB * `assetEnvelopeBurn[Bank2]` = `5 + 2` = 7 kB * `assetEnvelopeBurn[Bank3]` = `5 + 2` = 7 kB * `totalAssetBurn` = `12 + 7 + 7` = 26 kB * `floor(totalBurn * extraFeaturedAssetsActivityWeight)` = `floor(50 * 0.2)` = 10 kB * `assetActivityUncapped[Bank1]` = `(10 * 12) / 26` ~= 4.615 kB * `assetActivityUncapped[Bank2]` = `(10 * 7) / 26` ~= 2.692 kB * `assetActivityUncapped[Bank3]` = `(10 * 7) / 26` ~= 2.692 kB * `assetActivityCap` = `floor(50 * 0.1)` = 5 kB No asset exceeds the cap, so no activity is redistributed. With all app composition weights set to `1.0`, the featured asset activity records are approximately: * Bank1: 4.615 kB (about 46%) * Bank2: 2.692 kB (about 27%) * Bank3: 2.692 kB (about 27%) Note that the scaling of asset activity by envelope sizes is what ensures that Bank1, who is part of two DvP settlements, receives about twice the activity compared to Bank2 and Bank3, reflecting its participation in two DvP settlements. The split is close to the expected 50% / 25% / 25%, but not exactly due to the varying envelope sizes used in this example. A perfect split seems difficult to achieve with the information available at the mediator and sequencer level alone. ##### Asset Activity Calculation Example 2.4: asset activity from Canton Coin integration Assume that we modify the above examples and replace Bank1 with the DSO party. The nodes confirmed by Bank1 thus correspond to changes in Canton Coin ownership, which are confirmed by the DSO party. Given the default `dsoAppActivityWeight` of `0.1`, 10% of the asset activity attributed to the DSO party is assigned to the submitting app, `Bob`. The asset activity records for the other featured asset providers do not change. The resulting asset activities are therefore: * modified Example 2.1: * Bob asset activity: 0.25 kB * modified Example 2.2: * Bob asset activity: 0.25 kB * Bank2 asset activity: 2.5 kB * modified Example 2.3: * Bob asset activity: 0.461 kB * Bank2 asset activity: 2.692 kB * Bank3 asset activity: 2.692 kB #### Observing and Optimizing View Decompositions Note that the actual view decomposition of an app very much depends on their Daml models, and is best observed in production. For example the `SimpleAsset` contract used in this example does not make the `owner` a signatory. If the `owner` were a signatory, then the new owner would be a confirmer on the `Transfer` choice, and thus a corresponding DvP settlement transaction would only contain three views: one for the settlement choice, and one for each transfer. For this reason, this CIP proposes an [Incremental Roll-Out](#roll-out-plan), whose first increment makes the view decomposition and traffic costs observable on Scan. Using a [LocalNet deployment](https://docs.dev.sync.global/app_dev/testing/localnet.html) of this increment, app providers may experiment with their Daml models and observe the view decomposition and envelope sizes. They can also use Daml Studio to observe the full transaction trees from their Daml models, which can be used with enough experience to predict the view decomposition. ### Example: Tiered App Reward Distribution The following example illustrates how asset rewards are only granted when there is sufficient minting allowance remaining after app rewards have been allocated. Assume a minting allowance of 112,000 CC for app rewards per round, a traffic price of $60/MB, and a coin price of $0.10/CC. The traffic price is therefore 600 CC/MB. Consider identical DvP transactions submitted by featured asset movers, using the setup from [Example 2.2](#asset-activity-calculation-example-22-bank1-and-bank2-are-featured-asset-providers). The round contains `N` DvPs in total. Each DvP contributes: * Asset mover app activity: 25 kB to its submitting asset mover * Asset activity: 2.5 kB (for Bank1) * Asset activity: 2.5 kB (for Bank2) Translated to CC burn (using the traffic price of 600 CC/MB): * Asset mover app activity: 25 kB * 600 CC/MB = 15 CC * Asset activity: 2.5 kB * 600 CC/MB = 1.5 CC (for each bank) The resulting minting allowances are: * Asset mover minting allowance: `15 CC * 0.79 = 11.85 CC` * Asset minting allowance (for each bank): `1.5 CC * 0.79 = 1.185 CC` The following table compares round-total minting allowances and per-DvP rewards for round totals of 1k, 8k, and 20k DvPs. The asset mover column shows the reward for the featured app that submitted each DvP; the banks' columns show each bank's reward per DvP in the round. | Total DvPs | Total app burn | App minting allowance | Total asset burn | Asset minting allowance | Reward per DvP for asset movers | Bank1 reward per DvP | Bank2 reward per DvP | Effect on asset rewards | | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | --- | | 1,000 | 15,000 CC | 11,850 CC | 3,000 CC | 2,370 CC | 11.85 CC | 1.185 CC | 1.185 CC | Full app and asset rewards | | 8,000 | 120,000 CC | 94,800 CC | 24,000 CC | 17,200 CC | 11.85 CC | 1.075 CC | 1.075 CC | Full app rewards, asset rewards are scaled down | | 20,000 | 300,000 CC | 112,000 CC | 60,000 CC | 0 CC | 5.60 CC | 0 CC | 0 CC | No asset rewards remain and app rewards are scaled down | Note that the app minting allowance is equal to `min(112,000 CC, 0.79 * total app burn)`, which is why it is `94,800 CC` at 8,000 DvPs and it reaches `112,000 CC` at 20,000 DvPs. While the app tier is not capped, each asset mover receives 79% of the burn attributed to its DvPs. Featured asset rewards are likewise capped at 79% of asset burn, subject to the allowance remaining after app rewards. Once the total featured app reward reaches the round allowance, the capped app reward is shared among featured asset movers in proportion to their app burn. Since all DvPs in this example are identical, this gives the same app reward per DvP regardless of how many asset movers submit them. The asset rewards use only the allowance left after the app tier. ## Reference Implementation Available as part of Splice on its [`main` branch](https://github.com/canton-network/splice.git). ## Copyright This CIP is licensed under CC0-1.0: [Creative Commons CC0 1.0 Universal](https://creativecommons.org/publicdomain/zero/1.0/) ## Changelog - 2026-10-23: Align CIP with [Featured App Guidance 2.0b](https://docs.google.com/document/d/1pLDUen2wpEcfS2pdPtF8u8DNWVHtgb6GIDpgAeiuWGE/edit?tab=t.0#heading=h.22k6ymijl3pg), which was [approved by the Tokenomics committee on 22nd of Oct, 2026](https://lists.sync.global/g/tokenomics-announce/message/397). - introduce featured app metadata - adjust activity and reward computations - update roll-out plan to take into account work that already happened - add examples for featured app and asset rewards - 2026-10-22: Add [activity recording extension hook](#app-activity-recording-extension-hook) to prepare for the [dedicated synchronizers](https://lists.sync.global/g/cip-discuss/topic/cip_0129_universal_burn_and/121668399) with app rewards - 2026-10-22: Simplify architecture and implementation approach for free-confirmation responses based on new learnings - 2026-06-30: Propose amendment for featured app weights. - 2026-02-12: CIP Approved. - 2026-01-29: draft published - 2026-01-10: change to share app rewards among confirmers instead of informees